├── 2018 ├── codegate-pre │ └── welcome_to_droid │ │ ├── README.md │ │ └── droid.apk ├── inshack │ └── visual-hashing-hard │ │ ├── chall-1.png │ │ └── visual-hashing-hard.py ├── insomnihack-finals │ └── be_back │ │ ├── README.md │ │ └── beback.ipa ├── matesctf │ └── jailbreak │ │ ├── Jailbreak.ipa │ │ ├── Jailbreak___T09Jailbreak14ViewControllerC10btnCheckedyypF.pdf │ │ ├── README.md │ │ └── screenshot.png └── xiomara │ └── mario_in_maze │ └── mario_in_maze.py ├── 2020 └── googlectf │ ├── README.md │ └── reverse.apk ├── LICENSE └── README.md /2018/codegate-pre/welcome_to_droid/README.md: -------------------------------------------------------------------------------- 1 | ## Welcome to droid 2 | 3 | __Type:__ RE 4 | __Solves:__ 24 5 | __Description:__ 6 | ``` 7 | > o < 8 | ``` 9 | 10 | __Files:__ 11 | droid.apk 12 | 13 | ## Solution: 14 | Ok, so we got an android apk, let's first unpack it and see whats inside. 15 | 16 | ``` 17 | $ ls droid 18 | 19 | AndroidManifest.xml 20 | classes.dex 21 | res 22 | META-INF 23 | lib 24 | resources.arsc 25 | ``` 26 | 27 | The most interesting thing here is that `lib` folder which contains library `libnative-lib.so` for different architectures. Now we know that the app uses JNI to some precompiled library. Let's move on and decompile Java classes to see what it is doing. 28 | 29 | `$ d2j-dex2jar.sh droid/classes.dex` 30 | 31 | Now we can see the decompiled code. It has package 32 | ``` 33 | com.example.puing.a2018codegate 34 | -- MainActivity.class 35 | -- Main2Activity.class 36 | -- Main3Activity.class 37 | -- Main4Activity.class 38 | ``` 39 | 40 | Then I started to analyse what this code is doing and execute it in my head. There was input field for a password, some string manipulation, and on successful check it loads next stage/class: 41 | 42 | ```Java 43 | //MainActivity.class 44 | 45 | if ((i >= 10) && (i <= 26)) 46 | { 47 | paramAnonymousView = new Intent(paramAnonymousView.getContext(), Main2Activity.class); 48 | paramAnonymousView.putExtra("edittext", str); 49 | MainActivity.this.startActivity(paramAnonymousView); 50 | } 51 | ``` 52 | 53 | The same thing with the next class: 54 | 55 | ```Java 56 | //Main2Activity.class 57 | 58 | if (Main2Activity.a(paramBundle).equals(str)) 59 | { 60 | paramAnonymousView = new Intent(paramAnonymousView.getContext(), Main3Activity.class); 61 | paramAnonymousView.putExtra("id", paramBundle); 62 | paramAnonymousView.putExtra("pass", str); 63 | Main2Activity.this.startActivity(paramAnonymousView); 64 | return; 65 | } 66 | ``` 67 | 68 | Ok, so we have a chain of classes, each doing some kind of check. As a proper REVERSE engineers, let's look at it backwards and look what's at the end of it. 69 | 70 | ```Java 71 | //Main4Activity.class 72 | 73 | System.loadLibrary("native-lib"); 74 | this.l = ((EditText)findViewById(2131230782)); 75 | this.l.setText(stringFromJNI()); 76 | ``` 77 | Oh, just look at what we have here! A JNI call to previously found library. And on top of that without any parameters or manipulations. So it looks like this library is what returns the flag. Let's decompile it. 78 | 79 | 80 | NOPE.JPG, ain't nobody got time to reverse that. We can do better, I hope you have prepared android environment with emulator/device and native libs. 81 | 82 | Lets just attach that lib to our project, launch it and see what 83 | ```Java 84 | public native String stringFromJNI(); 85 | ``` 86 | returns. So let's create a new project, using the same package id and class name as droid.apk app, and link it to that library. 87 | 88 | ... a few minutes later ... 89 | 90 | Ok, done. 91 | 92 | ``` 93 | build.gradle 94 | ... 95 | sourceSets { 96 | main { 97 | jniLibs.srcDir file('jni/') 98 | } 99 | } 100 | ... 101 | ``` 102 | 103 | ```Java 104 | package com.example.puing.a2018codegate; 105 | 106 | import android.support.v7.app.AppCompatActivity; 107 | import android.os.Bundle; 108 | import android.util.Log; 109 | 110 | public class Main4Activity extends AppCompatActivity { 111 | 112 | @Override 113 | protected void onCreate(Bundle savedInstanceState) { 114 | super.onCreate(savedInstanceState); 115 | setContentView(R.layout.activity_main4); 116 | 117 | System.loadLibrary("native-lib"); 118 | String s = stringFromJNI(); 119 | Log.w("myApp", s); 120 | } 121 | 122 | public native String stringFromJNI(); 123 | } 124 | ``` 125 | 126 | Executing it we have a log message: 127 | 128 | ``` 129 | com.example.puing.a2018codegate W/myApp: Wol!! awesome!! FLAG{W3_w3r3_Back_70_$3v3n7een!!!} hahahah!! 130 | ``` 131 | 132 | Done. 133 | -------------------------------------------------------------------------------- /2018/codegate-pre/welcome_to_droid/droid.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsnrain/ctf-writeups/6a6c973d9a3fc74bd077142a201175974d7b4343/2018/codegate-pre/welcome_to_droid/droid.apk -------------------------------------------------------------------------------- /2018/inshack/visual-hashing-hard/chall-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsnrain/ctf-writeups/6a6c973d9a3fc74bd077142a201175974d7b4343/2018/inshack/visual-hashing-hard/chall-1.png -------------------------------------------------------------------------------- /2018/inshack/visual-hashing-hard/visual-hashing-hard.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import math 3 | 4 | start = 97 5 | end = 122 6 | 7 | flg = "1d9c0db55855bc6478a047c9" 8 | 9 | mx = int("ffffff", 16) 10 | 11 | def square_distance(a): 12 | s = 0 13 | for i in a: 14 | s += i*i 15 | return s 16 | 17 | for a1 in range(start, end): 18 | for a2 in range(start, end): 19 | for a3 in range(start, end): 20 | for a4 in range(start, end): 21 | for a5 in range(start, end): 22 | psw = "INSA{" + chr(a1) + chr(a2) + chr(a3) + chr(a4) + chr(a5) + "}" 23 | hsh = hashlib.sha1(psw).hexdigest() 24 | 25 | p = [] 26 | for i in range(0, 12, 2): 27 | a = flg[i:i+1] 28 | b = hsh[i:i+1] 29 | x = abs(int(a, 16) - int(b, 16)) 30 | p.append(x) 31 | 32 | xx = square_distance(p) 33 | 34 | if xx < mx: 35 | mx = xx 36 | print xx 37 | print psw 38 | -------------------------------------------------------------------------------- /2018/insomnihack-finals/be_back/README.md: -------------------------------------------------------------------------------- 1 | ## be_back 2 | 3 | __Type:__ RE/iOS 4 | 5 | __Files:__ 6 | [beback.ipa](beback.ipa) 7 | 8 | ## Solution: 9 | As usual, let's start with the static analysis of the app. Right after unpacking this app we can see something interesting. Specifically, `Settings.plist` file. 10 | 11 | ```XML 12 | Settings.plist 13 | 14 | 15 | 16 | secret.srv 17 | http://beback.insomni.hack 18 | app.secret 19 | s3cret1NS1800000 20 | 21 | 22 | ``` 23 | 24 | Some server address and a secret key. The server seems to detect access from desktop and returns 25 | ``` 26 | ARE YOU KIDDING ME?!! 27 | ``` 28 | so we'll see how it is used by the app later. 29 | 30 | Also we can see that the app was written in Swift, judging by all those `libswift*.dylib` in Frameworks folder. 31 | 32 | Now let's take a look at the binary. 33 | 34 | ``` 35 | $ file be_back 36 | be_back: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O executable arm_v7] [arm64:Mach-O 64-bit executable arm64] 37 | be_back (for architecture armv7): Mach-O executable arm_v7 38 | be_back (for architecture arm64): Mach-O 64-bit executable arm64 39 | ``` 40 | Well fine, no simulator, we will have to use the real device then. Let's continue with disassembly, I used Hopper. 41 | 42 | Searching for strings, besides a lot of ObjC methods, revealed some interesting things. The most suspicious looking strings was following: 43 | ``` 44 | flagLbl 45 | hflagLbl 46 | ... 47 | fakeflag 48 | flag 49 | ... 50 | 8929343685299874 51 | ... 52 | INS{**************} 53 | ... 54 | "Jailbreaked Device! 55 | /Applications/Cydia.app 56 | /Library/MobileSubstrate/MobileSubstrate.dylib 57 | /bin/bash 58 | /usr/sbin/sshd 59 | /etc/apt 60 | cydia://package/com.example.package 61 | ``` 62 | 63 | First, we can see that app tries to perform simple Jailbreak detection, which is only a bit more useful than completely useless. 64 | Second, we can guess from it that there is two variables/textlabels for the flag, and one of them is fake, which is probably the string nearby, and that may be really useful. 65 | Then, there is a really suspicious 16 char/digit string which may be some encryption key or iv. 66 | 67 | Ok, time to see the disassembly. Searching for the refs to found strings lead us to some relatively big and interesting procedure. Just by glancing at it we can see that it starts with reading the `Settings.plist` we found earlier. 68 | 69 | By intercepting the traffic from the device we can see following: 70 | ``` 71 | >>> 72 | GET / HTTP/1.1 73 | Host: beback.insomni.hack 74 | Accept: */* 75 | User-Agent: be_back/1 CFNetwork/894 Darwin/17.4.0 76 | Accept-Language: en-us 77 | Accept-Encoding: gzip, deflate 78 | Connection: close 79 | 80 | <<< 81 | HTTP/1.1 200 OK 82 | Date: Fri, 23 Mar 2018 20:46:09 GMT 83 | Server: Apache/2.4.18 (Ubuntu) 84 | Content-Length: 44 85 | Connection: close 86 | Content-Type: text/html; charset=UTF-8 87 | 88 | iP8/kFDsdzx6tZo7vk65HlIgC7cTaTj+HmoG4EMouo0= 89 | ``` 90 | 91 | Base64 encoded string. Decoded it looks like encrypted data, most likely the flag. By now we already have everything that we need to get the flag. But let's continue to see what we still can do. 92 | 93 | By following the disassembly we can see that closer to the end two variables is used. First `objc_ivar_offset__TtC7be_back14ViewController_flag` then `objc_ivar_offset__TtC7be_back14ViewController_fakeflag` right next to `INS{**************}`. So it indeed looks like it saves the real flag after decryption, but displays only the fake flag. Now we have different choices - we can modify property in binary, or add dylib to the app and reflect needed property in runtime. (Then repack it and resign with valid certificate). 94 | 95 | Also one of the ways may be to attach a debugger after the decryption is done and just do 96 | ```po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]``` 97 | 98 | which will return 99 | ``` 100 | (lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] 101 | ; layer = > 102 | | > 103 | | |