Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

HackTM 2020 - TripToTrick WriteUp

小论文改得蛋疼,看个Pwn缓解下心情。(家里上Github真的慢…怀念北邮的网速)

参考:

https://teamrocketist.github.io/2020/02/05/Pwn-HackTM-2020-Trip-To-Trick/

https://www.slideshare.net/AngelBoy1/play-with-file-structure-yet-another-binary-exploit-technique

一个经常出的类型的Pwn题,但是环境用了最新的libc-2.29,因此做个记录。

0x00 概述

  • 已知libc地址
  • 两次scanf任意地址写8字节数据
  • return前关闭stdinstdoutstderr
  • 有沙箱

0x01 思路

  • 第一次scanf_IO_2_1_stdin__IO_BUF_END设置为STDIN+0x2000
  • 第二次scanf就可以控制_IO_FILE的结构
  • 将STDOUT的vtable设置为_IO_helper_jumpsflags设置为0来绕过检查
  • 在libc-2.29中,vtable是可写的,因此通过控制_IO_helper_jumps->__finish来劫持控制流
  • 设置_IO_helper_jumps的__finish为setcontext+0x35
  • 构造ORW的ROP Chain

0x02 一些程序分析

沙箱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0e 0xc000003e if (A != ARCH_X86_64) goto 0016
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x0b 0xffffffff if (A != 0xffffffff) goto 0016
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 0x00000003 if (A == close) goto 0015
0009: 0x15 0x05 0x00 0x00000009 if (A == mmap) goto 0015
0010: 0x15 0x04 0x00 0x0000000a if (A == mprotect) goto 0015
0011: 0x15 0x03 0x00 0x0000000c if (A == brk) goto 0015
0012: 0x15 0x02 0x00 0x0000000f if (A == rt_sigreturn) goto 0015
0013: 0x15 0x01 0x00 0x0000003c if (A == exit) goto 0015
0014: 0x15 0x00 0x01 0x000000e7 if (A != exit_group) goto 0016
0015: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0016: 0x06 0x00 0x00 0x00000000 return KILL

vtable

在libc-2.29中,vtable区域又重新拥有可写的权限,因此出题人通过nohack函数进行了限制:

1
2
3
4
5
6
7
8
9
int nohack()
{
if ( ((_WORD)stdout + 2208) & 0xFFF )
{
puts("mprotect error");
exit(1);
}
return mprotect(&stdout[10]._IO_write_end, 0x700uLL, 1);
}

但是下列vtable仍然可写:

  • _IO_helper_jumps

  • _IO_cookie_jumps

  • _IO_proc_jumps

  • _IO_str_chk_jumps

  • _IO_wstrn_jumps

  • _IO_wfile_jumps_maybe_mmap

利用其中的函数指针可以控制RIP。

缓冲区

1
2
3
4
5
6
int main_init()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
return setvbuf(stderr, 0LL, 2, 0LL);
}

Pwn题的基本操作,将缓冲区置0,std将不会使用缓冲区。_IO_buf_base_IO_buf_end的差值只有1,会使得stdin只能存储一个字节在缓冲区,即回车或空字符(取决于输入形式)。

如果我们通过第一次scanf增加了stdio->_IO_buf_end的值,将能控制vtable。

除了修改stdout->vtable_IO_helper_jumps和修改_IO_helper_jumps中的函数指针外,中间经过的大片内存区域也需要修复而不是简单的进行零填充。