攻防世界 web高手进阶区 9分题 Triangle

前言

继续ctf的旅程
开始攻防世界web高手进阶区的9分题
本文是Triangle的writeup

解题过程

进入界面

在这里插入图片描述
惯例源码+御剑
源码里有发现3个js文件源码

在这里插入图片描述

1、源码分析

index界面
就是简单的login

在这里插入图片描述

secret.js
index里用到的三个函数都在这个js里定义了

function test_pw(e, _) {
    
    
    var t = stoh(atob(getBase64Image("eye")))
      , r = 4096
      , m = 8192
      , R = 12288
      , a = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
    a.reg_write_i32(uc.ARM_REG_R9, m),
    a.reg_write_i32(uc.ARM_REG_R10, R),
    a.reg_write_i32(uc.ARM_REG_R8, _.length),
    a.mem_map(r, 4096, uc.PROT_ALL);
    for (var o = 0; o < o1.length; o++)
        a.mem_write(r + o, [t[o1[o]]]);
    a.mem_map(m, 4096, uc.PROT_ALL),
    a.mem_write(m, stoh(_)),
    a.mem_map(R, 4096, uc.PROT_ALL),
    a.mem_write(R, stoh(e));
    var u = r
      , c = r + o1.length;
    return a.emu_start(u, c, 0, 0),
    a.reg_read_i32(uc.ARM_REG_R5)
}
function enc_pw(e) {
    
    
    var _ = stoh(atob(getBase64Image("frei")))
      , t = 4096
      , r = 8192
      , m = 12288
      , R = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
    R.reg_write_i32(uc.ARM_REG_R8, r),
    R.reg_write_i32(uc.ARM_REG_R9, m),
    R.reg_write_i32(uc.ARM_REG_R10, e.length),
    R.mem_map(t, 4096, uc.PROT_ALL);
    for (var a = 0; a < o2.length; a++)
        R.mem_write(t + a, [_[o2[a]]]);
    R.mem_map(r, 4096, uc.PROT_ALL),
    R.mem_write(r, stoh(e)),
    R.mem_map(m, 4096, uc.PROT_ALL);
    var o = t
      , u = t + o2.length;
    return R.emu_start(o, u, 0, 0),
    htos(R.mem_read(m, e.length))
}
function get_pw() {
    
    
    for (var e = stoh(atob(getBase64Image("templar"))), _ = "", t = 0; t < o3.length; t++)
        _ += String.fromCharCode(e[o3[t]]);
    return _
}

看了看
就很卧槽
都用unicorn框架仿真ARM
这得逆向了啊

util.js
定义了get_pw()函数里用到的三个函数

function stoh(t){
    
    
	return t.split("").map(function(t){
    
    
		return t.charCodeAt(0)
	})
}
function htos(t){
    
    
	return String.fromCharCode.apply(String,t)
}
function getBase64Image(t){
    
    
	var e=document.getElementById(t),a=document.createElement("canvas");
	a.width=e.width,a.height=e.height;
	var n=a.getContext("2d");
	n.drawImage(e,0,0);
	var r=a.toDataURL("image/png");
	return r.replace(/^data:image\/(png|jpeg);base64,/,"")
}

unicorn.js非常大
是JavaScript框架的源码
在secret.js中用于仿真ARM

思路

  • 根据index,get_pw()应该输出一个固定值
  • enc_pwtest_pw函数仿真ARM,需要逆向了解它在干嘛
  • 然后想办法满足index里的login

2、输出get_pw()

尝试直接在console输出get_pw()
在这里插入图片描述
得到XYzaSAAX_PBssisodjsal_sSUVWZYYYb
这样util就不用管了

3、逆向了解 enc_pwtest_pw函数

要逆向enc_pwtest_pw函数
先想办法得到16进制
再转换为ARM汇编
工具:Online HEX to ARM Converter

逆向enc_pw
仿照enc_pw构造getARM1()函数
又其输出是10进制的数组
构造转换为16进制的函数toHexString()

function getARM1(){
    
    
  var x = stoh(atob(getBase64Image("frei")));
  var output = new Array();
  for(var i = 0; i < o2.length ; i++){
    
    
    output[i] = x[o2[i]];
  }
  return output;
} 

//Looking at o2, we observe that our output will be in integers. 
//Lets try converting them to hex values.

function toHexString(byteArray) {
    
    
  return Array.from(byteArray, function(byte) {
    
    
    return ('0' + (byte & 0xFF).toString(16)).slice(-2);
  }).join('')
}

输出toHexString(getARM1())

在这里插入图片描述
得到

0800a0e10910a0e10a20a0e10030a0e30050a0e30040d0e5010055e30100001a036003e2064084e0064084e2015004e20040c1e5010080e2011081e2013083e2020053e1f2ffffba0000a0e30010a0e30020a0e30030a0e30040a0e30050a0e30060a0e30070a0e30090a0e300a0a0e3

转换为ARM

在这里插入图片描述

得到

