stack_move
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | vuln: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter ./ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=71608a3832e991be0d289c6a86027c189b0c76ff, not stripped |
- 64位,dynamically,Partial RELRO,NX
1 | 0000: 0x20 0x00 0x00 0x00000004 A = arch |
- ban 了 execve
漏洞分析
简单栈溢出:
1 | ssize_t vuln() |
入侵思路
先利用栈溢出写一个 puts(puts_got)
用于泄露 libc_base,然后填一个 main_addr 写循环
接下来可以选择栈迁移,也可以直接构造 read(0,stack_addr,0x300)
(由于之前执行过 read,前两个参数不用修改)
最后写一个 ORW 即可
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
u_heap
1 | GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27. |
1 | pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=dc263a465ef3a751f65d9680e8cac0ed688783d3, stripped |
- 64位,dynamically,Full RELRO,Canary,NX
漏洞分析
堆溢出漏洞:
1 | if ( key ) |
入侵思路
本题目的限制有点多,首先可以利用堆溢出的两字节来覆盖相邻下一个 chunk 的 size,从而构造出 unsorted chunk,从而泄露 libc_base:
1 | edit(0,b"b"*0x68+p16(0x4e1)) |
有了 libc_base 就可以触发程序提供的 gift 功能了:
1 | writen("Input the password"); |
- 只申请不写入,这里只能用于触发 largebin attack
根据程序的功能,接下来肯定是要打 largebin attack 的,但在此之前有一个问题让我纠结了很久:到底要不要构造堆风水去泄露 heap_base:
- 利用堆风水实现的错位可以使我们打印出 heap_addr
- 如果我们想构造堆风水去泄露 heap_addr,错位后的堆就不能使用 edit 去修改 large chunk
- 如果我们不泄露 heap_addr,就没法在 largebin attack 后构造 house of cat 来打 IO
这似乎是一个两难的选择,我在打比赛时两种方式都试过,最后选择先不泄露 heap_addr
在 libc-2.27 版本下打 largebin attack 有两种方式,由于程序只提供了两次 free 的机会,因此我们只能选择将一个小于 large chunk 的 unsorted chunk 插入 largebin(另一种方法可以写两处地址,但是至少需要3个 free chunk)
1 | edit(0,b"b"*0x68+p16(0x441)) |
往 io_list_all 中写入可控 heap_addr 后,可以查看一下此时的堆风水:
1 | unsortedbin |
- 未进行 largebin attack 之前
1 | pwndbg> telescope 0x1def340 |
- 由于该 unsorted chunk 比 large chunk 小,因此该 chunk 在进入 largebin 后会插入 largebin 的尾部,也就是 large chunk->fd 的位置
1 | pwndbg> telescope 0x6020A0 |
- 而这个位置的数据我们是可以打印的,也就泄露的 heap_base
最后伪造 fake_IO_FILE 去打 house of cat,由于程序单个 chunk 没有太多空间,我们只能分段进行伪造,另外 2.27 版本的 house of cat 和高版本有些许不同,可以一边动调一边修改高版本 house of cat 的模板:
1 | fake_io_addr = heap_base + 0x340 |
- 动调时重点看 cmp 指令是否可以满足跳转指令的条件,若不满足则尝试修改 fake_IO_FILE 甚至是堆风水
当 house of cat 劫持程序执行流后,就可以调用 setcontext 完成栈迁移,最后打一个 ORW 即可
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
fakecalc
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=073f580c2010498ed638e9d9d4051016cb790fdb, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,全开
漏洞分析
栈溢出漏洞:
1 | __isoc99_scanf("%d", &size); |
有后门:
1 | int door() |
入侵思路
利用溢出可以泄露 canary,不过这里有一点需要注意:
1 | for ( i = 0; i < size; ++i ) |
在 scanf 写入失败时虽然可以不覆盖数据,但遗留在缓冲区中的数据并不会被销毁,这会导致之后所有的 scanf 失败(scanf 使用 stdin 缓冲区)
1 | pwndbg> p _IO_2_1_stdin_ |
- stdin 缓冲区默认为
_IO_2_1_stdin_+131
,如果不够用则会使用栈上 0x400 大小的空间,如果再不够则会调用 malloc 申请堆空间
分析 scanf 的源码可以找到解决办法:
1 | case L_('d'): /* Signed decimal integer. */ |
1 | number: |
- 当遇到
+ -
时,scanf 会选择跳过然后匹配下一个字符,利用这一点即可使 scanf 不覆盖数据
这样我们就可以一次泄露1字节,将 canary 泄露出来,而在泄露 pro_base 时需要注意 canary 对后续计算的影响
下面是泄露使用的脚本:
1 | def leak_target(offset,canary=0): |
最后覆盖返回地址为后门地址即可
本题目需要提权去执行一个没有 x 权限的程序,可以使用 ld 来进行操作:
1 | ➜ exp ls -al ./getflag |
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
YouChat
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.12) stable release version 2.31. |
1 | YouChat: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d476e7cd0be2b555624f5111dedddeb8640df8f7, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,全开
漏洞分析
格式化字符串漏洞:
1 | std::operator<<<std::char_traits<char>>(&std::cout, "[+] Successfully set a new status: "); |
栈溢出漏洞:
1 | std::operator<<<std::char_traits<char>>(&std::cout, "Message: "); |
入侵思路
需要先进行两个匹配进入核心功能:
1 | def login(name,passwd): |
利用格式化字符串漏洞可以泄露 libc_base:
1 | set("%31$p") |
利用栈溢出覆盖 canary 低位可以泄露出 canary:
1 | choose("0") |
覆盖 canary 后可以泄露其后的返回地址:
1 | payload = "a"*0x200+"b"*8+"c"*8 |
最后执行一个 execve 就可以了
完整 exp 如下:(比赛时不知道本地和远程有 729088 字节的偏移,浪费了很长时间)
1 | # -*- coding:utf-8 -*- |
OuchOS
1 | OuchOS1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter ./ld-linux-x86-64.so.2, BuildID[sha1]=17c82d26ae3109262c4fe4584f0c85bcc2132b9f, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,全开
漏洞分析
栈溢出漏洞:
1 | len = std::string::length(input); |
入侵思路
利用栈溢出覆盖 canary 低位可以泄露 canary 和其后的 pro_addr 返回地址
然后先构造一个 getline(cin,bss_addr)
写入 /bin/sh\x00
,然后构造 system 即可
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |