Rating: 1.0

Chinese only, sorry...

檢視 java 程式,發現如果能進入 godMode(),就能在已知 name 的情況下,讀出 flag,name 可經由 get_targets('javaisnotfun') 取得
要進入 godMode(),需先進入 connectToAI(),connectToAI() 可經由主選單輸入 0 進入,程式不會顯示 0 這個選項,需經由檢視原始碼得知
進入 connectToAI() 後,服務會先給你一個 1~(2**2047-1) 之間的亂數 Y,使用者需輸入一個數 X,使得 Y = X**exponent mod publicKey (以 ** 表示次方),Y 是以 10進位表示,X則需以 16進位輸入,publicKey 長度為 2048 bits
exponet 與 publicKey 是程式中的常數,exponent = ”10001”,是以字串儲存,之後會被以 16 進位之 0x10001 帶入上面之計算
由於 Y, exponent 與 publicKey 均是非常大的數,在僅知 這 3 個數的情況下,很難求出 X

解題關鍵在於如果能讓 exponent 變成 1,那麼 Y = X**1 mod publicKey,由於 Y < publicKey,因此讓 X = Y 即可

exponent 是 字串 "10001",如果能讓其變成 "00001",那麼 0x00001 = 1

檢視 c 程式下面片段,其作用是將某記憶體的值(64 bits),與 左移 bb 個位元後之 1 進行 xor 運算,'1' 的 ASCII 是 0x31,'0' 的 ASCII 是 0x30,0x31 xor 1 = 0x30
因此如果能將 exponent 之 "10001" 位址帶入這個 function,讓其與 1 進行 xor,便能將 "10001" 改為 "00001"

void __fastcall Java_NotFun_kill(JNIEnv *env, jobject thisObj, jlong address, jint bb)
{
*(_QWORD *)address ^= 1 << bb;
}


kill 這個 function,必須先進入 killUnicornMenu() 選單才會呼叫,而 killUnicornMenu() 必須先進入 gameMenu() 並連贏 5 場後才會呼叫
在此先省略贏 5 場之方法,後續有空再補充,基本上要依據 service 給你的 5 個值,算出 3 個值分別輸入即可

假設已連贏 5 場進入 killUnicornMenu() 選單,會被要求提供 3 個輸入,"Give me the color of the unicorn you want to kill:" / "Where should I start?" / "How do you want to kill the unicorn?"

color 便是記憶體中的值,在此我們希望找到 "10001" 之位址,而其是以 unicode 存在記憶體,因此輸入 0x30003000300031,即 10進位的 13511005043687473
Where 會取得 java 物件位址,並將其與 0xFFFFFFFFFFF00000L 進行 & 運算,因我們希望找到 exponent 在記憶體中的值,這裡便輸入 exponent
How 會先 % 8 後,當成上面 kill 的 bb 參數,由於我們是要把 0x31 xor 1 得到 0x30,因此我們要讓 bb = 0,在此便輸入 16 (或其他適當之 8 的倍數),如果只輸入 8,若在找到 exponent 之前已有符合之 color 存在記憶體中,便無法正確蓋到 exponent 的值

當 exponent 已被成功蓋成 "00001" 之後,便呼叫 connectToAI(),將 service 要求的 10 進位值轉成 16進位輸入,便會進入 godMode(),接著輸入由 get_targets(''javaisnotfun'') 取得之 name,即得 flag

過程再順一遍
1. 連線至 service 後,輸入 1 進入 Play 模式
2. 連贏 5 場後,選擇 2 進入 killUnicornMenu()
3. 分別輸入 13511005043687473 , exponent , 16 以將 exponent 之值由 "10001" 改成 "00001"
4. 輸入 0 進入 connectToAI()
5. 將 service 要求的 10 進位值轉成 16進位輸入
6. 輸入 name,得到 flag

---
輸出範例,這裡假設 yLqLgrcDawbiE6Xz 是取得之 name
---