mov r0, r8		//r8根据secret.js,赋值了r=8192,在这里赋值给r0
mov r1, sb		//sb是静态基址寄存器,与r9同义,根据secret.js,赋值了m=12288,这里赋值给r1
mov r2, sl		//sl是堆栈限制寄存器,与r10同义,根据secret.js,赋值了输入e的长度,这里赋值给r2
mov r3, #0		//r3初始化为0
mov r5, #0		//r5初始化为0
ldrb r4, [r0]	//将存储器地址为R0的字节数据读入寄存器R4,并将R4的高24位清零,这里在rom中是0x14
cmp r5, #1		//将R5的值与1相减,结果存在标志位中
bne #0x28		//根据标志位的结果,判断R5与1是否相等,若不相等则跳转到0x28处
and r6, r3, #3	//将R3和3相与结果传入R6,相当于截取R3二进制最后两位传入R6
add r4, r4, r6	//将R4 与R6相加的结果传入R4
add r4, r4, #6	//R4加6,这里在rom中是0x28
and r5, r4, #1	//将R4和1相与的结果传入R5,若R4为偶数则R5=0反之R5=1
strb r4, [r1]	//将R4的低8位传入以R1为基址的存储器地址中
add r0, r0, #1	//R0加一
add r1, r1, #1	//R1加一
add r3, r3, #1	//R3加一
cmp r3, r2		//将R3与R2相减,结果存在标志位中
blt #0x14		//根据标志位的结果判断R3是否小于R2若小于则跳转到0x14处,即若计数器小于输入密码长度则继续循环
mov r0, #0		//这里往下都是清零
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov sb, #0
mov sl, #0

类似的
逆向test_pw

function getARM2(){
    
    
  var x = stoh(atob(getBase64Image("eye")));
  var output = new Array();
  for(var i = 0; i < o1.length ; i++){
    
    
    output[i] = x[o1[i]];
  }
  return output;
} 

在这里插入图片描述

0900a0e10a10a0e10830a0e10040a0e30050a0e300c0a0e30020d0e50060d1e5056086e201c004e200005ce30000000a036046e2060052e10500001a010080e2011081e2014084e2030054e1f1ffffba0150a0e30000a0e30010a0e30020a0e30030a0e30040a0e30060a0e30070a0e30080a0e30090a0e300a0a0e300c0a0e3
mov r0, sb		//向r0赋值m = 8192
mov r1, sl		//向r1赋值R = 12288
mov r3, r8		//向r3赋值输入值的长度
mov r4, #0		//初始化r4
mov r5, #0		//初始化r5
mov ip, #0		//初始化IP
ldrb r2, [r0]	//将存储器地址为R0的字节数据读入寄存器R2,并将R2的高24位清零,此处在rom中是0x18
ldrb r6, [r1]	//将存储器地址为R1的字节数据读入寄存器R6,并将R6的高24位清零
add r6, r6, #5	//将R6加5的结果传入R6
and ip, r4, #1	//将R4与1相与的结果传入IP
cmp ip, #0		//判断IP与0是否相等
beq #0x34		//如果IP==0,即R4是偶数,将会跳转到0x34处
sub r6, r6, #3	//else,即如果IP!=0,即R4是奇数,将R6减3的结果传入R6
cmp r2, r6		//判断R2与R6是否相等,此处在rom中是0x34
bne #0x54		//如果R2与R6不相等则跳转到0x54
add r0, r0, #1	//R0加一
add r1, r1, #1	//R1加一
add r4, r4, #1	//R4加一
cmp r4, r3		//比较R4与R3的大小
blt #0x18		//如果R4小于R3则跳转到0x18
mov r5, #1		//设置r5为1
mov r0, #0		//这里往下都是清零,此处在rom中是0x54
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov sb, #0
mov sl, #0
mov ip, #0


没接触过ARM汇编
难顶
去现学下
标注在上面

整理一下
enc_pw函数的流程如下

在这里插入图片描述
用python表示就是

def enc_pw(e):
  res = ''
  f = 0
  for i, c in enumerate(e):
    c = ord(c)
    if f == 1:
      c += i & 3
    c += 6
    f = c & 1
    res += chr(c)
  return res

test_pw函数的流程如下:

在这里插入图片描述
用python表示就是

def test_pw(e, t):
  for i, (c, d) in enumerate(zip(e, t)):
    c, d = ord(c), ord(d)
    c += 5
    if i & 1:
      c -= 3
    if c != d:
      return 0
  return 1

4、满足login

搞定了enc_pwtest_pw函数的python表示
那再搞个满足index里login的输入
逆着搞一下就是了

import string
def enc_pw(e):
  res = ''
  f = 0
  for i, c in enumerate(e):
    c = ord(c)
    if f == 1:
      c += i & 3
    c += 6
    f = c & 1
    res += chr(c)
  return res
  
encrypted = 'XYzaSAAX_PBssisodjsal_sSUVWZYYYb' # get_pw()的输出结果
flag = ''
# 逆向test_pw和index的login
for i, c in enumerate(encrypted):
  c = ord(c)
  c -= 5
  if i & 1:
    c += 3
  for d in string.printable:
    if enc_pw(flag + d)[i] == chr(c):
      flag += d
      break
  print flag

在这里插入图片描述
得到flag

结语

这是逆向啊
还是ARM汇编
现学现卖

知识点

  • unicorn框架
  • ARM汇编

参考

几个wp里都是用php语言的脚本

猜你喜欢

转载自blog.csdn.net/weixin_44604541/article/details/109135115