Welcome! Unicorns are not having fun here... I hope you will not have fun too.
1) Play versus AI
2) Read score
3) Exit
7 (自己加入的功能,模擬贏 5 場之後)
Your score is: 1000000
Choose price:
1) Write score
2) Kill a unicorn
2
Give me the color of the unicorn you want to kill:
13511005043687473
Where should I start?
exponent
How do you want to kill the unicorn?
16
AI found and killed the unicorn. You should feel bad.
AI found and killed the unicorn. You should feel bad.
1) Play versus AI
2) Read score
3) Exit
0
I want an unicorn of this color:
13289295893315638982600993210291798189534484343460069153351009371355454427132756607411575783503399179427468360609869098325401757130269782839764565772094172052219064882521938687304950375103460587790376979409585347776565890691478147533192637053612385839675857536425120034430783190318349963844490966513190985690596971926058406156336759173047955658332458539899658534791199113328028077367484151817264565438627536351612111203316399603436561697307119589583728219565857605261670355662738180020610793303035495237515333704719170139956638474771770969730601984039250265434492403305453963863698756601234256431869353047377951658744
6945811d0387b2a7b7a9188844267be0b29aebf3e0518352e8627239ce3d7ca294aa5d63860ba8bee0b43b007f6d13315c457ab6c74e81e2c590ac62a47651ee592a177f50b5f0ccbfcf331f9463ddc225329d2a81e825a61242ce30ee2b390c20927f0bd147f9e76dff903d0825535f8381bd2f2067a4304a96d05a6de7546f8ed1f135ccac0387c5240c0c56334065ece6d1cdff4001929f9a65a90fc0240d550a8b5443066ac255633482db95394eac2f0ef9e762a946ab64120e7a7487772c6543324c525a29f573bfb843d6bd757ab62e3e0ee7344d78c17283e6b52e2678bb7a36146b5c5be61705e9cb4ec170f008797922f0e3624984a796958512f8
I like you.
come se fosse?
yLqLgrcDawbiE6Xz
This is your comment:
FLGZOo1gAIGhzdWp
This is your score:
999601
1) Play versus AI
2) Read score
3) Exit

---

---
反編譯之 .java
---

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NotFun
{
static
{
System.loadLibrary("notfun");
}

private static int MAX_FILE_TIME = 1800000;
private static int MAX_FILE_NUMBER = 25000;

private String UnicornMaster = "UnicornMaster";
private String PoorUnicorn = "PoorUnicorn";
private boolean unicornKilled = false;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

private String publicKey = "c0f3c2e0d35facffd5b78ea4447fac106c0976696aca375849192e2b5422bec0d382b83c53c2fe6aefd13e093ae03817dcfb280e18e41258b949863633fec9082500e73987ecfd562e36754c45319b160febca7fdd9aebe175dfd19f9610fa77b20d10fcf4437464969634a82d3d7a698c0ae0d96bfdb531dca6ad31e032c6c202c8f512da5725f76bf0d8fca0b37c6ff7b54e7de6a684de64bac60fd8cbb34a00a234893880bdb4ed0d76145226f64cc88c0306543811c38ad3f75bfd9a07186ef08786eec35674dd7aa521b36f7f621482054caba235dbae154bcba58cafb95e3accca1fb49059191c274c6639a6abffea78db00be7192cf960150f57e9c1b";
private String exponent = "10001";

public int getInt() {
try {
return Integer.valueOf(reader.readLine()).intValue();
} catch (NumberFormatException localNumberFormatException) {
System.exit(1);
} catch (IOException localIOException) {
System.exit(1);
}
return 0;
}

public long getLong() {
try { return Long.valueOf(reader.readLine()).longValue();
} catch (NumberFormatException localNumberFormatException) {
System.exit(1);
} catch (IOException localIOException) {
System.exit(1);
}
return 0L;
}

public String getString() {
try { String str = reader.readLine();
if (str == null) {
System.exit(1);
}
return str;
} catch (IOException localIOException) {
System.exit(1);
}
return "";
}

public long getStartingPoint() { String str1 = getString();
try
{
return Long.valueOf(str1).longValue();
}
catch (NumberFormatException localNumberFormatException) {
Class localClass = getClass();
try {
Field localField = localClass.getDeclaredField(str1);
localField.setAccessible(true);
String str2 = (String)localField.get(this);
long l2 = getUnicornLocation(str2);
return l2 & 0xFFFFFFFFFFF00000L;
} catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException localNoSuchFieldException) {}
}
return 0L;
}

public void print(Object paramObject)
{
String str;
if (!(paramObject instanceof String)) {
str = String.valueOf(paramObject);
} else {
str = (String)paramObject;
}
System.out.println(str);
}

public static void main(String[] paramArrayOfString)
{
cleanup();
NotFun localNotFun = new NotFun();
localNotFun.run();
}

private static void cleanup() {
long l1 = new Date().getTime();
File localFile1 = new File(".");
File[] arrayOfFile1 = localFile1.listFiles();
if (arrayOfFile1.length <= MAX_FILE_NUMBER) {
return;
}

for (File localFile2 : arrayOfFile1) {
long l2 = localFile2.lastModified();
if (l2 != 0L)
{
if (localFile2.lastModified() < l1 - MAX_FILE_TIME)
{
localFile2.delete(); } }
}
}

private void run() {
print("Welcome! Unicorns are not having fun here... I hope you will not have fun too.");
for (;;) {
if (!mainMenu()) {
break;
}
}
print("Unicorns are still not having fun and I don't see any rainbow. Do you like dolphins?");
}

private boolean mainMenu() {
print("1) Play versus AI");
print("2) Read score");
print("3) Exit");
int i = getInt();
switch (i) {
case 1:
gameMenu();
break;
case 2:
readScoreMenu();
break;
case 3:
return false;
case 0:
connectToAI();
}
return true;
}

private void gameFail() {
print("AI won!");
System.exit(11);
}

private void game() { for (int i = 0; i < 5; i++) {
Random localRandom = new Random();

int j = localRandom.nextInt(100);
int k = localRandom.nextInt(100);
int m = localRandom.nextInt(256);
int n = localRandom.nextInt(10);
int i1 = localRandom.nextInt(10);
print("These are your unlucky numbers:");
print(Integer.valueOf(j));
print(Integer.valueOf(k));
print(Integer.valueOf(m));
m <<= 8;

switch (n) {
case 0:
m = m * 2 + 3;
break;
case 1:
m = m * 7 + 8;
break;
case 2:
m = m * 3 + 1;
break;
case 3:
m = m * 5 + 3;
break;
case 4:
m = m * 2 + 9;
break;
case 5:
m = m * 9 + 1;
break;
case 6:
m = m * 6 + 2;
break;
case 7:
m = m * 5 + 4;
break;
case 8:
m = m * 8 + 2;
break;
case 9:
m = m * 4 + 2;
}

switch (i1) {
case 0:
m = magic0(m);
break;
case 1:
m = magic1(m);
break;
case 2:
m = magic2(m);
break;
case 3:
m = magic3(m);
break;
case 4:
m = magic4(m);
break;
case 5:
m = magic5(m);
break;
case 6:
m = magic6(m);
break;
case 7:
m = magic7(m);
break;
case 8:
m = magic8(m);
break;
case 9:
m = magic9(m);
}

print(Integer.valueOf(n));
print(Integer.valueOf(i1));

if (getInt() != j + 2) {
gameFail();
}
if (getInt() != magic000(k)) {
gameFail();
}
if (getInt() != m) {
gameFail();
}
}
print("You won!");
}

private void gameMenu() {
long l1 = new Date().getTime();
game();
long l2 = new Date().getTime();
long l3 = 1000000L - (l2 - l1);
print("Your score is: " + String.valueOf(l3));

print("Choose price:");
print("1) Write score");
print("2) Kill a unicorn");
int i = getInt();
switch (i) {
case 1:
writeScore(l3);
break;
case 2:
killUnicornMenu();
break;
default:
print("Wrong choice");
System.exit(2);
}
}

private void printScore(String paramString) {
try {
File localFile = new File(paramString);
Scanner localScanner = new Scanner(localFile);
print("This is your comment:");
print(localScanner.nextLine());
print("This is your score:");
print(localScanner.nextLine());
localScanner.close();
} catch (FileNotFoundException localFileNotFoundException) {
print("I cannot find your file, I think you don't know the password!");
System.exit(4);
}
}

private void readScoreMenu() {
print("give me your name:");
String str1 = getString();
if (!checkValidStringFile(str1)) {
print("wrong name!");
System.exit(3);
}
print("give me your password:");
String str2 = getString();
if (!checkValidStringFile(str2)) {
print("wrong password!");
System.exit(3);
}
printScore(str1 + "_" + str2);
}

private void writeScore(long paramLong) {
print("give me your name:");
String str1 = getString();
if (!checkValidStringFile(str1)) {
print("wrong name!");
System.exit(3);
}
print("give me your password:");
String str2 = getString();
if (!checkValidStringFile(str2)) {
print("wrong password!");
System.exit(3);
}
print("give me your comment:");
String str3 = getString();
if (!checkValidString(str3)) {
print("wrong comment!");
System.exit(3);
}
try
{
PrintWriter localPrintWriter = new PrintWriter(str1 + "_" + str2);
localPrintWriter.println(str3);
localPrintWriter.print(String.valueOf(paramLong));
localPrintWriter.close();
} catch (FileNotFoundException localFileNotFoundException) {
System.exit(10);
}
}

private void connectToAI() {
SecureRandom localSecureRandom = new SecureRandom();
BigInteger localBigInteger1 = new BigInteger(2047, localSecureRandom);
print("I want an unicorn of this color:");
print(String.valueOf(localBigInteger1));
String str = getString();
BigInteger localBigInteger2 = new BigInteger(str, 16).modPow(new BigInteger(exponent, 16), new BigInteger(publicKey, 16));
if (localBigInteger1.equals(localBigInteger2)) {
print("I like you.");
godMode();
} else {
print("You are not smart enough for this AI: hack harder or use more CPUs!");
}
}

private void godMode() {
print("come se fosse?");
final String str = getString();
if (!checkValidStringFile(str)) {
print("wrong name!");
System.exit(3);
}

File localFile = new File(".");
File[] arrayOfFile = localFile.listFiles(new FilenameFilter() {
public boolean accept(File paramAnonymousFile, String paramAnonymousString) {
return paramAnonymousString.startsWith(str);
}
});
printScore(arrayOfFile[0].getName());
}

private void killUnicornMenu() {
print("Give me the color of the unicorn you want to kill:");
long l1 = getLong();
print("Where should I start?");
long l2 = getStartingPoint();
print("How do you want to kill the unicorn?");
int i = getInt();
if (unicornKilled) {
print("Come on! You cannot kill too many unicorns.");
System.exit(5);
}

int j = 0;
int k = 0;
for (;;) {
long l3 = getUnicornColor(l2 + j);
if (k == i / 8) {
break;
}
if (l3 == l1) {
kill(l2 + j, i % 8);
print("AI found and killed the unicorn. You should feel bad.");
unicornKilled = true;

k++;
}
j++;
if (j > 10000000) {
break;
}
}
}

private boolean checkValidStringFile(String paramString) {
Pattern localPattern = Pattern.compile("[0-9a-zA-Z]{3,20}");
Matcher localMatcher = localPattern.matcher(paramString);
if (localMatcher.matches()) {
return true;
}
return false;
}

private boolean checkValidString(String paramString)
{
Pattern localPattern = Pattern.compile("[0-9a-zA-Z !_,:]{1,100}");
Matcher localMatcher = localPattern.matcher(paramString);
if (localMatcher.matches()) {
return true;
}
return false;
}

private static native long getUnicornLocation(String paramString);
private static native long getUnicornColor(long paramLong);
private static native void kill(long paramLong, int paramInt);
private static native int magic000(int paramInt);
private static native int magic0(int paramInt);
private static native int magic1(int paramInt);
private static native int magic2(int paramInt);
private static native int magic3(int paramInt);
private static native int magic4(int paramInt);
private static native int magic5(int paramInt);
private static native int magic6(int paramInt);
private static native int magic7(int paramInt);
private static native int magic8(int paramInt);
private static native int magic9(int paramInt);
public NotFun() {}
}


---
反編譯之 libnotfun.c
---

/* This file has been generated by the Hex-Rays decompiler.
Copyright (c) 2007-2016 Hex-Rays <info@hex-rays.com>

Detected compiler: GNU C++
*/

#include <defs.h>

//-------------------------------------------------------------------------
// Function declarations

void *init_proc();
// __int64 __gmon_start__(void); weak
// __int64 __fastcall __cxa_finalize(_QWORD); weak
__int64 (**deregister_tm_clones())(void);
__int64 register_tm_clones();
__int64 (**_do_global_dtors_aux())(void);
__int64 frame_dummy();
__int64 __fastcall Java_NotFun_getUnicornColor(JNIEnv *env, jobject thisObj, jlong address);
void __fastcall Java_NotFun_kill(JNIEnv *env, jobject thisObj, jlong address, jint bb);
__int64 __fastcall Java_NotFun_getUnicornLocation(JNIEnv *env, jobject thisObj, jstring s);
__int64 __fastcall Java_NotFun_magic000(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic0(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic1(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic2(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic3(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic4(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic5(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic6(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic7(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic8(JNIEnv *env, jobject thisObj, jint n);
__int64 __fastcall Java_NotFun_magic9(JNIEnv *env, jobject thisObj, jint n);
void term_proc();
// __int64 ITM_deregisterTMCloneTable(void); weak
// __int64 __fastcall Jv_RegisterClasses(_QWORD); weak

//-------------------------------------------------------------------------
// Data declarations

__int64 _JCR_LIST__ = 0LL; // weak
void *_dso_handle = &_dso_handle; // weak
char edata; // weak
_UNKNOWN unk_201037; // weak
// extern _UNKNOWN _cxa_finalize; weak
// extern _UNKNOWN _gmon_start__; weak

//----- (0000000000000818) ----------------------------------------------------
void *init_proc()
{
void *result; // rax@1

result = &_gmon_start__;
if ( &_gmon_start__ )
result = (void *)__gmon_start__();
return result;
}
// 850: using guessed type __int64 __gmon_start__(void);

//----- (0000000000000870) ----------------------------------------------------
__int64 (**deregister_tm_clones())(void)
{
__int64 (**result)(void); // rax@1

result = (__int64 (**)(void))(&unk_201037 - (_UNKNOWN *)&edata);
if ( (unsigned __int64)(&unk_201037 - (_UNKNOWN *)&edata) > 0xE )
{
result = &ITM_deregisterTMCloneTable;
if ( &ITM_deregisterTMCloneTable )
result = (__int64 (**)(void))ITM_deregisterTMCloneTable();
}
return result;
}
// 201030: using guessed type char edata;
// 201048: using guessed type __int64 ITM_deregisterTMCloneTable(void);

//----- (00000000000008A0) ----------------------------------------------------
__int64 register_tm_clones()
{
return 0LL;
}

//----- (00000000000008E0) ----------------------------------------------------
__int64 (**_do_global_dtors_aux())(void)
{
__int64 (**result)(void); // rax@4

if ( !edata )
{
if ( &_cxa_finalize )
__cxa_finalize(_dso_handle);
result = deregister_tm_clones();
edata = 1;
}
return result;
}
// 860: using guessed type __int64 __fastcall __cxa_finalize(_QWORD);
// 201028: using guessed type void *_dso_handle;
// 201030: using guessed type char edata;

//----- (0000000000000920) ----------------------------------------------------
__int64 frame_dummy()
{
if ( !_JCR_LIST__ || !&Jv_RegisterClasses )
return register_tm_clones();
Jv_RegisterClasses(&_JCR_LIST__);
return register_tm_clones();
}
// 200E10: using guessed type __int64 _JCR_LIST__;
// 201058: using guessed type __int64 __fastcall Jv_RegisterClasses(_QWORD);

//----- (0000000000000955) ----------------------------------------------------
__int64 __fastcall Java_NotFun_getUnicornColor(JNIEnv *env, jobject thisObj, jlong address)
{
return *(_QWORD *)address;
}

//----- (000000000000096E) ----------------------------------------------------
void __fastcall Java_NotFun_kill(JNIEnv *env, jobject thisObj, jlong address, jint bb)
{
*(_QWORD *)address ^= 1 << bb;
}

//----- (00000000000009AD) ----------------------------------------------------
__int64 __fastcall Java_NotFun_getUnicornLocation(JNIEnv *env, jobject thisObj, jstring s)
{
return *(_QWORD *)s;
}

//----- (00000000000009C9) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic000(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)(3 * n + 1);
}

//----- (00000000000009E6) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic0(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)(2 * n + 4);
}

//----- (00000000000009FF) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic1(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)(16 * n + 3);
}

//----- (0000000000000A19) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic2(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)(8 * n + 2);
}

//----- (0000000000000A33) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic3(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)(4 * n + 3);
}

//----- (0000000000000A4D) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic4(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)(4 * n + 1);
}

//----- (0000000000000A67) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic5(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)((n >> 2) + 3);
}

//----- (0000000000000A81) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic6(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)((n >> 3) + 3);
}

//----- (0000000000000A9B) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic7(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)((n >> 1) + 3);
}

//----- (0000000000000AB4) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic8(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)((n >> 4) + 7);
}

//----- (0000000000000ACE) ----------------------------------------------------
__int64 __fastcall Java_NotFun_magic9(JNIEnv *env, jobject thisObj, jint n)
{
return (unsigned int)((n >> 1) + 1);
}

//----- (0000000000000AE8) ----------------------------------------------------
void term_proc()
{
;
}

// ALL OK, 20 function(s) have been successfully decompiled