├── .gitignore ├── 2024-0xl4augh ├── README.md ├── dance │ ├── bin │ │ └── dance │ ├── solve │ │ ├── dec.py │ │ └── extract_lib.py │ └── src │ │ ├── build_table.sh │ │ ├── chacha20.c │ │ ├── chacha20.h │ │ ├── compile.sh │ │ ├── crc32.c │ │ ├── dance.c │ │ ├── dance.h │ │ ├── dance_ops.h │ │ ├── generate_include.py │ │ ├── generate_include_opcode.py │ │ ├── libdance.c │ │ ├── patch_lib.py │ │ └── set_flag.py └── nano │ ├── bin │ └── nano │ ├── solve │ ├── fix.py │ └── slv.py │ └── src │ ├── nano.c │ └── setflag.py ├── flare-on_10 ├── README.md ├── ch00 │ ├── README.md │ ├── emu.py │ └── pics │ │ └── sec.png ├── ch01 │ └── README.md ├── ch02 │ ├── README.md │ ├── pics │ │ ├── doFinal.png │ │ └── iv.png │ └── slv.py ├── ch03 │ ├── README.md │ ├── dump.bin │ └── find_key_for_crc.py ├── ch04 │ ├── README.md │ ├── pics │ │ └── sauerbraten.png │ ├── stage2.bin │ ├── stage3.bin │ ├── stage4.bin │ ├── stage5.bin │ ├── stage6.bin │ ├── stg_1_aes.py │ ├── stg_2_rc4.py │ ├── stg_3_rc4.py │ ├── stg_4_rc4.py │ ├── stg_5_xor.py │ └── stg_6_get_flag.py ├── ch05 │ ├── README.md │ └── pics │ │ ├── good_option.png │ │ ├── nonsense.png │ │ ├── renamed.png │ │ └── yessense.png ├── ch06 │ ├── README.md │ └── patch.py ├── ch07 │ ├── README.md │ └── pics │ │ └── cheese.jpeg ├── ch08 │ ├── README.md │ ├── comm.py │ ├── enc_img.bin │ ├── pics │ │ └── sus.png │ └── powershell.enc ├── ch09 │ ├── 0x1000.dump │ ├── README.md │ ├── harness.py │ └── pics │ │ └── func.png ├── ch10 │ └── README.md ├── ch11 │ ├── README.md │ └── coppersmith_slv.sage ├── ch12 │ ├── README.md │ ├── pics │ │ ├── fixed.png │ │ ├── random.png │ │ └── rc4.png │ ├── rsrc_fixed.bin │ ├── slv.py │ └── test_array.py └── ch13 │ ├── README.md │ ├── pics │ └── zero.jpg │ ├── rop_to_shellcode.py │ └── solve.py └── flare-on_11 ├── README.md ├── ch01 ├── README.md └── slv.py ├── ch03 ├── README.md ├── aray.yara ├── bf.py └── slv.py ├── ch04 └── README.md ├── ch05 ├── README.md ├── emu.py ├── extract_shellcode.py └── liblzma.so.5.4.1 ├── ch06 ├── README.md └── slv.py ├── ch07 ├── README.md ├── c2_working.py ├── decrypt.py └── solve_script.sage ├── ch09 ├── README.md ├── emu_v4.py ├── emu_v7.py ├── requirements.txt ├── serpentine.exe ├── tables.py └── triton_solver.py └── ch10 ├── README.md ├── enc ├── catmeme1.jpg.c4tb ├── catmeme2.jpg.c4tb └── catmeme3.jpg.c4tb ├── uefi_shell.pe ├── vm1_and_2.py └── vm3.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /2024-0xl4augh/README.md: -------------------------------------------------------------------------------- 1 | Two ptrace based challenges I made for 0xL4augh CTF 2024. 2 | 3 | Writeups [here](https://matth.dmz42.org/posts/2024/ptrace_based_self_debugging_binaries/). 4 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/bin/dance: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/2024-0xl4augh/dance/bin/dance -------------------------------------------------------------------------------- /2024-0xl4augh/dance/solve/dec.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import ChaCha20 2 | 3 | key = b'l{\xb1\xee\nLG\x9d\xed\xd7[\xbc\xd2C\xdd@\x1d\xb2w\xb85nYK\xf8c&\xd7\xe2P\xed\xdb' 4 | nonce = b'\x96\xbf\xeb\xca\x8e|\xfb\xbc\xd9r\xa8S' 5 | message = b'\xb7\xe0]\xe8f\xae\xaej\xd9\x0f:WW\xcb\xb7{\xfeF\x9a\n\xf8/\xf8c\\^\xbc\x8b\xf6t\xf5fa\x97\xf1\x11\xc5il\xd5\xdb\xdd $6\x8al/\xfe' 6 | 7 | print(len(key)) 8 | 9 | c = ChaCha20.new(key=key, nonce=nonce) 10 | print(c.decrypt(message)) 11 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/solve/extract_lib.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import io 3 | from Crypto.Cipher import ChaCha20 4 | from zlib import crc32 5 | from pwn import u64, p32 6 | 7 | binary = "../dance" 8 | 9 | def get_lib(): 10 | with open(binary, "rb") as fp: 11 | 12 | # get libsize 13 | fp.seek(0x40a0) 14 | libsize = u64(fp.read(8)) 15 | 16 | # lib data 17 | fp.seek(0x40c0) 18 | libdata_enc = fp.read(libsize) 19 | 20 | c = ChaCha20.new(key=bytes.fromhex("48656c6c6f2c2074686174206973206f6e65206b657920666f7220796f752e2e"), 21 | nonce=bytes.fromhex("6e6963655f6d6f76655f3a29")) 22 | libdata = c.decrypt(libdata_enc) 23 | 24 | return libdata 25 | 26 | 27 | def get_nano_table(): 28 | _struct = "" \ 21 | | sort -R \ 22 | | sed 's/://' > /tmp/table.$$ 23 | 24 | python generate_include_opcode.py /tmp/table.$$ 25 | 26 | 27 | rm /tmp/table.$$ 28 | 29 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/chacha20.c: -------------------------------------------------------------------------------- 1 | /* 2 | * from https://github.com/Ginurx/chacha20-c.git 3 | */ 4 | 5 | #include "chacha20.h" 6 | 7 | 8 | __attribute__((__visibility__("hidden"))) static uint32_t rotl32(uint32_t x, int n) 9 | { 10 | return (x << n) | (x >> (32 - n)); 11 | } 12 | 13 | __attribute__((__visibility__("hidden"))) static uint32_t pack4(const uint8_t *a) 14 | { 15 | uint32_t res = 0; 16 | res |= (uint32_t)a[0] << 0 * 8; 17 | res |= (uint32_t)a[1] << 1 * 8; 18 | res |= (uint32_t)a[2] << 2 * 8; 19 | res |= (uint32_t)a[3] << 3 * 8; 20 | return res; 21 | } 22 | 23 | __attribute__((__visibility__("hidden"))) static void unpack4(uint32_t src, uint8_t *dst) { 24 | dst[0] = (src >> 0 * 8) & 0xff; 25 | dst[1] = (src >> 1 * 8) & 0xff; 26 | dst[2] = (src >> 2 * 8) & 0xff; 27 | dst[3] = (src >> 3 * 8) & 0xff; 28 | } 29 | 30 | __attribute__((__visibility__("hidden"))) static void chacha20_init_block(struct chacha20_context *ctx, uint8_t key[], uint8_t nonce[]) 31 | { 32 | memcpy(ctx->key, key, sizeof(ctx->key)); 33 | memcpy(ctx->nonce, nonce, sizeof(ctx->nonce)); 34 | 35 | // XXX 36 | //const uint8_t *magic_constant = (uint8_t*)"expand 32-byte k"; 37 | uint8_t magic_constant[] = {154, 135, 143, 158, 145, 155, 223, 204, 205, 210, 157, 134, 139, 154, 223, 148}; 38 | for (int i = 0; i < 16; i++) 39 | magic_constant[i] ^= 0xff; 40 | 41 | ctx->state[0] = pack4(magic_constant + 0 * 4); 42 | ctx->state[1] = pack4(magic_constant + 1 * 4); 43 | ctx->state[2] = pack4(magic_constant + 2 * 4); 44 | ctx->state[3] = pack4(magic_constant + 3 * 4); 45 | ctx->state[4] = pack4(key + 0 * 4); 46 | ctx->state[5] = pack4(key + 1 * 4); 47 | ctx->state[6] = pack4(key + 2 * 4); 48 | ctx->state[7] = pack4(key + 3 * 4); 49 | ctx->state[8] = pack4(key + 4 * 4); 50 | ctx->state[9] = pack4(key + 5 * 4); 51 | ctx->state[10] = pack4(key + 6 * 4); 52 | ctx->state[11] = pack4(key + 7 * 4); 53 | // 64 bit counter initialized to zero by default. 54 | ctx->state[12] = 0; 55 | ctx->state[13] = pack4(nonce + 0 * 4); 56 | ctx->state[14] = pack4(nonce + 1 * 4); 57 | ctx->state[15] = pack4(nonce + 2 * 4); 58 | 59 | memcpy(ctx->nonce, nonce, sizeof(ctx->nonce)); 60 | } 61 | 62 | __attribute__((__visibility__("hidden"))) static void chacha20_block_set_counter(struct chacha20_context *ctx, uint64_t counter) 63 | { 64 | ctx->state[12] = (uint32_t)counter; 65 | ctx->state[13] = pack4(ctx->nonce + 0 * 4) + (uint32_t)(counter >> 32); 66 | } 67 | 68 | __attribute__((__visibility__("hidden"))) static void chacha20_block_next(struct chacha20_context *ctx) { 69 | // This is where the crazy voodoo magic happens. 70 | // Mix the bytes a lot and hope that nobody finds out how to undo it. 71 | for (int i = 0; i < 16; i++) ctx->keystream32[i] = ctx->state[i]; 72 | 73 | #define CHACHA20_QUARTERROUND(x, a, b, c, d) \ 74 | x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 16); \ 75 | x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 12); \ 76 | x[a] += x[b]; x[d] = rotl32(x[d] ^ x[a], 8); \ 77 | x[c] += x[d]; x[b] = rotl32(x[b] ^ x[c], 7); 78 | 79 | for (int i = 0; i < 10; i++) 80 | { 81 | CHACHA20_QUARTERROUND(ctx->keystream32, 0, 4, 8, 12) 82 | CHACHA20_QUARTERROUND(ctx->keystream32, 1, 5, 9, 13) 83 | CHACHA20_QUARTERROUND(ctx->keystream32, 2, 6, 10, 14) 84 | CHACHA20_QUARTERROUND(ctx->keystream32, 3, 7, 11, 15) 85 | CHACHA20_QUARTERROUND(ctx->keystream32, 0, 5, 10, 15) 86 | CHACHA20_QUARTERROUND(ctx->keystream32, 1, 6, 11, 12) 87 | CHACHA20_QUARTERROUND(ctx->keystream32, 2, 7, 8, 13) 88 | CHACHA20_QUARTERROUND(ctx->keystream32, 3, 4, 9, 14) 89 | } 90 | 91 | for (int i = 0; i < 16; i++) ctx->keystream32[i] += ctx->state[i]; 92 | 93 | uint32_t *counter = ctx->state + 12; 94 | // increment counter 95 | counter[0]++; 96 | if (0 == counter[0]) 97 | { 98 | // wrap around occured, increment higher 32 bits of counter 99 | counter[1]++; 100 | // Limited to 2^64 blocks of 64 bytes each. 101 | // If you want to process more than 1180591620717411303424 bytes 102 | // you have other problems. 103 | // We could keep counting with counter[2] and counter[3] (nonce), 104 | // but then we risk reusing the nonce which is very bad. 105 | //XXX assert(0 != counter[1]); 106 | } 107 | } 108 | 109 | __attribute__((__visibility__("hidden"))) void chacha20_init_context(struct chacha20_context *ctx, uint8_t key[], uint8_t nonce[], uint64_t counter) 110 | { 111 | memset(ctx, 0, sizeof(struct chacha20_context)); 112 | 113 | chacha20_init_block(ctx, key, nonce); 114 | chacha20_block_set_counter(ctx, counter); 115 | 116 | ctx->counter = counter; 117 | ctx->position = 64; 118 | } 119 | 120 | __attribute__((__visibility__("hidden"))) void chacha20_xor(struct chacha20_context *ctx, uint8_t *bytes, size_t n_bytes) 121 | { 122 | uint8_t *keystream8 = (uint8_t*)ctx->keystream32; 123 | for (size_t i = 0; i < n_bytes; i++) 124 | { 125 | if (ctx->position >= 64) 126 | { 127 | chacha20_block_next(ctx); 128 | ctx->position = 0; 129 | } 130 | bytes[i] ^= keystream8[ctx->position]; 131 | ctx->position++; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/chacha20.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | struct chacha20_context 13 | { 14 | uint32_t keystream32[16]; 15 | size_t position; 16 | 17 | uint8_t key[32]; 18 | uint8_t nonce[12]; 19 | uint64_t counter; 20 | 21 | uint32_t state[16]; 22 | }; 23 | 24 | void chacha20_init_context(struct chacha20_context *ctx, uint8_t key[], uint8_t nounc[], uint64_t counter); 25 | 26 | void chacha20_xor(struct chacha20_context *ctx, uint8_t *bytes, size_t n_bytes); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # compile lib 4 | # this is a lame attempt at keeping the number of instruction under 0xfff 5 | # otherwise i get screwd by ASLR and i dont want to fix it 6 | gcc -O3 -Wno-attributes crc32.c -c -fpic 7 | gcc -O1 -Wno-attributes chacha20.c -c -fpic # prevent optimization to dexor the expand32... 8 | gcc -Wno-attributes libdance.c -c -fpic 9 | gcc -Wno-attributes libdance.o chacha20.o crc32.o -shared -o libdance.so 10 | strip -s libdance.so 11 | 12 | # generate opcode table 13 | ./build_table.sh > dance_ops.h 14 | 15 | # patch lib 16 | python patch_lib.py 17 | 18 | # generate lib include 19 | python generate_include.py > dance.h 20 | 21 | # compile bin 22 | gcc -Wno-attributes dance.c chacha20.c crc32.c -o dance 23 | strip -s dance 24 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/crc32.c: -------------------------------------------------------------------------------- 1 | /**********************************************************************\ 2 | |* Demonstration program to compute the 32-bit CRC used as the frame *| 3 | |* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| 4 | |* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| 5 | |* protocol). The 32-bit FCS was added via the Federal Register, *| 6 | |* 1 June 1982, p.23798. I presume but don't know for certain that *| 7 | |* this polynomial is or will be included in CCITT V.41, which *| 8 | |* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| 9 | |* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| 10 | |* errors by a factor of 10^-5 over 16-bit FCS. *| 11 | \**********************************************************************/ 12 | 13 | /* Copyright (C) 1986 Gary S. Brown. You may use this program, or 14 | code or tables extracted from it, as desired without restriction.*/ 15 | 16 | /* First, the polynomial itself and its table of feedback terms. The */ 17 | /* polynomial is */ 18 | /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ 19 | /* Note that we take it "backwards" and put the highest-order term in */ 20 | /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ 21 | /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ 22 | /* the MSB being 1. */ 23 | 24 | /* Note that the usual hardware shift register implementation, which */ 25 | /* is what we're using (we're merely optimizing it by doing eight-bit */ 26 | /* chunks at a time) shifts bits into the lowest-order term. In our */ 27 | /* implementation, that means shifting towards the right. Why do we */ 28 | /* do it this way? Because the calculated CRC must be transmitted in */ 29 | /* order from highest-order term to lowest-order term. UARTs transmit */ 30 | /* characters in order from LSB to MSB. By storing the CRC this way, */ 31 | /* we hand it to the UART in the order low-byte to high-byte; the UART */ 32 | /* sends each low-bit to hight-bit; and the result is transmission bit */ 33 | /* by bit from highest- to lowest-order term without requiring any bit */ 34 | /* shuffling on our part. Reception works similarly. */ 35 | 36 | /* The feedback terms table consists of 256, 32-bit entries. Notes: */ 37 | /* */ 38 | /* 1. The table can be generated at runtime if desired; code to do so */ 39 | /* is shown later. It might not be obvious, but the feedback */ 40 | /* terms simply represent the results of eight shift/xor opera- */ 41 | /* tions for all combinations of data and CRC register values. */ 42 | /* */ 43 | /* 2. The CRC accumulation logic is the same for all CRC polynomials, */ 44 | /* be they sixteen or thirty-two bits wide. You simply choose the */ 45 | /* appropriate table. Alternatively, because the table can be */ 46 | /* generated at runtime, you can start by generating the table for */ 47 | /* the polynomial in question and use exactly the same "updcrc", */ 48 | /* if your application needn't simultaneously handle two CRC */ 49 | /* polynomials. (Note, however, that XMODEM is strange.) */ 50 | /* */ 51 | /* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ 52 | /* of course, 32-bit entries work OK if the high 16 bits are zero. */ 53 | /* */ 54 | /* 4. The values must be right-shifted by eight bits by the "updcrc" */ 55 | /* logic; the shift must be unsigned (bring in zeroes). On some */ 56 | /* hardware you could probably optimize the shift in assembler by */ 57 | /* using byte-swap instructions. */ 58 | 59 | #include 60 | #include 61 | 62 | static const uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ 63 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 64 | 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 65 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 66 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 67 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 68 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 69 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 70 | 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 71 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 72 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 73 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 74 | 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 75 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 76 | 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 77 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 78 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 79 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 80 | 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 81 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 82 | 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 83 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 84 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 85 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 86 | 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 87 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 88 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 89 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 90 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 91 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 92 | 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 93 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 94 | 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 95 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 96 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 97 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 98 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 99 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 100 | 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 101 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 102 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 103 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 104 | 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 105 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 106 | }; 107 | 108 | #define UPDC32(octet,crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) 109 | 110 | __attribute__((__visibility__("hidden"))) uint32_t crc32(uint8_t *buf, size_t len) 111 | { 112 | uint32_t crc = 0xFFFFFFFF; 113 | 114 | for ( ; len; --len, ++buf) 115 | { 116 | crc = UPDC32(*buf, crc); 117 | } 118 | 119 | __asm__(".byte 0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00"); 120 | return crc; 121 | } 122 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/dance.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "chacha20.h" 15 | #include "dance.h" 16 | #include "dance_ops.h" 17 | 18 | //#define DEBUG 19 | 20 | /* 21 | * TODO: 22 | * suffer 23 | */ 24 | 25 | uint8_t dance_with_me(char *flag); 26 | uint32_t crc32(char *buf, size_t len); 27 | 28 | 29 | /* just ptrace() but without libc just to fuck around 30 | */ 31 | long _ptrace(long request, long pid, void *addr, void *data) 32 | { 33 | long ret; 34 | 35 | __asm__ volatile( 36 | "mov $79, %%rax\n" 37 | "mov %0, %%rdi\n" 38 | "mov %1, %%rsi\n" 39 | "mov %2, %%rdx\n" 40 | ".byte 0x74,0x03,0x75,0x01,0xe8\n" 41 | ".byte 0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00\n" 42 | "xor $42, %%rax\n" 43 | "mov %3, %%r10\n" 44 | //"mov $101, %%rax\n" 45 | "syscall" : : "g"(request), "g"(pid), "g"(addr), "g"(data)); 46 | asm("mov %%rax, %0" : "=r"(ret)); 47 | return ret; 48 | } 49 | 50 | 51 | 52 | /* 53 | * stolen with love from 54 | * https://github.com/elfmaster/saruman/blob/master/launcher.c 55 | */ 56 | int pid_write(int pid, void *dest, const void *src, size_t len) 57 | { 58 | size_t rem = len % sizeof(void *); 59 | size_t quot = len / sizeof(void *); 60 | unsigned char *s = (unsigned char *) src; 61 | unsigned char *d = (unsigned char *) dest; 62 | 63 | while (quot-- != 0) { 64 | #ifdef DEBUG 65 | printf("pid_write(addr = 0x%llx)\n", d); 66 | #endif 67 | 68 | 69 | if (_ptrace(PTRACE_POKEDATA, pid, d, *(void **)s) == -1 ) 70 | goto out_error; 71 | s += sizeof(void *); 72 | d += sizeof(void *); 73 | } 74 | 75 | if (rem != 0) { 76 | long w; 77 | unsigned char *wp = (unsigned char *)&w; 78 | 79 | /* 80 | PTRACE_PEEK* kernel and libc exposed API are not the same 81 | libc returns peeked data in eax, kernel does in *addr 82 | */ 83 | //w = ptrace(PTRACE_PEEKDATA, pid, d, NULL); 84 | _ptrace(PTRACE_PEEKDATA, pid, d, &w); 85 | if (w == -1 && errno != 0) { 86 | d -= sizeof(void *) - rem; 87 | 88 | //w = ptrace(PTRACE_PEEKDATA, pid, d, NULL); 89 | _ptrace(PTRACE_PEEKDATA, pid, d, &w); 90 | if (w == -1 && errno != 0) 91 | goto out_error; 92 | 93 | wp += sizeof(void *) - rem; 94 | } 95 | 96 | while (rem-- != 0) 97 | wp[rem] = s[rem]; 98 | 99 | if (_ptrace(PTRACE_POKEDATA, pid, (void *)d, (void *)w) == -1) 100 | goto out_error; 101 | } 102 | 103 | return 0; 104 | 105 | out_error: 106 | //fprintf(stderr, "pid_write() failed, pid: %d: %s\n", pid, strerror(errno)); 107 | return -1; 108 | } 109 | 110 | /* main nanomite stuff */ 111 | void lets_go(char *flag) 112 | { 113 | int fd; 114 | char libpath[256]; 115 | pid_t pid; 116 | uint8_t (*dance_with_me)(uint8_t *); 117 | int status; 118 | struct user_regs_struct regs; 119 | uint32_t base_addr, crc; 120 | uint64_t prev_size, prev_addr = 0; 121 | 122 | /* child */ 123 | if ((pid = fork()) == 0) 124 | { 125 | _ptrace(PTRACE_TRACEME, pid, NULL, NULL); 126 | 127 | /* open memfd */ 128 | fd = memfd_create("", 0); 129 | sprintf(libpath, "/proc/self/fd/%d", fd); 130 | 131 | /* decrypt */ 132 | struct chacha20_context ctx; 133 | uint8_t key[] = {72, 101, 108, 108, 111, 44, 32, 116, 104, 97, 116, 32, 105, 115, 32, 111, 110, 101, 32, 107, 101, 121, 32, 102, 111, 114, 32, 121, 111, 117, 46, 46}; 134 | uint8_t nonce[] = {110, 105, 99, 101, 95, 109, 111, 118, 101, 95, 58, 41}; 135 | chacha20_init_context(&ctx, key, nonce, 0); 136 | chacha20_xor(&ctx, (uint8_t *)&libdata, libsize); 137 | 138 | /* copy data */ 139 | ssize_t sz = libsize; 140 | ssize_t written; 141 | 142 | while (sz > 0) { 143 | written = write(fd, libdata + (libsize - sz), sz); 144 | sz -= written; 145 | } 146 | 147 | 148 | //void *libdance = dlopen("./libdance.so", RTLD_NOW); 149 | void *libdance = dlopen(libpath, RTLD_NOW); 150 | dance_with_me = dlsym(libdance, "dance_with_me"); 151 | #ifdef DEBUG 152 | printf("dance_with_me: %p\n", dance_with_me); 153 | #endif 154 | 155 | if (!dance_with_me(flag)) 156 | puts("ok"); 157 | else 158 | puts("nop"); 159 | 160 | dlclose(libdance); 161 | exit(0); 162 | } 163 | 164 | /* parent */ 165 | _ptrace(PTRACE_ATTACH, pid, NULL, NULL); 166 | while (1) 167 | { 168 | waitpid(pid, &status, 0); 169 | 170 | if (WIFEXITED(status)) 171 | break; 172 | 173 | if (WIFCONTINUED(status)) 174 | continue; 175 | 176 | if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) 177 | //if ((WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) || (WIFSTOPPED(status) && WSTOPSIG(status) == SIGILL)) 178 | { 179 | 180 | if (prev_size != 0 && prev_addr != 0) { 181 | #ifdef DEBUG 182 | printf("patchback @ 0x%x : %d\n", prev_addr, prev_size); 183 | #endif 184 | pid_write(pid, (void *)prev_addr, "\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc", prev_size); 185 | } 186 | 187 | 188 | _ptrace(PTRACE_GETREGS, pid, NULL, ®s); 189 | 190 | 191 | base_addr = (regs.rip - 1) & 0xfff; 192 | crc = ~crc32((uint8_t *)&base_addr, 4); 193 | 194 | #ifdef DEBUG 195 | printf("crashed @ %p\n", regs.rip); 196 | printf("crc32: 0x%x\n", crc); 197 | #endif 198 | 199 | /* look for instruction and patch it */ 200 | uint32_t i = -1; 201 | do { 202 | i++; 203 | if (insns[i].crc32 == crc) { 204 | break; 205 | } 206 | } while (insns[i].crc32 != 0); 207 | 208 | #ifdef DEBUG 209 | for (int v = 0; v < insns[i].num_ins; v++) 210 | printf("0x%02x ", insns[i].code[v]); 211 | puts(""); 212 | #endif 213 | 214 | pid_write(pid, (void *)(regs.rip - 1), insns[i].code, insns[i].num_ins); 215 | 216 | // backup for patchback 217 | prev_size = insns[i].num_ins; 218 | prev_addr = regs.rip -1; 219 | 220 | 221 | regs.rip -= 1; 222 | _ptrace(PTRACE_SETREGS, pid, NULL, ®s); 223 | 224 | } 225 | 226 | _ptrace(PTRACE_CONT, pid, NULL, NULL); 227 | } 228 | } 229 | 230 | 231 | /* entry */ 232 | int main(int argc, char **argv) 233 | { 234 | pid_t pid; 235 | int status; 236 | 237 | if (argc != 2) { 238 | printf("usage: %s \n", argv[0]); 239 | exit(1); 240 | } 241 | 242 | //lets_go(argv[1]); 243 | //exit(0); 244 | 245 | /* fork and attach */ 246 | if ((pid = fork()) == 0) { 247 | lets_go(argv[1]); 248 | } 249 | 250 | /* trace child */ 251 | _ptrace(PTRACE_ATTACH, pid, NULL, NULL); 252 | while (1) { 253 | waitpid(pid, &status, 0); 254 | 255 | if (WIFEXITED(status)) 256 | break; 257 | if (WIFCONTINUED(status)) 258 | continue; 259 | _ptrace(PTRACE_CONT, pid, NULL, NULL); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/generate_include.py: -------------------------------------------------------------------------------- 1 | 2 | def split(line, n): 3 | return [line[i:i+n] for i in range(0, len(line), n)] 4 | 5 | data = open("libdance.so_patched", "rb").read() 6 | 7 | print("static ssize_t libsize = %d;"%len(data)) 8 | print("uint8_t libdata[] = {") 9 | for line in split(data, 16): 10 | print(" ", end="") 11 | for c in line: 12 | print("0x%02x, "%c, end='') 13 | print("") 14 | print("};") 15 | 16 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/generate_include_opcode.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import zlib 3 | from pwn import p32 4 | 5 | 6 | print(""" 7 | typedef struct { 8 | uint32_t crc32; 9 | uint8_t num_ins; 10 | uint8_t code[16]; 11 | } instruction; 12 | 13 | 14 | instruction insns[] = 15 | { 16 | """) 17 | 18 | def print_line(crc, num, opcodes): 19 | print(" {0x%08x, 0x%02x, {"%(crc, num), end='') 20 | print(", ".join(["0x%02x"%_ for _ in opcodes]), end='') 21 | print("}},") 22 | 23 | 24 | with open(sys.argv[1], 'r') as fp: 25 | for line in fp: 26 | line = line.strip().split() 27 | addr = int(line[0], 16) 28 | bytecode = bytes.fromhex(''.join(line[1:])) 29 | numops = len(bytecode) 30 | bytecode = bytecode.ljust(16, b'\x00') 31 | crc = zlib.crc32(p32(addr & 0xfff)) 32 | print_line(crc, numops, bytecode) 33 | 34 | print_line(0, 0, b'\x00'*16) 35 | 36 | print("};") 37 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/libdance.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "chacha20.h" 6 | 7 | 8 | #define BUFF_SIZE 256 9 | #define PATH_MAX 4096 10 | 11 | //#define DEBUG 12 | 13 | uint32_t crc32(char *buf, size_t len); 14 | uint8_t crc_check(); 15 | 16 | __attribute__((constructor)) static void init() { 17 | uint8_t zob = crc_check(); 18 | } 19 | 20 | 21 | /* 22 | * exit() syscall 23 | */ 24 | __attribute__((__visibility__("hidden"))) void sys_exit(int status) 25 | { 26 | __asm__ volatile( 27 | "mov $58, %%rax\n" 28 | "mov %0, %%rdi\n" 29 | "add $2, %%rax\n" 30 | "syscall" : : "g"(status)); 31 | } 32 | 33 | /* 34 | * find nop marker between start and end addresses 35 | */ 36 | __attribute__((__visibility__("hidden"))) uint64_t find_marker(uint8_t *start, uint64_t end) 37 | { 38 | while ((uint64_t)start < (uint64_t)(end - 8)) 39 | { 40 | if ( ((*(start + 0)) == 0x66) && ((*(start + 1)) == 0x0f) && ((*(start + 2)) == 0x1f) && ((*(start + 3)) == 0x84) && \ 41 | ((*(start + 4)) == 0x00) && ((*(start + 5)) == 0x00) && ((*(start + 6)) == 0x00) && \ 42 | ((*(start + 7)) == 0x00) && ((*(start + 8)) == 0x00)) 43 | break; 44 | start++; 45 | } 46 | 47 | if ((uint64_t)start == end - 8) 48 | return 0; 49 | return (uint64_t)start; 50 | } 51 | 52 | 53 | /* 54 | * find 2 markers in .text and do crc check 55 | */ 56 | __attribute__((__visibility__("hidden"))) uint8_t crc_check() 57 | { 58 | FILE *fp; 59 | ssize_t n; 60 | 61 | char bf[PATH_MAX+1024]; 62 | uint64_t start; 63 | uint64_t end; 64 | char prot[5]; 65 | uint64_t pgoff; 66 | uint16_t min, maj; 67 | char execname[PATH_MAX]; 68 | unsigned int ino; 69 | 70 | if ((fp = fopen("/proc/self/maps", "r")) == NULL) { 71 | #ifdef DEBUG 72 | puts("bail at fopen"); 73 | #endif 74 | puts("i'm dead"); 75 | sys_exit(1); 76 | return 1; 77 | } 78 | 79 | while (1) 80 | { 81 | 82 | if (fgets(bf, sizeof(bf), fp) == NULL) 83 | break; 84 | 85 | strcpy(execname, ""); 86 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ 87 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", 88 | &start, &end, prot, &pgoff, &maj, &min, &ino, execname); 89 | 90 | /* found first r-x */ 91 | if (prot[0] == 'r' && prot[2] == 'x') 92 | break; 93 | } 94 | fclose(fp); 95 | 96 | #ifdef DEBUG 97 | printf("%p %p %s %s\n", start, end, prot, execname); 98 | #endif 99 | 100 | /* find markers */ 101 | uint64_t m_start = find_marker((uint8_t *)(start + 0x100), end); 102 | #ifdef DEBUG 103 | printf("%p\n", m_start); 104 | #endif 105 | if (m_start == 0) 106 | sys_exit(42); 107 | 108 | uint64_t m_end = find_marker((uint8_t *)(m_start + 16), end); 109 | #ifdef DEBUG 110 | printf("%p\n", m_end); 111 | #endif 112 | if (m_end == 0) 113 | sys_exit(42); 114 | 115 | 116 | uint32_t crc = crc32((void *)m_start, m_end-m_start); 117 | 118 | //uncomment to dump crc and fix the value below 119 | //printf("text_crc: %p\n", crc); 120 | 121 | if (crc != 0x5285f228) { 122 | #ifdef DEBUG 123 | puts("wrong crc"); 124 | #endif 125 | puts("i'm dead"); 126 | sys_exit(42); 127 | return 1; 128 | } 129 | 130 | return 0; 131 | } 132 | 133 | /* 134 | * skip lib function 135 | */ 136 | __attribute__((__visibility__("hidden"))) int my_memcmp(void *b, void *c, int len) 137 | { 138 | unsigned char *p = b; 139 | unsigned char *q = c; 140 | 141 | while (len > 0) 142 | { 143 | if (*p != *q) 144 | return (*p - *q); 145 | len--; 146 | p++; 147 | q++; 148 | } 149 | return 0; 150 | } 151 | 152 | 153 | uint8_t dance_with_me(char *flag) 154 | { 155 | uint8_t chk; 156 | 157 | uint8_t enc[] = { 158 | 183, 224, 93, 232, 102, 174, 174, 106, 217, 15, 58, 87, 87, 203, 183, 123, 254, 70, 154, 10, 248, 47, 248, 99, 92, 94, 188, 139, 246, 116, 245, 102, 97, 151, 241, 17, 197, 105, 108, 213, 219, 221, 32, 36, 54, 138, 108, 47, 254 159 | }; 160 | 161 | uint8_t key[] = { 162 | 108, 123, 177, 238, 10, 76, 71, 157, 237, 215, 91, 188, 210, 67, 221, 163 | 64, 29, 178, 119, 184, 53, 110, 89, 75, 248, 99, 38, 215, 226, 80, 237, 219 164 | }; 165 | 166 | uint8_t nonce[] = { 167 | 150, 191, 235, 202, 142, 124, 251, 188, 217, 114, 168, 83 168 | }; 169 | 170 | struct chacha20_context ctx; 171 | size_t input_len = strlen(flag); 172 | 173 | 174 | chacha20_init_context(&ctx, key, nonce, 0); 175 | if ((chk = crc_check()) == 1) { 176 | puts("i'm dead"); 177 | return 1; 178 | } 179 | chacha20_xor(&ctx, flag, input_len); 180 | 181 | if (input_len < sizeof(enc)) 182 | return 1; 183 | if (!my_memcmp(flag, enc, sizeof(enc))) 184 | return 0; 185 | return 1; 186 | } 187 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/patch_lib.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from Crypto.Cipher import ChaCha20 4 | import lief 5 | 6 | # objdump -M intel -j .text -d libdance.so 7 | 8 | dance = lief.ELF.parse("libdance.so") 9 | s = dance.get_section(".text") 10 | 11 | start_offset = s.virtual_address 12 | end_offset = start_offset + s.size - 1 13 | 14 | data = bytearray(open('libdance.so', 'rb').read()) 15 | 16 | # pad to some size - that helps keeping the crc OK :^) 17 | pad = 14300 18 | lol = b'this is so lame '*256 19 | data = data + lol[:pad - len(data)] 20 | 21 | while start_offset <= end_offset: 22 | data[start_offset] = 0xcc 23 | start_offset += 1 24 | 25 | key = bytes.fromhex("48656c6c6f2c2074686174206973206f6e65206b657920666f7220796f752e2e") 26 | nonce = bytes.fromhex("6e6963655f6d6f76655f3a29") 27 | c = ChaCha20.new(key=key, nonce=nonce) 28 | enc_data = c.encrypt(data) 29 | 30 | with open('libdance.so_patched', 'wb') as fp: 31 | fp.write(enc_data) 32 | 33 | 34 | with open('libdance.so_patched_clear', 'wb') as fp: 35 | fp.write(data) 36 | -------------------------------------------------------------------------------- /2024-0xl4augh/dance/src/set_flag.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import ChaCha20 2 | 3 | flag = b'0xL4ugh{i_h0p3_you_l1k3d_ptr4c3_and_lat1n_danc3s}' 4 | key = b'l{\xb1\xee\nLG\x9d\xed\xd7[\xbc\xd2C\xdd@\x1d\xb2w\xb85nYK\xf8c&\xd7\xe2P\xed\xdb' 5 | nonce = b'\x96\xbf\xeb\xca\x8e|\xfb\xbc\xd9r\xa8S' 6 | 7 | c = ChaCha20.new(key=key, nonce=nonce) 8 | enc = c.encrypt(flag) 9 | 10 | print("enc") 11 | print([_ for _ in enc]) 12 | print("key") 13 | print([_ for _ in key]) 14 | print("nonce") 15 | print([_ for _ in nonce]) 16 | 17 | -------------------------------------------------------------------------------- /2024-0xl4augh/nano/bin/nano: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/2024-0xl4augh/nano/bin/nano -------------------------------------------------------------------------------- /2024-0xl4augh/nano/solve/fix.py: -------------------------------------------------------------------------------- 1 | data = open("nano", "rb").read() 2 | 3 | # nop anti disass 4 | data = data.replace(b"\x74\x03\x75\x01\xe8", b"\x90\x90\x90\x90\x90") 5 | 6 | open("nano_fixed", "wb").write(data) 7 | -------------------------------------------------------------------------------- /2024-0xl4augh/nano/solve/slv.py: -------------------------------------------------------------------------------- 1 | 2 | flag = bytearray(b'\x0c\x5c\x60\x20\x69\x63\x64\x0f\x4f\x1e\x33\x3a\x68\x2a\x7c\xd9\xd5\xd0\xc9\xe7\xc3\xf0\xbc\xab\x9b\xd7\x98\x8b\xaf\xb0\xf8\x47\x49\x16\x49\x68') 3 | 4 | for rnd in range(1, len(flag)+1): 5 | k = ((rnd * 0x8) & 0xff ^ 0xca | (rnd >> 5)) ^ 0xfe 6 | flag[rnd-1] ^= k 7 | 8 | print(flag) 9 | 10 | -------------------------------------------------------------------------------- /2024-0xl4augh/nano/src/nano.c: -------------------------------------------------------------------------------- 1 | // compile with gcc nano.c -no-pie -o nano 2 | // adding optimization will screw it up 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define PTRACE_SYSCALL func_00101189 13 | #define JZJNZ __asm__ volatile(".byte 0x74,0x03,0x75,0x01,0xe8\n"); 14 | 15 | // encrypted flag 16 | uint8_t flag[] = {12, 92, 96, 32, 105, 99, 100, 15, 79, 30, 51, 58, 104, 42, 124, 217, 213, 208, 201, 231, 195, 240, 188, 171, 155, 215, 152, 139, 175, 176, 248, 71, 73, 22, 73, 104}; 17 | 18 | // fake troll key 19 | uint8_t KEY[] = {123, 61, 20, 67, 1, 67, 94, 47, 39, 106, 71, 74, 27, 16, 83, 246, 172, 191, 188, 147, 182, 222, 222, 206, 180, 179, 201, 252, 155, 199, 193, 16, 46, 78, 42, 57}; 20 | 21 | 22 | /* just ptrace() but without libc just to fuck around 23 | */ 24 | long func_00101189(long request, long pid, void *addr, void *data) 25 | { 26 | long ret; 27 | 28 | __asm__ volatile( 29 | "mov $79, %%rax\n" 30 | "mov %0, %%rdi\n" 31 | "mov %1, %%rsi\n" 32 | "mov %2, %%rdx\n" 33 | ".byte 0x74,0x03,0x75,0x01,0xe8\n" 34 | "xor $42, %%rax\n" 35 | "mov %3, %%r10\n" 36 | "syscall" : : "g"(request), "g"(pid), "g"(addr), "g"(data)); 37 | asm("mov %%rax, %0" : "=r"(ret)); 38 | return ret; 39 | } 40 | 41 | int check(uint8_t *f) 42 | { 43 | int max = strlen(f); 44 | int meh = 0; 45 | register int k asm ("r12"); /* r12 holds the xorkey */ 46 | 47 | for (uint8_t i = 0; i < 36; i++) 48 | { 49 | if (i > max) 50 | return 1; 51 | 52 | /* load fake key please decompilers :p */ 53 | k = KEY[i]; 54 | 55 | /* trigger sigsegv and crash */ 56 | asm("mov (0), %r11\n"); 57 | 58 | /* here the father catched the sigsegv and set the correct key in r12 */ 59 | uint8_t c = flag[i]; 60 | uint8_t e = f[i]^(k&0xff); 61 | 62 | if (e != c) 63 | meh = 1; 64 | } 65 | return meh; 66 | } 67 | 68 | 69 | int main(int argc, char **argv) 70 | { 71 | 72 | pid_t pid; 73 | int status; 74 | struct user_regs_struct regs; 75 | int lol = 0; 76 | uint8_t k; 77 | struct user_regs_struct *tmp; 78 | 79 | 80 | if (argc != 2) { 81 | printf("usage: %s \n", argv[0]); 82 | exit(1); 83 | } 84 | 85 | 86 | 87 | /* child */ 88 | if ((pid = fork()) == 0) 89 | { 90 | JZJNZ; 91 | PTRACE_SYSCALL(PTRACE_TRACEME, pid, NULL, NULL); 92 | if (check(argv[1]) == 0) 93 | puts("yes"); 94 | else 95 | puts("no"); 96 | exit(0); 97 | } 98 | 99 | /* parent */ 100 | PTRACE_SYSCALL(PTRACE_ATTACH, pid, NULL, NULL); 101 | while (1) 102 | { 103 | waitpid(pid, &status, 0); 104 | 105 | if (WIFEXITED(status)) 106 | break; 107 | 108 | if (WIFCONTINUED(status)) 109 | continue; 110 | 111 | if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) 112 | { 113 | // compute real key 114 | lol += 1; 115 | k = ((lol << 3) & 0xff); 116 | JZJNZ; 117 | k ^= 0xca; 118 | JZJNZ; 119 | k |= (lol >> 5); 120 | JZJNZ; 121 | k ^= 0xfe; 122 | 123 | /* fix dead child and revive it, stronger than jesus */ 124 | JZJNZ; 125 | PTRACE_SYSCALL(PTRACE_GETREGS, pid, NULL, ®s); 126 | tmp = ®s; 127 | 128 | regs.r12 = 0x7ffc9286a800 | k; // set real key to r12 reg of the child 129 | regs.rip += 8; // skip the 8 bytes of the mov r12, [0] 130 | 131 | PTRACE_SYSCALL(PTRACE_SETREGS, pid, NULL, ®s); 132 | tmp = ®s; 133 | JZJNZ; 134 | } 135 | JZJNZ; 136 | PTRACE_SYSCALL(PTRACE_CONT, pid, NULL, NULL); 137 | } 138 | 139 | 140 | 141 | return 0; 142 | } 143 | 144 | 145 | -------------------------------------------------------------------------------- /2024-0xl4augh/nano/src/setflag.py: -------------------------------------------------------------------------------- 1 | 2 | from pwn import * 3 | 4 | flag = b'0xL4ugh{3z_n4n0mites_t0_g3t_st4rt3d}' 5 | fake = b'watch : https://youtu.be/dQw4w9WgXcQ' 6 | 7 | k = [] 8 | for x in range(1, 37): 9 | v = (x << 3) & 0xff 10 | v ^= 0xca 11 | v |= (x >> 5) 12 | v ^= 0xfe 13 | k.append(v) 14 | 15 | 16 | f = xor(flag, k) 17 | print("flag") 18 | print([_ for _ in f]) 19 | print("key") 20 | print([_ for _ in xor(f, fake)]) 21 | -------------------------------------------------------------------------------- /flare-on_10/README.md: -------------------------------------------------------------------------------- 1 | # Flare-On 10 "writeups" 2 | 3 | These are quick & dirty writeups (such a big words) for the Flare-On 10 challenges. 4 | 5 | I rushed a bit through the CTF because it's not like i can afford taking some vacation time to do it. 6 | 7 | I'm also trying to stay in good terms with both my family and my employer, so I tried to go straight to the point and not spent my whole free time on it during a month and half. 8 | 9 | That being said, i'm not going to spent another 2 weeks writing detailed solutions, some folks already did an amazing job at that: 10 | - [jk42](https://twitter.com/jkfourtwo) goes into a [great deal of detail](https://github.com/jk45054/CTF-writeups/tree/main/Flare-On%2010) 11 | - [Jesko Hüttenhain](https://twitter.com/huettenhain) wrote the [mandatory BinRef writeup](https://github.com/binref/refinery/blob/master/tutorials/tbr-files.v0x08.flare.on.10.ipynb) any (in)sane person was waiting for 12 | - And of course the collection of [official solutions](https://www.mandiant.com/resources/blog/flareon10-challenge-solutions) 13 | 14 | You'll find here some quick writeups, cheesy solutions and somewhat low quality, cursed accompanying code - barely touched after getting the flags. The raw CTF experience :) 15 | -------------------------------------------------------------------------------- /flare-on_10/ch00/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Warmup 2 | 3 | Few days before the start of the event, [@m_r_tz](https://twitter.com/m_r_tz/status/1705204684393222654) twitted the following picture 4 | 5 | ![sec](pics/sec.png) 6 | 7 | from which we can extract the following base64 8 | 9 | ``` 10 | eJwVkLtPWmEchgNIalosxdjooC6OLlWnbj3q2xyS4yVGNA6YOMhkKtG1GwuXnMXJPwPv6HQO4KvoAbH324VqrxQrahU2+Dm8efLle7/nTb4nPjVWUkOlIBHv0vQhg1gJaLpPuDqHPsHajHtSsL5we9iYJTbnia1nRKKH2H40GMaO2wNDjaAszAciqAoLhLFImHeIZBuRaiTSrcTuPYIuYk+47yEyU8TBKHH4lLA6iWwDkXMSR8J8B3F8l3gxTryUvHIQrx8Sb4Rv7xPv2mX7PfFBeh+HiU/TxOd+4ovcf5V3hSbim/RPZPdU8n2E+DFA/JTdX2PE7wniTzNR1Ii/LUTJRpw9IP6J+9xOlP3EhZe4FN+V+P6L71p8N+KqSKpKImtZllIpKDFDqRlm0aG7lhyZblNdjmrOuBK1eWtmr6mE7d5aUglVHz/Pqbr8VCgd9KfqTrG9kg== 11 | ``` 12 | 13 | this is a zlib compress shellcode that does: 14 | 15 | ```python 16 | >>> from pwn import xor 17 | >>> 18 | >>> k = b'#flareon10' 19 | >>> s = b's\x07\x18\x08\x17\x0b\x0c\x0b\x11YPF\x1e\x04\x05\x04\x1d\nTT\x03\x15\x03\x0e\x1c\x00\x1dN^B\x03\n\r\x15\x17\x17OC\x11RV\x12L\x14\x01\x10\x0e\x02]I\x03\n\r\x15\x17\x17A' 20 | >>> print(xor(k, s)) 21 | b'Patience is rewarded sooner or later - but usually later.' 22 | ``` 23 | 24 | 25 | Alternatively it's an easy unicorn target (yes, i am in a [unicorn](https://www.unicorn-engine.org/) mood lately): [emu.py](emu.py) 26 | -------------------------------------------------------------------------------- /flare-on_10/ch00/emu.py: -------------------------------------------------------------------------------- 1 | from unicorn import * 2 | from unicorn.x86_const import * 3 | import base64 4 | import zlib 5 | 6 | 7 | code = zlib.decompress(base64.b64decode("eJwVkLtPWmEchgNIalosxdjooC6OLlWnbj3q2xyS4yVGNA6YOMhkKtG1GwuXnMXJPwPv6HQO4KvoAbH324VqrxQrahU2+Dm8efLle7/nTb4nPjVWUkOlIBHv0vQhg1gJaLpPuDqHPsHajHtSsL5we9iYJTbnia1nRKKH2H40GMaO2wNDjaAszAciqAoLhLFImHeIZBuRaiTSrcTuPYIuYk+47yEyU8TBKHH4lLA6iWwDkXMSR8J8B3F8l3gxTryUvHIQrx8Sb4Rv7xPv2mX7PfFBeh+HiU/TxOd+4ovcf5V3hSbim/RPZPdU8n2E+DFA/JTdX2PE7wniTzNR1Ii/LUTJRpw9IP6J+9xOlP3EhZe4FN+V+P6L71p8N+KqSKpKImtZllIpKDFDqRlm0aG7lhyZblNdjmrOuBK1eWtmr6mE7d5aUglVHz/Pqbr8VCgd9KfqTrG9kg==")) 8 | 9 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 10 | mu.mem_map(0x100000, 0x1000) 11 | mu.mem_map(0x700000, 0x1000) 12 | 13 | mu.mem_write(0x100000, code) 14 | mu.reg_write(UC_X86_REG_RBP, 0x700100) 15 | mu.reg_write(UC_X86_REG_RSP, 0x700100) 16 | mu.emu_start(0x100000, 0x100000 + len(code) - 1) # skip ret that sends us to unmapped shithole 17 | print(mu.mem_read(0x700000, 0x100).strip(b'\x00')) 18 | -------------------------------------------------------------------------------- /flare-on_10/ch00/pics/sec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch00/pics/sec.png -------------------------------------------------------------------------------- /flare-on_10/ch01/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Window Reverse Engineer 2 | 3 | It's a windows application, i heard windows people like to use the mouse. 4 | 5 | Since it's a 2 digits lock, it's faster to bruteforce it with your mouse than figure it's a DotNET app and open it 6 | 7 | ## 2. Certified Forensic Analyst 8 | 9 | It turned out the FLARE team actually made a typical CTF forensic challenge: just run strings on it and get the flag. 10 | 11 | ``` 12 | % strings -e l X.dll | grep flare 13 | glorified_captcha@flare-on.com 14 | ``` 15 | 16 | But since it's a 500 points hard forensic challenge, you actually have to give `strings` the correct parameter to show widestrings (it also proves your boss your 10k cert was worth it). 17 | -------------------------------------------------------------------------------- /flare-on_10/ch02/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Read code 2 | 3 | use jadx-gui and read code. 4 | 5 | Looked for the crypto and walked my way up the xrefs. The firebase stuff was safely ignored :) 6 | 7 | ![crypto](pics/doFinal.png) 8 | 9 | 10 | ## 2. Extract data 11 | 12 | ``` 13 | % java -jar apktool_2.8.1.jar d -o out -f ItsOnFire.apk 14 | ``` 15 | 16 | ## 3. Decrypt 17 | 18 | after reading code and extracting data you should be able to [decrypt](slv.py) it ezpz. 19 | 20 | ![iv](pics/iv.png) 21 | -------------------------------------------------------------------------------- /flare-on_10/ch02/pics/doFinal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch02/pics/doFinal.png -------------------------------------------------------------------------------- /flare-on_10/ch02/pics/iv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch02/pics/iv.png -------------------------------------------------------------------------------- /flare-on_10/ch02/slv.py: -------------------------------------------------------------------------------- 1 | 2 | import zlib 3 | from Crypto.Cipher import AES 4 | 5 | s1 = b"https://flare-on.com/evilc2server/report_token/report_token.php?token=" 6 | s2 = b"wednesday" 7 | 8 | s = b"" 9 | s += s1[4:10] 10 | s += s2[2:5] 11 | 12 | c = str(zlib.crc32(s)) 13 | out = c + c 14 | print(out) 15 | key = bytes(out[0:16], 'ascii') 16 | print(key) 17 | 18 | iv = b'abcdefghijklmnop' 19 | 20 | c = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) 21 | data = open("out/res/raw/ps.png", "rb").read() 22 | open("ps.png", "wb").write(c.decrypt(data)) 23 | 24 | c = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) 25 | data = open("out/res/raw/iv.png", "rb").read() 26 | open("iv.png", "wb").write(c.decrypt(data)) 27 | 28 | -------------------------------------------------------------------------------- /flare-on_10/ch03/README.md: -------------------------------------------------------------------------------- 1 | ## 1. NOPE 2 | 3 | This was enough of a pain to reverse and go through, i'm not going to do it again for a writeup. 4 | 5 | It is tedious but there's a lot to learn for aspiring malware analysts/reverse engineers, I encourage you to do it (partly to share our pain). 6 | 7 | 8 | ## 2. Bruteforce like an idiot. 9 | 10 | At some point you need a crc32 to match, and i missed the whole date check stuff, so i just [bruteforced](find_key_for_crc.py) it. 11 | 12 | It uses a modified RC4, so i just emulated the function with unicorn instead of coding and testing... 13 | 14 | It spits out the character that goes in front of `pizza` 15 | 16 | ## 3. Flag 17 | 18 | ``` 19 | C:\Users\me\Desktop\f\c03>mypassion.exe "00gRRR@brUc3E/1337pr.ost/20AAAAAAAA/!pizza/AMu$E`0R.~AZe/YPXEKCZXYIGMNOXNMXPYCXGXN/ob5cUr3/fin/" 20 | RUECKWAERTSINGENIEURWESEN 21 | b0rn_t0_5truc7_b4by@flare-on.com 22 | ``` 23 | -------------------------------------------------------------------------------- /flare-on_10/ch03/dump.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch03/dump.bin -------------------------------------------------------------------------------- /flare-on_10/ch03/find_key_for_crc.py: -------------------------------------------------------------------------------- 1 | 2 | from unicorn import * 3 | from unicorn.x86_const import * 4 | from capstone import * 5 | from pwn import p64, u32 6 | import zlib 7 | import string 8 | 9 | code = b'\x48\x89\x5c\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7c\x24\x20\x41\x56\x48\x81\xec\x00\x01\x00\x00\x45\x33\xdb\x48\x8d\x14\x24\x45\x8b\xd3\x41\x8b\xc3\x49\x8b\xd9\x4d\x8b\xf0\x48\x8b\xf1\x66\x90\x88\x02\x48\x8d\x52\x01\xff\xc0\x3d\x00\x01\x00\x00\x7c\xf1\x45\x8b\xc3\x4c\x8d\x0c\x24\x66\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x41\x0f\xb6\x39\xb8\x89\x88\x88\x88\x41\xf7\xe0\x41\x8b\xc8\xc1\xea\x03\x6b\xc2\x0f\x2b\xc8\x48\x63\xc1\x0f\xbe\x0c\x30\x44\x03\xd1\x44\x03\xd7\x41\x81\xe2\xff\x00\x00\x80\x7d\x0d\x41\xff\xca\x41\x81\xca\x00\xff\xff\xff\x41\xff\xc2\x49\x63\xc2\x48\x8d\x0c\x24\x48\x03\xc8\x41\xff\xc0\x0f\xb6\x01\x41\x88\x01\x49\xff\xc1\x40\x88\x39\x41\x81\xf8\x00\x01\x00\x00\x7c\xa4\x45\x8b\xc3\x4c\x8d\x0c\x24\x0f\x1f\x40\x00\x66\x0f\x1f\x84\x00\x00\x00\x00\x00\x41\x0f\xb6\x39\xb8\x89\x88\x88\x88\x41\xf7\xe0\x41\x8b\xc8\xc1\xea\x03\x6b\xc2\x0f\x2b\xc8\x48\x63\xc1\x0f\xbe\x0c\x30\x44\x03\xd1\x44\x03\xd7\x41\x81\xe2\xff\x00\x00\x80\x7d\x0d\x41\xff\xca\x41\x81\xca\x00\xff\xff\xff\x41\xff\xc2\x49\x63\xc2\x48\x8d\x0c\x24\x48\x03\xc8\x41\xff\xc0\x0f\xb6\x01\x41\x88\x01\x49\xff\xc1\x40\x88\x39\x41\x81\xf8\x00\x01\x00\x00\x7c\xa4\x48\x8b\xb4\x24\x30\x01\x00\x00\x41\x8b\xd3\x45\x8b\xc3\x48\x85\xf6\x0f\x84\xf6\x00\x00\x00\x48\x89\xac\x24\x10\x01\x00\x00\x48\x8b\xfb\x48\x8b\xee\x4c\x2b\xf3\xff\xc2\x81\xe2\xff\x00\x00\x80\x7d\x0a\xff\xca\x81\xca\x00\xff\xff\xff\xff\xc2\x48\x63\xc2\x4c\x8d\x0c\x24\x4c\x03\xc8\x45\x0f\xb6\x11\x45\x03\xc2\x41\x81\xe0\xff\x00\x00\x80\x7d\x0d\x41\xff\xc8\x41\x81\xc8\x00\xff\xff\xff\x41\xff\xc0\x49\x63\xc0\x48\x8d\x0c\x24\x48\x03\xc8\x0f\xb6\x01\x41\x88\x01\x44\x88\x11\x41\x0f\xb6\x01\x49\x03\xc2\x0f\xb6\xc0\x0f\xb6\x0c\x04\x41\x32\x0c\x3e\x88\x0f\x48\xff\xc7\x48\x83\xed\x01\x75\x95\x48\x8b\xac\x24\x10\x01\x00\x00\x41\x8b\xd3\x66\x0f\x1f\x44\x00\x00\x41\xff\xc3\x41\x81\xe3\xff\x00\x00\x80\x7d\x0d\x41\xff\xcb\x41\x81\xcb\x00\xff\xff\xff\x41\xff\xc3\x49\x63\xc3\x4c\x8d\x0c\x24\x4c\x03\xc8\x45\x0f\xb6\x01\x41\x03\xd0\x81\xe2\xff\x00\x00\x80\x7d\x0a\xff\xca\x81\xca\x00\xff\xff\xff\xff\xc2\x48\x63\xc2\x48\x8d\x0c\x24\x48\x03\xc8\x0f\xb6\x01\x41\x88\x01\x44\x88\x01\x41\x0f\xb6\x01\x4c\x03\xc0\x41\x0f\xb6\xc0\x0f\xb6\x0c\x04\x30\x0b\x48\xff\xc3\x48\x83\xee\x01\x75\x97\x4c\x8d\x9c\x24\x00\x01\x00\x00\x33\xc0\x49\x8b\x5b\x18\x49\x8b\x73\x20\x49\x8b\x7b\x28\x49\x8b\xe3\x41\x5e\xc3' 10 | 11 | def disas_single(code, addr): 12 | md = Cs(CS_ARCH_X86, CS_MODE_64) 13 | for i in md.disasm(code, addr): 14 | return (i.address, i.mnemonic, i.op_str) 15 | 16 | def hook_code(mu, addr, size, user_data): 17 | mem = mu.mem_read(addr, size) 18 | dis = disas_single(mem, addr) 19 | addr, mnemonic, op_str = dis 20 | print(" >> 0x%x\t%s\t%s"%(addr, mnemonic, op_str)) 21 | if mnemonic == "ret": 22 | mu.emu_stop() 23 | 24 | 25 | def get_data(c): 26 | data = bytearray(open("dump.bin", "rb").read()) 27 | for x in range(len(data)): 28 | data[x] = (data[x] - c) & 0xff 29 | return bytes(data) 30 | 31 | def decrypt(data): 32 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 33 | 34 | MEM_TEXT = 0x100000 35 | MEM_STACK = 0x700000 36 | MEM_HEAP = 0xa00000 37 | 38 | addr_key = MEM_HEAP 39 | addr_file = MEM_HEAP+0x100 40 | 41 | mu.mem_map(MEM_TEXT, 0x1000) 42 | mu.mem_map(MEM_STACK, 0x100000) 43 | mu.mem_map(MEM_HEAP, 0x100000) 44 | 45 | mu.mem_write(MEM_TEXT, code) 46 | mu.mem_write(addr_key, b'REVERSEENGINEER') 47 | mu.mem_write(addr_file, data) 48 | 49 | rsp = MEM_STACK + 0x1000 50 | rbp = MEM_STACK + 0x2000 51 | 52 | mu.reg_write(UC_X86_REG_RSP, rsp) 53 | mu.reg_write(UC_X86_REG_RBP, rbp) 54 | 55 | # args 56 | mu.reg_write(UC_X86_REG_RCX, addr_key) 57 | mu.reg_write(UC_X86_REG_RDX, 0x6f) 58 | mu.reg_write(UC_X86_REG_R8, addr_file) 59 | mu.reg_write(UC_X86_REG_R9, addr_file) 60 | mu.mem_write(rsp + 0x28, p64(0x2a837)) 61 | 62 | #mu.hook_add(UC_HOOK_CODE, hook_code) 63 | 64 | mu.emu_start(MEM_TEXT, MEM_TEXT+len(code) - 1) 65 | 66 | dec = mu.mem_read(addr_file, 0x2a837) 67 | return dec 68 | 69 | 70 | expected_crc = u32(bytes.fromhex("88 A8 A7 92")) 71 | for c in string.printable: 72 | dec = decrypt(get_data(ord(c))) 73 | crc = zlib.crc32(dec) 74 | if crc == expected_crc: 75 | print("found: %s"%c) 76 | break 77 | -------------------------------------------------------------------------------- /flare-on_10/ch04/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Sauerbraten 2 | 3 | This is an aimbot (and info stealer) for a game called sauerbraten. 4 | 5 | My initial google search returned 6 | 7 | ![sauerbraten](pics/sauerbraten.png) 8 | 9 | So i assumed this was just a made up thing by the german FLARE team, and did most of it without the actual game, up until the last stage where i was kind of stuck. I refined my search and found 10 | [this](http://sauerbraten.org/) FPS game. 11 | 12 | Meanwhile i had to hunt for config files of softwares i dont own, sometimes even install them and bloat my VM. I love snapshots... 13 | 14 | ## 2. Matrioshka 15 | 16 | Basically each shellcode will look for a specific software (steam, discord, etc...) config or file of interest, stash them somewhere safe for exfil and use part of the said files to decrypt the next stage shellcode. 17 | Each shellcode uses the same structure, so once you've reversed one, you've basically reversed them all. 18 | 19 | ## 3. Morality 20 | 21 | Practice your google search and don't assume you're getting trolled 24/7 by german gentlemen :) (even though you are). 22 | -------------------------------------------------------------------------------- /flare-on_10/ch04/pics/sauerbraten.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch04/pics/sauerbraten.png -------------------------------------------------------------------------------- /flare-on_10/ch04/stage2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch04/stage2.bin -------------------------------------------------------------------------------- /flare-on_10/ch04/stage3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch04/stage3.bin -------------------------------------------------------------------------------- /flare-on_10/ch04/stage4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch04/stage4.bin -------------------------------------------------------------------------------- /flare-on_10/ch04/stage5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch04/stage5.bin -------------------------------------------------------------------------------- /flare-on_10/ch04/stage6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch04/stage6.bin -------------------------------------------------------------------------------- /flare-on_10/ch04/stg_2_rc4.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import ARC4 2 | 3 | RC4_DATA_SIZE = 0x3b19 4 | RC4_DATA_START = 0x953 5 | 6 | RC4_KEY = b'"InstallConfigSt' 7 | print(RC4_KEY) 8 | 9 | with open("stage2.bin", "rb") as fp: 10 | fp.seek(RC4_DATA_START) 11 | data = fp.read(RC4_DATA_SIZE) 12 | 13 | assert len(data) == RC4_DATA_SIZE 14 | 15 | c = ARC4.new(key=RC4_KEY) 16 | with open("stage3.bin", "wb") as fp: 17 | fp.write(c.decrypt(data)) 18 | -------------------------------------------------------------------------------- /flare-on_10/ch04/stg_3_rc4.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import ARC4 2 | 3 | RC4_DATA_SIZE = 0x2ba8 4 | RC4_DATA_START = 0xf30 5 | 6 | #RC4_KEY = b'"InstallConfigSt' 7 | RC4_KEY = b'SQLite format 3\x00' 8 | print(RC4_KEY) 9 | 10 | with open("stage3.bin", "rb") as fp: 11 | fp.seek(RC4_DATA_START) 12 | data = fp.read(RC4_DATA_SIZE) 13 | 14 | assert len(data) == RC4_DATA_SIZE 15 | 16 | c = ARC4.new(key=RC4_KEY) 17 | #print(c.decrypt(data)) 18 | with open("stage4.bin", "wb") as fp: 19 | fp.write(c.decrypt(data)) 20 | -------------------------------------------------------------------------------- /flare-on_10/ch04/stg_4_rc4.py: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import ARC4 2 | 3 | RC4_DATA_SIZE = 0x1bcc 4 | RC4_DATA_START = 0xfdb 5 | 6 | RC4_KEY = b'recentWalletFiles' 7 | 8 | print(RC4_KEY) 9 | 10 | with open("stage4.bin", "rb") as fp: 11 | fp.seek(RC4_DATA_START) 12 | data = fp.read(RC4_DATA_SIZE) 13 | 14 | assert len(data) == RC4_DATA_SIZE 15 | 16 | penis = open("sparrow.conf", "rb").read() 17 | 18 | c = ARC4.new(key=RC4_KEY) 19 | #print(c.decrypt(data)) 20 | with open("stage5.bin", "wb") as fp: 21 | fp.write(c.decrypt(data)) 22 | -------------------------------------------------------------------------------- /flare-on_10/ch04/stg_5_xor.py: -------------------------------------------------------------------------------- 1 | 2 | from pwn import xor 3 | 4 | RC4_DATA_SIZE = 0xda8 5 | RC4_DATA_START = 0xd75 6 | 7 | # known plaintext // 'the blob decrypted....' 8 | XOR_KEY = 'pV4\x12' 9 | 10 | with open("stage5.bin", "rb") as fp: 11 | fp.seek(RC4_DATA_START) 12 | data = fp.read(RC4_DATA_SIZE) 13 | 14 | assert len(data) == RC4_DATA_SIZE 15 | 16 | #print(c.decrypt(data)) 17 | with open("stage6.bin", "wb") as fp: 18 | fp.write(xor(data, XOR_KEY)) 19 | -------------------------------------------------------------------------------- /flare-on_10/ch04/stg_6_get_flag.py: -------------------------------------------------------------------------------- 1 | 2 | from pwn import xor, p32 3 | import string 4 | import zlib 5 | import sys 6 | 7 | found = "computer_ass1sted_ctf" 8 | allow = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$&*+,-./:;?_' 9 | 10 | for a in string.printable: 11 | for b in string.printable: 12 | for c in string.printable: 13 | for d in string.printable: 14 | s = bytes(found+a+b+c+d, 'ascii') 15 | if zlib.crc32(s) == 0xa5561586: 16 | print(s + b'flare-on.com') 17 | sys.exit(0) 18 | -------------------------------------------------------------------------------- /flare-on_10/ch05/README.md: -------------------------------------------------------------------------------- 1 | It's flare-on, you made it this far, you know how to dump shellcodes to disk and analyze them: there's no need to cover. 2 | 3 | But you end up with a code full of jumps, your tool creates functions everywhere, you can't make sense of anything and it drives you crazy. 4 | 5 | ## 1. It's 2023 - who needs a deobfuscator for JUMPS ? 6 | 7 | ![nonsense](pics/nonsense.png) 8 | 9 | Use the cheap (free) [dragon](https://ghidra-sre.org/) tool and load your shellcode. 10 | 11 | When asked for analysis, don't get too excited and take the time to uncheck `Shared Return Calls`: 12 | 13 | ![goodoption](pics/good_option.png) 14 | 15 | You can then enjoy readable pseudocode: 16 | 17 | ![yessense](pics/yessense.png) 18 | 19 | You can then press the `auto-reverse hotkey` to have everyting nicely renamed: 20 | 21 | ![renamed](pics/renamed.png) 22 | 23 | ## 2. Find the RC6 24 | 25 | Assuming you found the one shellcode that does RC6, follow the `decrypt` function, to 26 | 27 | ```python 28 | void decrypt(undefined4 buff,undefined4 siuze,uint key) 29 | 30 | { 31 | undefined4 heap; 32 | undefined4 *key_out; 33 | 34 | heap = (*_GetProcessHeap)(8,0x90); 35 | key_out = (undefined4 *)(*_RtlAllocateHeap)(heap); 36 | if (key_out != NULL) { 37 | key_setup(key,key_out); 38 | rc6_block(buff,siuze,key_out); 39 | heap = (*_GetProcessHeap)(8,key_out); 40 | (*_DAT_00460218)(heap); 41 | } 42 | return; 43 | } 44 | ``` 45 | 46 | in the `key_setup`, you can find the magic constants and say yayy for RC6: 47 | 48 | ```python 49 | void key_setup(uint key,undefined4 *key_out) 50 | 51 | { 52 | undefined4 uVar1; 53 | int local_30; 54 | int local_2c; 55 | uint local_20; 56 | uint local_1c; 57 | 58 | /* 0xbaadbeef -> 0xef0001ba */ 59 | key = key >> 0x18 | key >> 8 & 0x10 | (key & 1) << 8 | key << 0x18; 60 | *key_out = 0xb7e15163; 61 | local_1c = 1; 62 | while( true ) { 63 | if (0x23 < local_1c) break; 64 | key_out[local_1c] = key_out[local_1c - 1] + -0x61c88647; 65 | local_1c = local_1c + 1; 66 | } 67 | local_20 = 0; 68 | local_2c = 0; 69 | local_1c = 0; 70 | local_30 = 1; 71 | while( true ) { 72 | if (0x6c < local_30) break; 73 | uVar1 = FUN_000064e7(3); 74 | uVar1 = FUN_00002b3e(key_out[local_1c] + local_2c + local_20,uVar1); 75 | key_out[local_1c] = uVar1; 76 | local_2c = key_out[local_1c]; 77 | uVar1 = FUN_000064e7(local_2c + local_20); 78 | key = FUN_00002b3e(key + local_2c + local_20,uVar1); 79 | local_1c = (local_1c + 1) % 0x24; 80 | local_30 = local_30 + 1; 81 | local_20 = key; 82 | } 83 | return; 84 | } 85 | ``` 86 | 87 | 88 | there's also a slight modification of the key which transforms `BAADBEEF` to `EF0001BA`: 89 | ```python 90 | key = key >> 0x18 | key >> 8 & 0x10 | (key & 1) << 8 | key << 0x18; 91 | ``` 92 | 93 | 94 | The RC6 uses `0x10` rounds as evidences suggest in there: 95 | 96 | ```python 97 | void rc6_block(int param_1,uint param_2,int *param_3) 98 | 99 | { 100 | int iVar1; 101 | uint uVar2; 102 | undefined4 uVar3; 103 | uint uVar4; 104 | uint *puVar5; 105 | int round; 106 | uint local_c; 107 | 108 | local_c = 0; 109 | while( true ) { 110 | if (param_2 <= local_c) break; 111 | puVar5 = (uint *)(param_1 + local_c); 112 | puVar5[1] = puVar5[1] + *param_3; 113 | puVar5[3] = puVar5[3] + param_3[1]; 114 | round = 1; 115 | while( true ) { 116 | /* 0x10 rounds */ 117 | if (0x10 < round) break; 118 | uVar3 = FUN_000064e7(5); 119 | uVar4 = FUN_00002b3e((puVar5[1] * 2 + 1) * puVar5[1],uVar3); 120 | uVar3 = FUN_000064e7(5,uVar4); 121 | uVar2 = FUN_00002b3e((puVar5[3] * 2 + 1) * puVar5[3],uVar3); 122 | uVar3 = FUN_000064e7(uVar2); 123 | iVar1 = FUN_00002b3e(*puVar5 ^ uVar4,uVar3); 124 | *puVar5 = iVar1 + param_3[round * 2]; 125 | uVar3 = FUN_000064e7(uVar4); 126 | iVar1 = FUN_00002b3e(puVar5[2] ^ uVar2,uVar3); 127 | puVar5[2] = iVar1 + param_3[round * 2 + 1]; 128 | uVar2 = *puVar5; 129 | *puVar5 = puVar5[1]; 130 | puVar5[1] = puVar5[2]; 131 | puVar5[2] = puVar5[3]; 132 | puVar5[3] = uVar2; 133 | round = round + 1; 134 | } 135 | *puVar5 = *puVar5 + param_3[0x22]; 136 | puVar5[2] = puVar5[2] + param_3[0x23]; 137 | local_c = local_c + 0x10; 138 | } 139 | return; 140 | } 141 | ``` 142 | 143 | ## 3. Going in for the flag 144 | 145 | Now is the time of endless wandering looking for data to decrypt. 146 | 147 | Somewhere along the path you dumped the thing injected into explorer.exe. 148 | 149 | In there lies a blob of high entropy data data stuck between 2 BAADBEEF markers, you find it and reuse the previously acquired key, (in my case, badly) using the awesome [Binary Refinery](https://github.com/binref/). 150 | 151 | ``` 152 | % python -c 'import sys; sys.stdout.buffer.write(open("explorer_injected2.bin", "rb").read().split(b"\xef\xbe\xad\xba")[1])' | rc6 le:e:0xef0001ba -Rr -k 0x10 153 | ``` 154 | -------------------------------------------------------------------------------- /flare-on_10/ch05/pics/good_option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch05/pics/good_option.png -------------------------------------------------------------------------------- /flare-on_10/ch05/pics/nonsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch05/pics/nonsense.png -------------------------------------------------------------------------------- /flare-on_10/ch05/pics/renamed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch05/pics/renamed.png -------------------------------------------------------------------------------- /flare-on_10/ch05/pics/yessense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch05/pics/yessense.png -------------------------------------------------------------------------------- /flare-on_10/ch06/README.md: -------------------------------------------------------------------------------- 1 | This was one of my favorite challenge this year 2 | 3 | ## 1. WIN32 4 | 5 | Upon loading you face something that looks like a malformed PE. 6 | 7 | After giving it a bit of love, the truth reveals itself. 8 | 9 | It calculates the hash of some 16 bytes memory region and if it is equal to `0x31d9f5ff` it will do some crypto magic, decrypt some data and display a popup. 10 | BUT the key is zero'd, so we need to find it. 11 | 12 | 13 | 14 | ```python 15 | 16 | undefined8 entry(undefined8 param_1,undefined2 param_2) 17 | 18 | { 19 | byte *input_key; 20 | undefined8 data_size; 21 | byte *enc_data; 22 | longlong crypto_data; 23 | longlong winning; 24 | uint hash; 25 | longlong lVar1; 26 | short local_48 [8]; 27 | short local_38 [8]; 28 | short *local_28; 29 | short *local_20; 30 | undefined8 local_18; 31 | undefined8 local_10; 32 | byte *ptr_input_key; 33 | 34 | input_key = (byte *)get_data_block(KEY); 35 | hash = 0; 36 | lVar1 = 4; 37 | ptr_input_key = input_key + 2; 38 | do { 39 | hash = hash ^ (hash << 7 | hash >> 0x19) + (uint)ptr_input_key[-2]; 40 | hash = hash ^ (hash << 7 | hash >> 0x19) + (uint)ptr_input_key[-1]; 41 | hash = hash ^ (hash << 7 | hash >> 0x19) + (uint)*ptr_input_key; 42 | hash = hash ^ (hash << 7 | hash >> 0x19) + (uint)ptr_input_key[1]; 43 | lVar1 = lVar1 + -1; 44 | ptr_input_key = ptr_input_key + 4; 45 | } while (lVar1 != 0); 46 | if (hash == 0x31d9f5ff) { 47 | data_size = get_enc_data_size(); 48 | enc_data = (byte *)get_data_block(ENC_DATA); 49 | decrypt((uint *)input_key,enc_data,(uint)data_size); 50 | crypto_data = get_data_block(ENC_DATA); 51 | winning = get_data_block(Winning); 52 | FUN_00409600(local_38,winning); 53 | FUN_00409600(local_48,crypto_data); 54 | local_28 = local_48; 55 | local_18 = 0x30; 56 | local_20 = local_38; 57 | local_10 = 0xffffffff; 58 | FUN_00409993(0x50000018,4); 59 | } 60 | return 0; 61 | } 62 | ``` 63 | 64 | After playing with Z3 to generate valid inputs, noticing there's just too many of them, trying some random constrains and deciding it was too random, i got so only sane possible idea and dwelve back into the binary. 65 | 66 | ## 2. DOSBOX and fancy music 67 | 68 | The massive DOS stub of the binary hosts an entirely different program. 69 | 70 | If you run the binary in DOSBOX, you get a fancy Simon Says game promising endless hours of nostalgic fun. 71 | 72 | After an exciting reversing session, it appears that if you win this game, it will re-write itself, filling the 16 zero'd bytes we found earlier. 73 | 74 | Also in order to get a deterministic value, you have to enter the konami cheat code. 75 | 76 | 77 | ## 3. TL;DR 78 | 79 | - [patch](patch.py) bin so it autoplays (i have a bad memory, i can't beat this game :p) 80 | - run with dosbox 81 | - input konami code to set the seed 82 | - let it play (speed up the emulator or go grab a coffee) 83 | - run under windows to get flag 84 | 85 | everything was perfect about this challenge 86 | -------------------------------------------------------------------------------- /flare-on_10/ch06/patch.py: -------------------------------------------------------------------------------- 1 | data = bytearray(open("FlareSay.exe.ori", "rb").read()) 2 | 3 | # mov ah, dl; nop 4 | data[0x674] = 0x88 5 | data[0x675] = 0xd4 6 | data[0x676] = 0xd0 7 | 8 | # mov dh, dl; nop 9 | data[0x677] = 0x88 10 | data[0x678] = 0xd6 11 | data[0x679] = 0x90 12 | 13 | #data[0x9eb] = 2 14 | open("flaresay.exe", "wb").write(data) 15 | -------------------------------------------------------------------------------- /flare-on_10/ch07/README.md: -------------------------------------------------------------------------------- 1 | ## 1. The Python Challenge 2 | 3 | it's a snake game, it's compiled with [Nuikta](https://nuitka.net/). 4 | 5 | ![cheese](pics/cheese.jpeg) 6 | 7 | 8 | ## 2. Feed the snake with cheese 9 | 10 | - Grab the [PyInjector](https://github.com/call-042PE/PyInjector) pre-built DLL 11 | - write `print(dir())` into `code.py` 12 | - inject with ProcessHacker into the `flake.exe` 13 | - notice the `game_win` function 14 | - write `game_win()` into `code.py` 15 | - inject with ProcessHacker into the `flake.exe` 16 | - shameless (?), low effort flag 17 | 18 | -------------------------------------------------------------------------------- /flare-on_10/ch07/pics/cheese.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch07/pics/cheese.jpeg -------------------------------------------------------------------------------- /flare-on_10/ch08/README.md: -------------------------------------------------------------------------------- 1 | A fun challenge that could have been slightly harder 2 | 3 | ## 1. Oh Noes... RUST. 4 | 5 | We get a pcap and rust binary, obviously the answer lies within the pcap. 6 | 7 | The binary embeds two full blown PE, xorred with an obvious key (especially knowing the [author](https://twitter.com/cPeterr)): 8 | 9 | ``` 10 | 000ad1b0 40 63 50 65 74 65 72 72 40 63 50 65 74 65 72 72 |@cPeterr@cPeterr| 11 | 000ad200 40 63 50 65 74 65 72 72 40 63 50 65 74 65 71 72 |@cPeterr@cPeteqr| 12 | 000ad220 50 63 50 65 2c 65 72 f2 40 63 50 65 74 65 72 72 |PcPe,er.@cPeterr| 13 | 000ad230 40 63 50 65 74 65 73 72 41 63 50 65 04 65 72 f2 |@cPetesrAcPe.er.| 14 | 000ad240 40 63 50 65 74 65 72 72 40 63 50 65 74 65 73 72 |@cPeterr@cPetesr| 15 | 000ad250 41 63 50 65 fc 65 72 f2 40 63 50 65 74 65 72 72 |AcPe.er.@cPeterr| 16 | 000ad260 40 63 50 65 74 65 73 72 41 63 50 65 d4 65 72 f2 |@cPetesrAcPe.er.| 17 | 000ad270 40 63 50 65 74 65 72 72 40 63 50 65 74 65 73 72 |@cPeterr@cPetesr| 18 | ``` 19 | 20 | It will inject them into `svchost.exe`, but we just extract and manually run them because we're wild! 21 | 22 | ## 2. It will ruin your VM 23 | 24 | The first binary will infect every executable in `%APPDATA%` (iirc), it will most likely ruin you VM but it is definitly worth the try ! 25 | 26 | ![sus](pics/sus.png) 27 | 28 | ## 3. Abuse the implant! 29 | 30 | The second binary will run some sort of bindshell and it can be abused to decrypt the transfered files by [replaying](comm.py) the content of the pcap. 31 | 32 | The flag is in the wallpaper. 33 | -------------------------------------------------------------------------------- /flare-on_10/ch08/comm.py: -------------------------------------------------------------------------------- 1 | 2 | from pwn import * 3 | #context.log_level = 'debug' 4 | 5 | 6 | key1 = bytes.fromhex("6574212c9b4d9334d893bec2477cb86a70983b3c33952d68a8cc5c0226070abf") 7 | key2 = bytes.fromhex("0e02f4a9a8b5beeaba8348d6d2f87c606849df9a5eef49a65c98cf07d4c238a6") 8 | 9 | shell = open('powershell.enc', 'rb').read() 10 | wall = open('enc_img.bin', 'rb').read() 11 | 12 | 13 | io = remote("192.168.122.111", 8345) 14 | io.send(key1) 15 | print(io.recv(1024)) 16 | io.send(key2) 17 | print(io.recv(1024)) 18 | io.send(b'exec whoami\r') 19 | print(io.recv(1024)) 20 | io.send(b'upload C:\\Users\\me\\Desktop\\f\\lol\\wallpaper.ps1 708\r') 21 | print(io.recv(1024)) 22 | print(io.send(shell)) 23 | print(io.recv(1024)) 24 | 25 | io.send(b'upload C:\\Users\\me\\Desktop\\f\\lol\\desktop.png 122218\r') 26 | print(io.recv(1024)) 27 | print(io.send(wall)) 28 | print(io.recv(1024)) 29 | io.close() 30 | -------------------------------------------------------------------------------- /flare-on_10/ch08/enc_img.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch08/enc_img.bin -------------------------------------------------------------------------------- /flare-on_10/ch08/pics/sus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch08/pics/sus.png -------------------------------------------------------------------------------- /flare-on_10/ch08/powershell.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch08/powershell.enc -------------------------------------------------------------------------------- /flare-on_10/ch09/0x1000.dump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch09/0x1000.dump -------------------------------------------------------------------------------- /flare-on_10/ch09/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Extract MBR 2 | 3 | ``` 4 | dd if=hda.img of=mbr.bin bs=512 count=1 5 | ``` 6 | 7 | load in ghidra as `x86:LE:16:Real Mode` with base address `0x7c00` 8 | reverse, see it does stuff, decrypts more code and jump at 0x1000 9 | 10 | 11 | ## 1. Dump stuff 12 | 13 | ``` 14 | % qemu-system-i386 -drive file=hda.img,format=raw -s -S 15 | % gef 16 | > set architecture i8086 17 | > target remote localhost:1234 18 | > break *0x1000 19 | > dump memory 0x1000.bin 0x1000 0x8c00 20 | ``` 21 | 22 | gdb craps itself a bit at disassembling 16bit code but it's ok, we can survive. 23 | 24 | ## 2. Reverse more 25 | 26 | load the newly acquired dump in your favorite tool and get the first part of the of decryption key by xoring the victim id 27 | 28 | ``` 29 | >>> xor(bytes.fromhex("3487B3B41F20"), 0x55).hex() 30 | '61d2e6e14a75' 31 | ``` 32 | 33 | ## 3. Bruteforce much 34 | 35 | The function at `0x1296` (`check_key`) returns 0 if the key is correct (otherwise a pointer to an error string). 36 | 37 | ![func](pics/func.png) 38 | 39 | We can use this to our advantage and write a quick unicorn [harness](harness.py) to bruteforce the last 2 bytes of the key. 40 | 41 | The script runs a couple minutes and returns `b'\x04\n\r\x0c'` which means the last part of the key is `4ADC` and the full key `61D2E6E14A754ADC` 42 | 43 | Use the key, watch the disk being decrypted, reboot and get the flag. Easy peasy Moneypenny. 44 | -------------------------------------------------------------------------------- /flare-on_10/ch09/harness.py: -------------------------------------------------------------------------------- 1 | from unicorn import * 2 | from unicorn.x86_const import * 3 | from capstone import * 4 | import sys 5 | 6 | 7 | 8 | # victim id = 3487B3B41F20 9 | # input key addr: 0x2a4c 10 | # 1/ first check if xor ^ 0x5555 11 | # 2/ key = 61d2e6e14a75 12 | # 3/ bf last 2 chrs 13 | 14 | 15 | # gef➤ x/16bx 0x2a4c 16 | # 0x2a4c: 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x01 0x02 17 | # 0x2a54: 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x00 18 | 19 | 20 | def trykey(key): 21 | base_key = bytes.fromhex("06010d020e060e01040a0705") 22 | key = base_key + key 23 | 24 | code = open("0x1000.dump", "rb").read() 25 | 26 | mu = Uc(UC_ARCH_X86, UC_MODE_16) 27 | mu.mem_map(0x1000, 0x8000) 28 | mu.mem_map(0xf000, 0x1000) 29 | 30 | mu.mem_write(0x1000, code) 31 | mu.mem_write(0x2a4c, key) 32 | 33 | 34 | # dumped cpu context with gdb and blindly 35 | # copied most register values 36 | mu.reg_write(UC_X86_REG_SP, 0xfff4) 37 | mu.reg_write(UC_X86_REG_BP, 0x1) 38 | mu.reg_write(UC_X86_REG_ES, 0x0) 39 | 40 | mu.reg_write(UC_X86_REG_AX, 0x1c0d) 41 | mu.reg_write(UC_X86_REG_CX, 0x1) 42 | mu.reg_write(UC_X86_REG_DX, 0x1224) 43 | mu.reg_write(UC_X86_REG_SI, 0x0) 44 | mu.reg_write(UC_X86_REG_DI, 0x2a5c) 45 | mu.reg_write(UC_X86_REG_CR0, 0x10) 46 | 47 | # emulate from function start to right before the ret instruction 48 | mu.emu_start(0x1296, 0x130b) 49 | 50 | # grab return value from EAX 51 | return mu.reg_read(UC_X86_REG_AX) 52 | 53 | 54 | 55 | i = 0 56 | for a in range(0x10): 57 | for b in range(0x10): 58 | for c in range(0x10): 59 | for d in range(0x10): 60 | 61 | kk = bytes([a, b, c, d]) 62 | 63 | if not i % 1000: 64 | print(i) 65 | i += 1 66 | res = trykey(kk) 67 | if res == 0: 68 | print("found!!!") 69 | print(kk) 70 | sys.exit(0) 71 | 72 | -------------------------------------------------------------------------------- /flare-on_10/ch09/pics/func.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch09/pics/func.png -------------------------------------------------------------------------------- /flare-on_10/ch10/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Back to the future 2 | 3 | The real challenge here was that i was mostly reversing in sweat pants, crashing on my sofa and the PDP11 emulator liked to max out one my CPU core, having the side effect of draining my battery and having me regularly run for power. That helped reaching the 10000 steps a days fitness goal. 4 | 5 | Anyway, I fiddled around 5 mins to attach the tape, then getting to run the binary was pretty straightfoward, as i used to type the same during my solaris 2.6 days: 6 | 7 | ``` 8 | # stty erase ^H 9 | # cat /dev/rmt12 > readme.txt 10 | # cat /dev/rmt12 > hell.Z 11 | # file hell.Z 12 | hell.Z: block compressed 12 bit code data 13 | # uncompress hell 14 | # chmod +x ./hell 15 | # ./hell 16 | MoogleForth starting. Stack: 3802 17 | ``` 18 | 19 | I took the first random hexdump.c from github and compiled it inside the guest so i could exfil the binary. 20 | 21 | You can load it in any PDP11 capable disassembler. If it doesnt understand the oldschool `a.out` format, just skip the first 0x10 bytes. 22 | 23 | The symbols and their addresses can be acquired with the `nm` command if you dont want to write a proper loader. 24 | 25 | 26 | ## 2. Ancient DeBugger (ADB) 27 | 28 | I did everying using `adb`, not the android debugger, but some gdb ancestor. 29 | 30 | Every 5 minutes spent in there make you spend 1 minute of silence out of respect for the people who actually had to work with that. We're so spoiled with modern tooling :) 31 | 32 | The source code lies somewhere on github and is a [piece of art](https://github.com/RetroBSD/2.11BSD/blob/master/usr/bin/adb/command.c). 33 | 34 | Anyway, it has everything you could hope for: 35 | 36 | - It can disassemble 34 instructions starting from the `decrypt` symbol: 37 | 38 | ``` 39 | adb> decrypt,34?ai 40 | decrypt: mov r2,-(sp) 41 | decrypt+02: mov r3,-(sp) 42 | decrypt+04: mov r4,-(sp) 43 | decrypt+06: mov r5,-(sp) 44 | decrypt+010: jsr r4,bl 45 | decrypt+014: swab -(r2) 46 | decrypt+016: mov (sp)+,r4 47 | decrypt+020: jsr r4,parse 48 | decrypt+024: swab *-(r2) 49 | decrypt+026: mov (sp)+,r4 50 | decrypt+030: mov (r5)+,r3 51 | decrypt+032: mov (r5)+,r1 52 | decrypt+034: mov (r5),r2 53 | decrypt+036: mov 02(r5),r0 54 | decrypt+042: mov r3,-(sp) 55 | decrypt+044: cmp r2,r3 56 | decrypt+046: bgt decrypt+054 57 | decrypt+050: mov r2,r3 58 | decrypt+052: beq decrypt+076 59 | decrypt+054: sub r3,r2 60 | decrypt+056: movb (r0),r4 61 | decrypt+060: movb (r1)+,r5 62 | decrypt+062: xor r4,r5 63 | decrypt+064: movb r5,(r0)+ 64 | decrypt+066: sob r3,decrypt+056 65 | decrypt+070: mov (sp),r3 66 | decrypt+072: sub r3,r1 67 | decrypt+074: br decrypt+044 68 | decrypt+076: tst (sp)+ 69 | decrypt+0100: mov (sp)+,r5 70 | decrypt+0102: mov (sp)+,r4 71 | decrypt+0104: mov (sp)+,r3 72 | decrypt+0106: mov (sp)+,r2 73 | decrypt+0110: mov (r4)+,pc 74 | ``` 75 | 76 | - you can place breakpoints and run the program 77 | 78 | ``` 79 | adb> decode:b 80 | adb> :r 81 | ./hell: running 82 | MoogleForth starting. Stack: 3802 83 | decode lol 84 | breakpoint decode: jsr r4,_docol 85 | ``` 86 | 87 | - you can single step 88 | ``` 89 | adb> :s 90 | ./hell: running 91 | stopped at zero: jsr pc,_const 92 | adb> :s 93 | ./hell: running 94 | stopped at _const: mov *(sp)+,-(r5) 95 | adb> :s 96 | ./hell: running 97 | stopped at _const+02: mov (r4)+,pc 98 | adb> :s 99 | ./hell: running 100 | stopped at tor: mov (r5)+,-(sp) 101 | adb> :s 102 | ./hell: running 103 | stopped at tor+02: mov (r4)+,pc 104 | adb> :s 105 | ./hell: running 106 | stopped at dup: mov (r5),-(r5) 107 | adb> :s 108 | ./hell: running 109 | stopped at dup+02: mov (r4)+,pc 110 | adb> :s 111 | ./hell: running 112 | stopped at if: mov (r4)+,r0 113 | adb> :s 114 | ./hell: running 115 | stopped at if+02: tst (r5)+ 116 | adb> :s 117 | ./hell: running 118 | stopped at if+04: bne if+010 119 | adb> :s 120 | ./hell: running 121 | stopped at if+010: mov (r4)+,pc 122 | adb> :s 123 | ./hell: running 124 | stopped at swap: mov (r5)+,r0 125 | adb> :s 126 | ./hell: running 127 | stopped at swap+02: mov (r5),-(r5) 128 | adb> :s 129 | ./hell: running 130 | stopped at swap+04: mov r0,02(r5) 131 | ``` 132 | 133 | - dump registers: 134 | 135 | ``` 136 | adb> $r 137 | ps 0170000 138 | pc 01376 swap+04 139 | sp 0177636 140 | r5 07332 _data_s0 141 | r4 0246 142 | r3 06634 143 | r2 06427 144 | r1 06 145 | r0 062544 146 | swap+04: mov r0,02(r5) 147 | ``` 148 | 149 | - and also dump memory: 150 | 151 | ``` 152 | adb> 07332,20?o 153 | 07332: 067543 067543 062544 066040 066157 040 0 0 154 | 0 0 0 0 0 0 0 0 155 | 0 0 0 0 156 | ``` 157 | 158 | Note that adb is part of a weird cult that favours octal over anything else. 159 | 160 | 161 | ## 3. Strategy (like i have one) 162 | 163 | Even though the forth interpreter binary is kind of a piece of art, i took the dynamic route. 164 | 165 | There's 3 interesting words: `decrypt`, `decode` and `secret` 166 | - `secret` will load the symbol `_secret` (which is our secret, prefixed by its size). 167 | - `decrypt` is purely written in assembly and is just xoring two buffers together 168 | - `decode` is word made of other forth words. 169 | 170 | i singled stepped this last one, taking note of each symbol change (as they match the forth words) and watching the forth stack (which is located +/- at an address pointed by the r5 register). 171 | 172 | and this gets us the flag: 173 | ```python 174 | from pwn import xor 175 | 176 | def decode(meh): 177 | acc = 0 178 | meh = bytearray(meh) 179 | for n, c in enumerate(meh): 180 | acc += c 181 | meh[n] = acc % 128 182 | return bytes(meh) 183 | 184 | def decrypt(meh): 185 | return xor(meh, b'p/q2-q4!') # with Ken Thompson's password 186 | 187 | # skip the first 2 bytes (the length) 188 | secret = bytes.fromhex("2E 00 1B D5 78 C3 2F 7C C2 DA 75 2E 78 32 D6 7B D8 23 7D D9 8A 31 3D 86 CC 2C 81 2D 7C C4 D6 74 3F 27 82 F6 57 34 D8 60 C7 E9 32 D0 B1 07 21 8F 5A 0F")[2:] 189 | 190 | print(decode(decrypt(secret))) 191 | ``` 192 | 193 | -------------------------------------------------------------------------------- /flare-on_10/ch11/README.md: -------------------------------------------------------------------------------- 1 | ## 1. The Crypto Challenge 2 | 3 | The reversing is pretty straightforward: you get two threads. 4 | - 1 thread looks for files with a certain extension 5 | - 1 thread encrypts said files 6 | 7 | OpenSSL is statically compiled but there's load of debug strings with filenames and line numbers. 8 | 9 | A random key is generated and used to encrypt the file using a sort of ChaCha20. 10 | 11 | The 256 bytes cipher context is then encrypted using textbook RSA with the following public key and then append to the encrypted file: 12 | 13 | ``` 14 | -----BEGIN PUBLIC KEY----- 15 | MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAycMwco9oCHr8YKEz5Jud 16 | PeSfD/mZXF4S5cZcEYl7xxjj5NJy1aWM5GN1WyxjRn8NCfk8Mctn/jGICa9/yLLI 17 | xyGrVHzk22Pb3/9dmwbIV5n97mkPkMR5xtC546P2blXWMCnOWgLvhMaq3F4iQWgw 18 | JMxl11ZCr+C6vnbymmd86xWb5IuzJl69K9UZoq9+A2zC5kAcN1VXYagcPR0opFbD 19 | i5G1WQNb/wE92gQ5BTuelvSyePcZ6Tnmd9BYvG6YAFr/IwgUpJerNLf6kCtmbRgN 20 | 6E4k6Q91PXnbC3IXrLXEb00apWvuVz8tR6Qzfd0eK5Z+3HA4/usJDex0ktlNlom7 21 | YQIBAw== 22 | -----END PUBLIC KEY----- 23 | ``` 24 | 25 | which has the following properties: 26 | 27 | ``` 28 | % openssl rsa -pubin -in pubkey.pem -text -noout 29 | Public-Key: (2048 bit) 30 | Modulus: 31 | 00:c9:c3:30:72:8f:68:08:7a:fc:60:a1:33:e4:9b: 32 | 9d:3d:e4:9f:0f:f9:99:5c:5e:12:e5:c6:5c:11:89: 33 | 7b:c7:18:e3:e4:d2:72:d5:a5:8c:e4:63:75:5b:2c: 34 | 63:46:7f:0d:09:f9:3c:31:cb:67:fe:31:88:09:af: 35 | 7f:c8:b2:c8:c7:21:ab:54:7c:e4:db:63:db:df:ff: 36 | 5d:9b:06:c8:57:99:fd:ee:69:0f:90:c4:79:c6:d0: 37 | b9:e3:a3:f6:6e:55:d6:30:29:ce:5a:02:ef:84:c6: 38 | aa:dc:5e:22:41:68:30:24:cc:65:d7:56:42:af:e0: 39 | ba:be:76:f2:9a:67:7c:eb:15:9b:e4:8b:b3:26:5e: 40 | bd:2b:d5:19:a2:af:7e:03:6c:c2:e6:40:1c:37:55: 41 | 57:61:a8:1c:3d:1d:28:a4:56:c3:8b:91:b5:59:03: 42 | 5b:ff:01:3d:da:04:39:05:3b:9e:96:f4:b2:78:f7: 43 | 19:e9:39:e6:77:d0:58:bc:6e:98:00:5a:ff:23:08: 44 | 14:a4:97:ab:34:b7:fa:90:2b:66:6d:18:0d:e8:4e: 45 | 24:e9:0f:75:3d:79:db:0b:72:17:ac:b5:c4:6f:4d: 46 | 1a:a5:6b:ee:57:3f:2d:47:a4:33:7d:dd:1e:2b:96: 47 | 7e:dc:70:38:fe:eb:09:0d:ec:74:92:d9:4d:96:89: 48 | bb:61 49 | Exponent: 3 (0x3) 50 | ``` 51 | 52 | Few things ring a bell here: 53 | - the low exponent (3) 54 | - the fact that RSA encrypted message always starts by a bunch of zeros (no proper padding) 55 | - the last 16 bytes of that message are always `expand 32-byte k` 56 | 57 | Googling leads to Coppersmith and few CTF writeups like: 58 | - https://medium.com/@hva314/some-basic-rsa-challenges-in-ctf-part-2-applying-theoretical-attack-55a2cc7baa11 59 | - https://hgarrereyn.gitbooks.io/th3g3ntl3man-ctf-writeups/content/2017/UIUCTF/problems/Cryptography/papaRSA/ 60 | 61 | 62 | which translates to the following sagemath script: 63 | ```python 64 | # RsaCtfTool.py --dumpkey --publickey pubkey.pem 65 | n = 25470150703730072315086034936055649836295236884601534304156993296936285040601301375939610442634162257314189499275100972455566398455602026574433195970815202585090501432569441133857842325042217925159448570072586058996240505604332536419689764920477213974406475165093073579216369638057129512420088827606714396031123135244463251843168817519429473193827165432916372277360150211932008151288302906204095482949720169306181114320172114379252171541724857670073249548632622866650173757036971232388781059615489960396402755953330835572369467647829965472365925514887194394952977362957692659807638830075891677256168792219800752995169 66 | e = 3 67 | 68 | # crypted message 69 | c = 2425592482954093142911053394287864523808964564181573160646727426912420816161421295499810615636292488448086115375476578572126347389008149317940146698511301628342882097728861790163917385171608505786502099378180432350549613073164000743046053171252337966368352372410009389267473352698726296264255749133362831429534971651466910078754923485995987572417696906602747262956933918749969313809832939636800411857199483428558375468904127868025514462771636245588377871475012975670951402940280762132382274242486303138563790236596067661371781157135962527788369561955804123957047366621254000506424769282365883497834294487244664347316 70 | 71 | # known message 72 | msg = int(b'expand 32-byte k'.hex(), 16) 73 | P. = PolynomialRing(Zmod(n)) 74 | # ^16 = len(message) 75 | f = (msg + ((2^8)^16)*x)^e - c 76 | f = f.monic() 77 | f = f.small_roots(epsilon=1/20)[0] 78 | 79 | recovered = bytes.fromhex("0" + hex(f)[2:]) + b'expand 32-byte k' 80 | print(recovered) 81 | print(len(recovered)) 82 | ``` 83 | 84 | This is all black magic to me and even though I found all the leads (my googling improved since challenge 4), I just couldn't make the maths work without the help of a beautiful numbers crushing nerd (to my defense, I was 18 when i last attended a mathematics course and that was 20y ago, so close tho...). 85 | 86 | Using a debugger, we can then patch the decrypted context into memory, let the binary decrypt the file for us and call it a day. 87 | 88 | -------------------------------------------------------------------------------- /flare-on_10/ch11/coppersmith_slv.sage: -------------------------------------------------------------------------------- 1 | 2 | # https://medium.com/@hva314/some-basic-rsa-challenges-in-ctf-part-2-applying-theoretical-attack-55a2cc7baa11 3 | # https://hgarrereyn.gitbooks.io/th3g3ntl3man-ctf-writeups/content/2017/UIUCTF/problems/Cryptography/papaRSA/ 4 | 5 | # RsaCtfTool.py --dumpkey --publickey pubkey.pem 6 | n = 25470150703730072315086034936055649836295236884601534304156993296936285040601301375939610442634162257314189499275100972455566398455602026574433195970815202585090501432569441133857842325042217925159448570072586058996240505604332536419689764920477213974406475165093073579216369638057129512420088827606714396031123135244463251843168817519429473193827165432916372277360150211932008151288302906204095482949720169306181114320172114379252171541724857670073249548632622866650173757036971232388781059615489960396402755953330835572369467647829965472365925514887194394952977362957692659807638830075891677256168792219800752995169 7 | e = 3 8 | 9 | # crypted message 10 | c = 2425592482954093142911053394287864523808964564181573160646727426912420816161421295499810615636292488448086115375476578572126347389008149317940146698511301628342882097728861790163917385171608505786502099378180432350549613073164000743046053171252337966368352372410009389267473352698726296264255749133362831429534971651466910078754923485995987572417696906602747262956933918749969313809832939636800411857199483428558375468904127868025514462771636245588377871475012975670951402940280762132382274242486303138563790236596067661371781157135962527788369561955804123957047366621254000506424769282365883497834294487244664347316 11 | 12 | # known message 13 | msg = int(b'expand 32-byte k'.hex(), 16) 14 | P. = PolynomialRing(Zmod(n)) 15 | # ^16 = len(message) 16 | f = (msg + ((2^8)^16)*x)^e - c 17 | f = f.monic() 18 | f = f.small_roots(epsilon=1/20)[0] 19 | 20 | recovered = bytes.fromhex("0" + hex(f)[2:]) + b'expand 32-byte k' 21 | print(recovered) 22 | print(len(recovered)) 23 | 24 | 25 | -------------------------------------------------------------------------------- /flare-on_10/ch12/README.md: -------------------------------------------------------------------------------- 1 | ## 1. I Wanted a VM challenge and all i got was an hypervisor challenge... 2 | 3 | ...and it was cool :) 4 | 5 | opening the binary yields some clean pseudocode (for a change) that uses the [Windows Hypervisor Platform API](https://learn.microsoft.com/en-us/virtualization/api/hypervisor-platform/hypervisor-platform). 6 | 7 | It feeds the partition some code from the PE resource and respond to IO requests (namely IN and OUT instructions) by RC4 decrypting and encrypting code relative to plus or minus RIP offset. 8 | 9 | ## 2. Debugging 10 | 11 | I wanted to get a grasp of the code in the rsrc because offset 0 wasn't making much x64 sense and didnt know where to start (author explains why in the official solution - it starts with 16 bits code). 12 | 13 | I have no windows host, so filled with some sweet candid hope, I enabled HyperV in my analysis VM, rebooted and ended up... with a bricked VM... once more, yayy for snapshots. 14 | 15 | Long story short, i have never been able to run the binary. 16 | 17 | ## 3. Static analysis 18 | 19 | I loaded the extracted resource into Binary Ninja, found some random piece of code that made sense and walked my way out. 20 | 21 | ![random](pics/random.png) 22 | 23 | Gosso modo, the `OUT` instruction means we can RC4 `decrypt` `R9` (0x1b) bytes above the current position using the `R8` as key (0x1acf57fbe20bb050). 24 | 25 | It actually tells the hypervirsor to reencrypt the executed code block, but that's too much details heee. 26 | 27 | Binary Ninja let you do RC4 from the tip of your mouse: 28 | 29 | ![rc4](pics/rc4.png) 30 | 31 | 32 | ![fixed](pics/fixed.png) 33 | 34 | Rinse and repeat, all it took was a clicky clicky session during a 20mins train ride from work to home while chatting with a friend, not the end of the world... 35 | 36 | 37 | ## 4. Going for the flag 38 | 39 | Using advanced reverse engineering skills, we can find the entry point of the VM: the function with no XREF at offset `0xbb2`, which quickly leads to: 40 | 41 | ```python 42 | undefined8 check_and_final_ret(byte *flare2023,undefined8 param_2) 43 | 44 | { 45 | int res_func1; 46 | undefined8 res_func2; 47 | 48 | res_func1 = check_param1(flare2023); 49 | res_func2 = check_param2(flare2023,param_2); 50 | if ((res_func1 == 0x24) && ((int)res_func2 == 1)) { 51 | res_func2 = 0x1337; 52 | } 53 | else { 54 | res_func2 = 0; 55 | } 56 | return res_func2; 57 | } 58 | ``` 59 | 60 | The inputs consists for 2 parts, the first part is gifted to us: 61 | 62 | ```python 63 | int check_param1(byte *param_1) 64 | 65 | { 66 | local_48 = 0x41405b283733232a; 67 | local_40 = 0x595f20202e202b46; 68 | local_38 = 0x57373d2d21334042; 69 | local_30 = 0x2a3e2c3935435b5d; 70 | local_28 = 0x6d7573705a5f5540; 71 | local_20 = 0x7370696d65726f6c; 72 | local_18 = 0; 73 | local_88 = 0x7370696d65726f6c; 74 | local_80 = 0x696d65726f6c6d75; 75 | local_78 = 0x65726f6c6d757370; 76 | local_70 = 0x6f6c6d757370696d; 77 | local_68 = 0x6d757370696d6572; 78 | local_60 = 0x7370696d65726f6c; 79 | local_58 = 0; 80 | len = strlen(param_1); 81 | match = 0; 82 | for (i = 0; i < len; i = i + 1) { 83 | if ((param_1[i] ^ *(byte *)((longlong)&local_88 + (longlong)i)) == *(byte *)((longlong)&local_48 + (longlong)i)) { 84 | match = match + 1; 85 | } 86 | } 87 | return match; 88 | } 89 | ``` 90 | 91 | 92 | just xor those 2 stack arrays and feel the power flow through your body: 93 | 94 | ``` 95 | % cat | stack.py 96 | local_48 = 0x41405b283733232a; 97 | local_40 = 0x595f20202e202b46; 98 | local_38 = 0x57373d2d21334042; 99 | local_30 = 0x2a3e2c3935435b5d; 100 | local_28 = 0x6d7573705a5f5540; 101 | local_20 = 0x7370696d65726f6c; 102 | size: 64 103 | b'*#37([@AF+ . _YB@3!-=7W][C59,>*@U_Zpsumloremips' 104 | 105 | % cat | stack.py 106 | local_88 = 0x7370696d65726f6c; 107 | local_80 = 0x696d65726f6c6d75; 108 | local_78 = 0x65726f6c6d757370; 109 | local_70 = 0x6f6c6d757370696d; 110 | local_68 = 0x6d757370696d6572; 111 | local_60 = 0x7370696d65726f6c; 112 | size: 64 113 | b'loremipsumloremipsumloremipsumloremipsumloremips' 114 | 115 | % python 116 | >>> from pwn import xor 117 | >>> xor(b'*#37([@AF+ . _YB@3!-=7W][C59,>*@U_Zpsumloremips', b'loremipsumloremipsumloremipsumloremipsumloremips') 118 | b'FLARE2023FLARE2023FLARE2023FLARE2023\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 119 | ``` 120 | 121 | 122 | 123 | The 2nd part actually requires doing some reversing, but i'm too much of an idiot for that. 124 | 125 | It first take the first input and generates some data, then performs a serie of xors. 126 | 127 | I've asked my [unicorn](test_array.py) friend to generate that data for me and then my second friend [z3](slv.py) to solve it for me. 128 | 129 | When you have no brain, you have good friends :) 130 | -------------------------------------------------------------------------------- /flare-on_10/ch12/pics/fixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch12/pics/fixed.png -------------------------------------------------------------------------------- /flare-on_10/ch12/pics/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch12/pics/random.png -------------------------------------------------------------------------------- /flare-on_10/ch12/pics/rc4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch12/pics/rc4.png -------------------------------------------------------------------------------- /flare-on_10/ch12/rsrc_fixed.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch12/rsrc_fixed.bin -------------------------------------------------------------------------------- /flare-on_10/ch12/slv.py: -------------------------------------------------------------------------------- 1 | 2 | from pwn import u64, p64, xor 3 | import base64 4 | from z3 import * 5 | 6 | flag_enc = b'\x19v7/=\x1d&?{\x069X\x12#%k*\x07<8\x18h\x16\x1c0\t4#\x08[!$6aj&j\x0fD]\x06\x00' 7 | 8 | def split(line, n): 9 | return [line[i:i+n] for i in range(0, len(line), n)] 10 | 11 | # from test_array.py 12 | table = b'\x02a$\xf5m\x84\x0cx\xfa\xfa\x18\xa3\xb9\x1c$_\xb9\x1c$_\x02a$\xf5m\x84\x0cx\xfa\xfa\x18\xa3\xfa\xfa\x18\xa3\xb9\x1c$_\x02a$\xf5m\x84\x0cxm\x84\x0cx\xfa\xfa\x18\xa3\xb9\x1c$_\x02a$\xf5' 13 | 14 | output = b'FLARE2023FLARE2023FLARE2023FLARE2023\x00\x00\x00\x00' 15 | output += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 16 | 17 | table = [u64(_) for _ in split(table, 8)] 18 | output = [u64(_) for _ in split(output, 8)] 19 | inputs = [] 20 | in_back = [] 21 | for x in range(len(output)): 22 | v = BitVec("i%x"%x, 64) 23 | in_back.append(v) 24 | inputs.append(v) 25 | 26 | print(inputs) 27 | 28 | for i in range(0, 7, 2): 29 | for j in range(7, -1, -1): 30 | tmp1 = inputs[i] 31 | tmp2 = table[j] ^ inputs[i+1] 32 | inputs[i] ^= tmp2 33 | inputs[i+1] = tmp1 34 | #print(inputs) 35 | #input() 36 | 37 | s = Solver() 38 | for i in range(len(inputs)): 39 | s.add(inputs[i] == output[i]) 40 | #print(s) 41 | print(s.check()) 42 | m = s.model() 43 | 44 | out = b'' 45 | for v in in_back: 46 | out += p64(m[v].as_long()) 47 | 48 | xx = base64.b64encode(out[:48]) 49 | print(xor(flag_enc, xx)[:41]) 50 | 51 | -------------------------------------------------------------------------------- /flare-on_10/ch12/test_array.py: -------------------------------------------------------------------------------- 1 | from unicorn import * 2 | from unicorn.x86_const import * 3 | from capstone import * 4 | from Crypto.Cipher import ARC4 5 | from pwn import p64 6 | 7 | 8 | def decrypt(key, data): 9 | c = ARC4.new(key) 10 | return c.decrypt(data) 11 | 12 | def disas_single(code, addr): 13 | md = Cs(CS_ARCH_X86, CS_MODE_64) 14 | for i in md.disasm(code, addr): 15 | return (i.address, i.mnemonic, i.op_str) 16 | 17 | def disas_all(code, addr): 18 | md = Cs(CS_ARCH_X86, CS_MODE_64) 19 | for i in md.disasm(code, addr): 20 | print("0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 21 | 22 | 23 | def hook_code(mu, addr, size, user_data): 24 | mem = mu.mem_read(addr, size) 25 | dis = disas_single(mem, addr) 26 | addr, mnemonic, op_str = dis 27 | #print(">> %-32s 0x%x\t%s\t%s"%(mem.hex(), addr, mnemonic, op_str)) 28 | if addr in [0x51f0, 0x552, 0x55a]: 29 | rbp = mu.reg_read(UC_X86_REG_RBP) 30 | # a8 / 28 31 | print(mu.mem_read(rbp - 0xa0, 64)) 32 | print(mu.mem_read(rbp - 0x60, 64)) 33 | if addr == 0x5df: 34 | #print("ooo") 35 | #print(hex(mu.reg_read(UC_X86_REG_RDI))) 36 | #print(hex(mu.reg_read(UC_X86_REG_RSI))) 37 | print(mu.mem_read(0xb000, 48)) 38 | 39 | if addr == 0x5e0: 40 | mu.emu_stop() 41 | 42 | 43 | def emu(): 44 | entry = 0x4af 45 | code = open('rsrc_fixed.bin', 'rb').read() 46 | 47 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 48 | mu.mem_map(0x0, 0x1000) 49 | mu.mem_map(0x7000, 0x1000) # stack 50 | mu.mem_map(0xa000, 0x1000) # s1 51 | mu.mem_map(0xb000, 0x1000) # s2 52 | mu.mem_write(0x0, code) 53 | mu.mem_write(0x7000, b'\x00'*0x1000) 54 | 55 | inp1 = b'FLARE2023FLARE2023FLARE2023FLARE2023' 56 | inp2 = b'\xcc\x16)L\x15\x16&\xf7\xfd1A\xf8*\xd7\x18\xbf\xbb\x1dQU\x0fr3\x82\x89NF\xe6.\xb7m\xbf\x8b,\x16l\x02a$\xf5\x89M2\x99o\xe5(\x8d' 57 | 58 | mu.reg_write(UC_X86_REG_RSP, 0x7500) 59 | mu.reg_write(UC_X86_REG_RBP, 0x7500) 60 | mu.reg_write(UC_X86_REG_RDX, 0x52414c46) # FLAR 61 | mu.reg_write(UC_X86_REG_RDI, 0xb000) 62 | mu.mem_write(0xa000, inp1) 63 | mu.mem_write(0xb000, inp2) 64 | mu.reg_write(UC_X86_REG_ESI, len(inp2)) 65 | 66 | mu.hook_add(UC_HOOK_CODE, hook_code) 67 | mu.emu_start(entry, 0x1000) 68 | #print(mu.mem_read(0xa000, 0x100)) 69 | #print(mu.reg_read(UC_X86_REG_RAX)) 70 | 71 | 72 | if __name__ == "__main__": 73 | emu() 74 | -------------------------------------------------------------------------------- /flare-on_10/ch13/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Obfuscated what ? 2 | 3 | Apply [challenge 5](../ch05) recipe, but disable all analysis options that you think might automatically create functions. 4 | 5 | Then give Ghidra the love it deserves and you're good to go... 6 | 7 | Some functions remained a bit hard to read because of messed up stack frames but if you stare long enough without blinking, it becomes good enough (apparently it can be fixed, but it seems i dont have the magic finger of [@huettenhain](https://twitter.com/huettenhain)). 8 | 9 | Everything interesting has already been said in more serious writeups, so i'll just share my [script](rop_to_shellcode.py) which emulates the ROP chain construction and dumps it as a [shellcode](shellcode.bin) for easier analysis (excuse the code quality, but at this stage of the contest, it becomes one big copy pasta mess). 10 | 11 | ## 2. Young Flareawan 12 | 13 | The rop chain takes 3 input: 14 | - the encrypted flag out of the jpg overlay 15 | - the character index it processes 16 | - the random buffer filled from the mersenne twister 17 | 18 | It performs a serie of operations on the encrypted flag, char by char (decrypt it), before xoring it with the random buffer and spitting it out as a base32 encoded string. 19 | 20 | ![zeros](pics/zero.jpg) 21 | 22 | Whenever there's some XOR like that, always try to zero the buffer, it can pay off ;) 23 | 24 | I've put everything together (rsrc decryption, rop chain to shellcode and flag decryption) is the final [script](solve.py). 25 | -------------------------------------------------------------------------------- /flare-on_10/ch13/pics/zero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_10/ch13/pics/zero.jpg -------------------------------------------------------------------------------- /flare-on_10/ch13/rop_to_shellcode.py: -------------------------------------------------------------------------------- 1 | 2 | # cursed because too much copy/paste during flareon ;) 3 | 4 | from unicorn import * 5 | from unicorn.x86_const import * 6 | from capstone import * 7 | from capstone.x86 import * 8 | import sys 9 | import base64 10 | 11 | 12 | 13 | r9 = base64.b64decode("BawAAADDBeQAAADDBegAAADDg8Baw4PAYMODwHDDg8B7wwWPAAAAwwWWAAAAwwNFJMNIg8U4wzlFJMP/wMOIBArDiVUQw0yJRRjDiEUgw4lFJMNIiU0Iw8dFJAAAAADDi0Ukw4tFSMOLTSTDSItNQMNIi1VAw0iLVVDDD7YEAcMPtkUgww+2DArDD7ZNIMP32MP30MMLwcPR+MPB+ALDwfgDw8H4BcPB+AbDwfgHw9Hhw8HhAsPB4QPDweEFw8HhBsPB4QfDLbEAAADDLbIAAADDLcMAAADDLcUAAADDLdwAAADDLfMAAADDLf8AAADDg+gYw4PoGsOD6B7Dg+gow4PoNsOD6ATDg+hJw4PoVsOD6FjDLYEAAADDLZAAAADDLZoAAADDK0Ukw0iD7TjDNaMAAADDNbYAAADDNb8AAADDNcIAAADDNckAAADDNcsAAADDg/ANwzXhAAAAwzXrAAAAw4PwFsOD8CDDg/Aiw4PwJcOD8EDDg/B4w4PwfMM1jwAAAMMzRSTDM8DDM8HD/8HDi8nDgeH/AAAAw4tVJMODwgLDi9LDTItFUMNBD7YUEMPR+sOB4v8AAADDI8rDg8EDww==") 14 | 15 | 16 | 17 | def disas_single(code, addr): 18 | md = Cs(CS_ARCH_X86, CS_MODE_64) 19 | md.detail = True 20 | for i in md.disasm(code, addr): 21 | return i 22 | #return (i.address, i.mnemonic, i.op_str) 23 | 24 | shellcode = [] 25 | def dis_gadget(addr, code): 26 | global shellcode 27 | md = Cs(CS_ARCH_X86, CS_MODE_64) 28 | size = 0 29 | for i in md.disasm(code, addr): 30 | if i.mnemonic == "ret": 31 | shellcode.append(code[:size]) 32 | break 33 | size += i.size 34 | print("%s\t%s" %(i.mnemonic, i.op_str)) 35 | 36 | 37 | class Emu: 38 | def __init__(self): 39 | fp = open(sys.argv[1], "rb") 40 | fp.seek(0x400) 41 | self.code = fp.read(0x65000) 42 | fp.close() 43 | 44 | 45 | def log_instruction(self, mu, i): 46 | #print(" >> 0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 47 | match i.mnemonic: 48 | case 'push': 49 | rsi = mu.reg_read(UC_X86_REG_RSI) 50 | #print(hex(rsi)[2:].rjust(8, "0")) 51 | data = mu.mem_read(rsi, 0x100) 52 | dis_gadget(rsi, data) 53 | case "ret": 54 | mu.emu_stop() 55 | case _: 56 | pass 57 | 58 | #print(" >> 0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 59 | 60 | 61 | 62 | def hook_code(self, mu, addr, size, user_data): 63 | mem = mu.mem_read(addr, size) 64 | dis = disas_single(mem, addr) 65 | 66 | self.log_instruction(mu, dis) 67 | 68 | def emu(self): 69 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 70 | mu.mem_map(0x1e70000, 0x1000) 71 | mu.mem_map(0x180001000, 0x100000) 72 | mu.mem_map(0x7f0a00000, 0x10000) 73 | 74 | mu.mem_write(0x180001000, self.code) 75 | mu.mem_write(0x1e70000, r9) 76 | mu.reg_write(UC_X86_REG_R9, 0x1e70000) 77 | mu.reg_write(UC_X86_REG_RBP, 0x7f0a04000) 78 | mu.reg_write(UC_X86_REG_RSP, 0x7f0a04000) 79 | mu.hook_add(UC_HOOK_CODE, self.hook_code) 80 | mu.emu_start(0x18001d361, 0x18009a81b) 81 | 82 | if __name__ == "__main__": 83 | e = Emu() 84 | e.emu() 85 | 86 | with open("shellcode.bin", "wb") as fp: 87 | for gadget in shellcode[::-1]: 88 | fp.write(gadget) 89 | -------------------------------------------------------------------------------- /flare-on_10/ch13/solve.py: -------------------------------------------------------------------------------- 1 | from unicorn import * 2 | from unicorn.x86_const import * 3 | from capstone import * 4 | from capstone.x86 import * 5 | from pwn import p64 6 | from Crypto.Cipher import ARC4 7 | import sys 8 | import base64 9 | import pefile 10 | 11 | 12 | def extract_rsrc(pe): 13 | """ stolen from binref, thx Jesko 14 | """ 15 | def _search(pe: pefile.PE, directory, level=0, *parts): 16 | for entry in directory.entries: 17 | if entry.name: 18 | identifier = str(entry.name) 19 | elif entry.id is not None: 20 | identifier = entry.id 21 | else: 22 | identifier = "lol" 23 | 24 | if entry.struct.DataIsDirectory: 25 | yield from _search(pe, entry.directory, level + 1, *parts, identifier) 26 | else: 27 | rva = entry.data.struct.OffsetToData 28 | size = entry.data.struct.Size 29 | path = '/'.join(str(p) for p in (*parts, identifier)) 30 | extract = None 31 | if extract is None: 32 | def extract(pe=pe): 33 | return pe.get_data(rva, size) 34 | yield { 35 | 'path': path, 36 | 'name': path.split("/")[1], 37 | 'extract': extract, 38 | 'offset': pe.get_offset_from_rva(rva) 39 | } 40 | 41 | pe.parse_data_directories(directories=pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']) 42 | rsrc = pe.DIRECTORY_ENTRY_RESOURCE 43 | return _search(pe, rsrc) 44 | 45 | 46 | 47 | 48 | def disas_single(code, addr): 49 | md = Cs(CS_ARCH_X86, CS_MODE_64) 50 | md.detail = True 51 | for i in md.disasm(code, addr): 52 | return i 53 | #return (i.address, i.mnemonic, i.op_str) 54 | 55 | 56 | 57 | class Emu: 58 | def __init__(self): 59 | self.extract_rsrc() 60 | 61 | # load .text section 62 | fp = open("y0da.exe", "rb") 63 | fp.seek(0x400) 64 | self.ropchain = [] 65 | self.code = fp.read(0x65000) 66 | fp.close() 67 | 68 | def extract_rsrc(self): 69 | pe = pefile.PE('y0da.exe') 70 | print("getting resource...") 71 | for rsrc in extract_rsrc(pe): 72 | if rsrc['path'] == 'M4ST3R/Y0D4/0': 73 | enc_res = rsrc['extract']() 74 | print("got %s"%rsrc['path']) 75 | 76 | key = b'patience_y0u_must_h4v3' # from md5 check 77 | print("decrypting (ARC4 with key %s)"%key) 78 | c = ARC4.new(key) 79 | dec_res = c.decrypt(enc_res) 80 | 81 | with open("y0da.jpg", "wb") as fp: 82 | fp.write(dec_res) 83 | print("saved as y0da.jpg for the lulz") 84 | 85 | overlay = dec_res[-516:] 86 | 87 | self.encrypted_flag = overlay[4:0x3d] 88 | self.rop_data = overlay[0x3d+4:] 89 | 90 | print("got this from the jpg overlay") 91 | print(" enc_flag: %s"%self.encrypted_flag.hex()) 92 | print(" rop_data: %s"%self.rop_data.hex()) 93 | 94 | 95 | def get_gadget_code(self, addr, code): 96 | md = Cs(CS_ARCH_X86, CS_MODE_64) 97 | size = 0 98 | for i in md.disasm(code, addr): 99 | if i.mnemonic == "ret": 100 | break 101 | size += i.size 102 | #print("%s\t%s" %(i.mnemonic, i.op_str)) 103 | self.ropchain.append(code[:size]) 104 | 105 | def log_instruction(self, mu, i): 106 | #print(" >> 0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 107 | match i.mnemonic: 108 | case "jmp": 109 | return 110 | case "call": 111 | if '[' in i.op_str: 112 | dest = i.op_str.split()[-1] 113 | mu.reg_write(UC_X86_REG_EIP, mu.reg_read(UC_X86_REG_EIP) + i.size) 114 | case 'push': 115 | rsi = mu.reg_read(UC_X86_REG_RSI) 116 | #print(hex(rsi)[2:].rjust(8, "0")) 117 | data = mu.mem_read(rsi, 0x100) 118 | self.get_gadget_code( rsi, data) 119 | case "ret": 120 | mu.emu_stop() 121 | case _: 122 | pass 123 | 124 | #print(" >> 0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 125 | 126 | 127 | 128 | def hook_code(self, mu, addr, size, user_data): 129 | mem = mu.mem_read(addr, size) 130 | dis = disas_single(mem, addr) 131 | 132 | self.log_instruction(mu, dis) 133 | 134 | def build_rop(self): 135 | """ emulates the ropchain creation 136 | grabs gadgets addr from the stack and convert 137 | them a working shellcode 138 | """ 139 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 140 | mu.mem_map(0x1e70000, 0x1000) 141 | mu.mem_map(0x180001000, 0x100000) 142 | mu.mem_map(0x7f0a00000, 0x10000) 143 | 144 | mu.mem_write(0x180001000, self.code) 145 | mu.mem_write(0x1e70000, self.rop_data) 146 | mu.reg_write(UC_X86_REG_R9, 0x1e70000) 147 | mu.reg_write(UC_X86_REG_RBP, 0x7f0a04000) 148 | mu.reg_write(UC_X86_REG_RSP, 0x7f0a04000) 149 | mu.hook_add(UC_HOOK_CODE, self.hook_code) 150 | print("emulating ropchain builder 0x18001d361 -> 0x18009a81b and convert chain to shellcode...") 151 | mu.emu_start(0x18001d361, 0x18009a81b) 152 | 153 | print("got %d gadgets"%(len(self.ropchain))) 154 | self.ropchain = b''.join(self.ropchain[::-1]) 155 | 156 | def emu_rop(self): 157 | flag_addr = 0x1E60000 158 | twist_addr = 0x1E50000 159 | 160 | stack_addr = 0xf000000 161 | code_addr = 0x2000000 162 | 163 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 164 | 165 | mu.mem_map(flag_addr, 0x1000) 166 | mu.mem_map(twist_addr, 0x1000) # dot not init, must zeros 167 | mu.mem_map(stack_addr, 0x1000) 168 | mu.mem_map(code_addr, 0x1000) 169 | 170 | mu.mem_write(code_addr, self.ropchain) 171 | mu.mem_write(flag_addr, self.encrypted_flag) 172 | 173 | rbp = stack_addr+0x100 174 | mu.reg_write(UC_X86_REG_RBP, rbp) 175 | mu.reg_write(UC_X86_REG_RSP, rbp) 176 | 177 | mu.mem_write(rbp+0x40, p64(flag_addr)) 178 | mu.mem_write(rbp+0x50, p64(twist_addr)) 179 | 180 | print("emulating ropchain 0x39 times...") 181 | 182 | for index in range(0x39): 183 | mu.mem_write(rbp+0x24, p64(index)) # char index to decrypt 184 | mu.emu_start(code_addr, code_addr + len(self.ropchain)) 185 | 186 | 187 | return mu.mem_read(flag_addr, 0x40).rstrip(b'\x00').decode('ascii') 188 | 189 | 190 | 191 | 192 | 193 | if __name__ == "__main__": 194 | e = Emu() 195 | e.build_rop() 196 | flag = e.emu_rop() 197 | print(flag) 198 | 199 | -------------------------------------------------------------------------------- /flare-on_11/README.md: -------------------------------------------------------------------------------- 1 | # Flare-On 11 "writeups" 2 | 3 | -------------------------------------------------------------------------------- /flare-on_11/ch01/README.md: -------------------------------------------------------------------------------- 1 | ## 1. frog 2 | -------------------------------------------------------------------------------- /flare-on_11/ch01/slv.py: -------------------------------------------------------------------------------- 1 | def GenerateFlagText(x, y): 2 | key = x + y*20 3 | encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf" 4 | return ''.join([chr(ord(c) ^ key) for c in encoded]) 5 | 6 | 7 | print(GenerateFlagText(10,10)) 8 | -------------------------------------------------------------------------------- /flare-on_11/ch03/README.md: -------------------------------------------------------------------------------- 1 | ## 3. aray 2 | 3 | cut, sed, awk the yara into a z3/python file. 4 | [bruteforce](bf.py) the 2 bytes crc32/md5/sha256 5 | [solve](slv.py) 6 | -------------------------------------------------------------------------------- /flare-on_11/ch03/aray.yara: -------------------------------------------------------------------------------- 1 | import "hash" 2 | 3 | rule aray 4 | { 5 | meta: 6 | description = "Matches on b7dc94ca98aa58dabb5404541c812db2" 7 | condition: 8 | filesize == 85 and hash.md5(0, filesize) == "b7dc94ca98aa58dabb5404541c812db2" and filesize ^ uint8(11) != 107 and uint8(55) & 128 == 0 and uint8(58) + 25 == 122 and uint8(7) & 128 == 0 and uint8(48) % 12 < 12 and uint8(17) > 31 and uint8(68) > 10 and uint8(56) < 155 and uint32(52) ^ 425706662 == 1495724241 and uint8(0) % 25 < 25 and filesize ^ uint8(75) != 25 and filesize ^ uint8(28) != 12 and uint8(35) < 160 and uint8(3) & 128 == 0 and uint8(56) & 128 == 0 and uint8(28) % 27 < 27 and uint8(4) > 30 and uint8(15) & 128 == 0 and uint8(68) % 19 < 19 and uint8(19) < 151 and filesize ^ uint8(73) != 17 and filesize ^ uint8(31) != 5 and uint8(38) % 24 < 24 and uint8(3) > 21 and uint8(54) & 128 == 0 and filesize ^ uint8(66) != 146 and uint32(17) - 323157430 == 1412131772 and hash.crc32(8, 2) == 0x61089c5c and filesize ^ uint8(77) != 22 and uint8(75) % 24 < 24 and uint8(66) < 133 and uint8(21) % 11 < 11 and uint8(46) < 154 and hash.crc32(34, 2) == 0x5888fc1b and uint8(55) > 5 and uint8(36) + 4 == 72 and filesize ^ uint8(82) != 228 and filesize ^ uint8(13) != 42 and filesize ^ uint8(6) != 39 and uint8(33) < 160 and filesize ^ uint8(55) != 244 and filesize ^ uint8(15) != 205 and filesize ^ uint8(3) != 43 and filesize ^ uint8(54) != 39 and uint8(28) & 128 == 0 and uint8(10) < 146 and filesize ^ uint8(56) != 246 and filesize ^ uint8(32) != 77 and uint8(73) > 26 and uint8(36) > 11 and uint8(70) > 6 and filesize ^ uint8(33) != 27 and uint8(48) & 128 == 0 and filesize ^ uint8(74) != 45 and uint8(27) ^ 21 == 40 and uint8(60) % 23 < 23 and filesize ^ uint8(67) != 63 and filesize ^ uint8(0) != 16 and uint8(51) % 15 < 15 and uint8(50) > 19 and uint8(27) < 147 and filesize ^ uint8(40) != 230 and filesize ^ uint8(2) != 205 and uint8(79) % 24 < 24 and uint8(69) < 148 and uint8(16) & 128 == 0 and uint8(61) % 26 < 26 and uint8(63) > 31 and uint8(14) & 128 == 0 and uint8(35) > 1 and filesize ^ uint8(11) != 33 and uint8(52) < 136 and uint8(54) > 15 and filesize ^ uint8(20) != 83 and uint8(43) > 24 and uint8(82) < 152 and uint32(59) ^ 512952669 == 1908304943 and filesize ^ uint8(79) != 186 and filesize ^ uint8(83) != 197 and uint8(39) < 134 and filesize ^ uint8(43) != 33 and uint8(72) > 10 and uint8(83) < 134 and uint8(44) % 27 < 27 and uint8(40) < 131 and uint8(80) % 31 < 31 and filesize ^ uint8(47) != 11 and uint8(55) % 11 < 11 and filesize ^ uint8(71) != 3 and uint8(65) - 29 == 70 and uint8(58) > 30 and filesize ^ uint8(37) != 37 and uint8(60) < 130 and uint8(27) & 128 == 0 and uint8(3) < 141 and uint8(73) & 128 == 0 and filesize ^ uint8(70) != 209 and filesize ^ uint8(2) != 54 and filesize ^ uint8(20) != 17 and uint8(33) > 18 and uint8(37) % 19 < 19 and filesize ^ uint8(62) != 15 and filesize ^ uint8(10) != 44 and uint8(7) % 12 < 12 and uint8(71) > 19 and filesize ^ uint8(50) != 86 and uint8(45) ^ 9 == 104 and uint8(8) < 133 and uint8(31) < 145 and uint8(14) > 20 and uint8(54) % 25 < 25 and filesize ^ uint8(49) != 156 and uint8(47) > 13 and uint8(29) > 22 and uint8(14) % 19 < 19 and filesize ^ uint8(17) != 16 and filesize ^ uint8(12) != 226 and filesize ^ uint8(65) != 28 and uint8(45) & 128 == 0 and filesize ^ uint8(6) != 129 and uint8(18) % 30 < 30 and filesize ^ uint8(62) != 246 and uint8(78) % 13 < 13 and uint8(36) & 128 == 0 and uint8(10) & 128 == 0 and uint8(62) > 1 and uint8(33) & 128 == 0 and filesize ^ uint8(83) != 31 and uint8(83) % 21 < 21 and uint8(11) > 18 and uint8(80) < 143 and uint8(81) % 14 < 14 and uint8(43) < 160 and uint8(1) > 19 and uint8(42) % 17 < 17 and uint8(44) < 147 and filesize ^ uint8(63) != 34 and filesize ^ uint8(44) != 17 and uint32(28) - 419186860 == 959764852 and uint8(74) + 11 == 116 and uint8(48) < 136 and uint8(47) < 142 and hash.crc32(63, 2) == 0x66715919 and uint8(58) < 146 and filesize ^ uint8(71) != 128 and uint8(45) < 136 and uint8(31) % 17 < 17 and uint8(43) & 128 == 0 and filesize ^ uint8(43) != 251 and uint8(65) > 1 and uint8(24) & 128 == 0 and uint8(37) < 139 and filesize ^ uint8(28) != 238 and uint8(78) & 128 == 0 and filesize ^ uint8(13) != 219 and uint8(19) % 30 < 30 and hash.sha256(14, 2) == "403d5f23d149670348b147a15eeb7010914701a7e99aad2e43f90cfa0325c76f" and filesize ^ uint8(53) != 243 and uint8(81) & 128 == 0 and uint8(46) % 28 < 28 and filesize ^ uint8(65) != 215 and filesize ^ uint8(0) != 41 and uint8(84) < 129 and uint8(60) & 128 == 0 and uint8(20) > 1 and uint8(2) % 28 < 28 and uint8(58) % 14 < 14 and uint8(34) & 128 == 0 and uint8(21) & 128 == 0 and uint8(84) % 18 < 18 and uint8(74) % 10 < 10 and uint8(9) < 151 and uint8(73) % 23 < 23 and filesize ^ uint8(39) != 49 and uint8(4) % 17 < 17 and filesize ^ uint8(60) != 142 and filesize ^ uint8(69) != 30 and uint8(30) > 6 and uint8(65) & 128 == 0 and uint8(39) % 11 < 11 and uint8(13) % 27 < 27 and uint8(17) % 11 < 11 and uint8(56) % 26 < 26 and uint8(29) < 157 and uint8(57) & 128 == 0 and filesize ^ uint8(29) != 37 and uint8(77) > 5 and filesize ^ uint8(16) != 144 and uint8(37) & 128 == 0 and filesize ^ uint8(25) != 47 and uint8(67) & 128 == 0 and filesize ^ uint8(24) != 94 and uint8(68) < 138 and uint8(57) < 138 and filesize ^ uint8(27) != 43 and filesize ^ uint8(30) != 18 and filesize ^ uint8(59) != 13 and uint8(27) % 26 < 26 and uint8(56) > 8 and uint8(69) & 128 == 0 and uint8(18) & 128 == 0 and uint8(64) < 154 and uint8(76) & 128 == 0 and uint8(71) % 28 < 28 and filesize ^ uint8(84) != 3 and filesize ^ uint8(38) != 84 and uint8(32) < 140 and filesize ^ uint8(42) != 91 and uint8(40) > 15 and uint8(27) > 23 and uint8(6) % 12 < 12 and uint8(10) % 10 < 10 and uint8(8) % 21 < 21 and filesize ^ uint8(18) != 234 and uint8(68) & 128 == 0 and uint8(7) < 131 and uint8(72) < 134 and uint8(16) > 25 and uint8(12) % 23 < 23 and uint8(41) % 27 < 27 and uint8(1) % 17 < 17 and uint8(26) > 31 and hash.sha256(56, 2) == "593f2d04aab251f60c9e4b8bbc1e05a34e920980ec08351a18459b2bc7dbf2f6" and uint8(65) < 149 and filesize ^ uint8(51) != 0 and uint8(66) > 30 and filesize ^ uint8(68) != 8 and uint8(25) % 23 < 23 and uint8(1) & 128 == 0 and filesize ^ uint8(81) != 7 and uint8(36) % 22 < 22 and uint8(24) < 148 and uint8(12) < 147 and uint8(74) < 152 and filesize ^ uint8(21) != 27 and filesize ^ uint8(23) != 18 and uint8(38) & 128 == 0 and uint8(26) % 25 < 25 and filesize ^ uint8(19) != 31 and uint8(82) > 3 and uint8(5) % 27 < 27 and uint8(5) & 128 == 0 and uint8(75) - 30 == 86 and uint8(54) < 152 and uint8(75) < 142 and uint8(20) % 28 < 28 and uint8(30) & 128 == 0 and uint32(66) ^ 310886682 == 849718389 and uint8(64) % 24 < 24 and uint32(10) + 383041523 == 2448764514 and uint8(79) & 128 == 0 and filesize ^ uint8(59) != 194 and uint8(61) & 128 == 0 and uint8(70) < 139 and uint8(77) & 128 == 0 and uint8(13) & 128 == 0 and uint8(21) < 138 and filesize ^ uint8(46) != 186 and uint8(43) % 26 < 26 and uint8(61) < 160 and filesize ^ uint8(34) != 39 and uint8(6) > 6 and uint8(35) & 128 == 0 and uint8(23) < 141 and filesize ^ uint8(82) != 32 and filesize ^ uint8(48) != 29 and uint8(59) & 128 == 0 and uint8(40) % 19 < 19 and filesize ^ uint8(39) != 18 and filesize ^ uint8(45) != 146 and uint8(80) & 128 == 0 and uint8(16) < 134 and uint8(74) > 1 and uint8(23) & 128 == 0 and uint8(32) & 128 == 0 and filesize ^ uint8(47) != 119 and filesize ^ uint8(63) != 135 and uint8(64) > 27 and uint32(37) + 367943707 == 1228527996 and uint8(82) % 28 < 28 and uint8(32) > 28 and filesize ^ uint8(24) != 217 and uint8(53) < 144 and uint8(29) & 128 == 0 and uint32(22) ^ 372102464 == 1879700858 and uint8(52) % 23 < 23 and filesize ^ uint8(76) != 88 and filesize ^ uint8(55) != 17 and uint8(26) & 128 == 0 and uint8(51) > 7 and uint8(12) > 19 and filesize ^ uint8(14) != 99 and filesize ^ uint8(37) != 141 and filesize ^ uint8(14) != 161 and uint8(45) % 17 < 17 and uint8(33) % 25 < 25 and filesize ^ uint8(67) != 55 and filesize ^ uint8(53) != 19 and uint8(30) < 131 and uint8(0) & 128 == 0 and uint8(66) & 128 == 0 and uint8(41) > 5 and uint8(71) & 128 == 0 and uint8(29) % 12 < 12 and uint8(4) < 139 and uint8(77) < 154 and filesize ^ uint8(12) != 116 and uint8(39) > 7 and uint8(75) & 128 == 0 and uint8(78) > 24 and uint8(69) > 25 and uint8(2) + 11 == 119 and uint8(15) < 156 and filesize ^ uint8(69) != 241 and filesize ^ uint8(35) != 18 and filesize ^ uint8(17) != 208 and hash.md5(0, 2) == "89484b14b36a8d5329426a3d944d2983" and filesize ^ uint8(4) != 23 and uint8(15) % 16 < 16 and filesize ^ uint8(75) != 35 and uint32(46) - 412326611 == 1503714457 and uint8(11) % 27 < 27 and hash.crc32(78, 2) == 0x7cab8d64 and uint8(83) & 128 == 0 and filesize ^ uint8(26) != 161 and uint8(49) % 13 < 13 and filesize ^ uint8(18) != 33 and uint8(6) < 155 and uint8(41) < 140 and filesize ^ uint8(68) != 135 and filesize ^ uint8(9) != 5 and uint8(9) & 128 == 0 and filesize ^ uint8(36) != 95 and uint8(7) > 18 and filesize ^ uint8(23) != 242 and uint8(62) < 146 and uint8(49) & 128 == 0 and uint8(62) & 128 == 0 and uint8(4) & 128 == 0 and filesize ^ uint8(58) != 12 and uint8(72) & 128 == 0 and uint8(18) > 13 and filesize ^ uint8(42) != 1 and uint8(59) % 23 < 23 and uint8(53) & 128 == 0 and filesize ^ uint8(78) != 163 and uint8(60) > 14 and uint8(47) % 18 < 18 and uint8(79) > 31 and uint8(22) < 152 and filesize ^ uint8(64) != 50 and filesize ^ uint8(19) != 222 and uint8(81) < 131 and uint8(7) - 15 == 82 and filesize ^ uint8(51) != 204 and uint8(28) > 27 and uint32(70) + 349203301 == 2034162376 and filesize ^ uint8(61) != 94 and uint8(76) > 2 and filesize ^ uint8(77) != 223 and uint8(19) > 4 and uint8(80) > 2 and filesize ^ uint8(35) != 120 and filesize ^ uint8(22) != 31 and uint8(10) > 9 and uint8(22) > 20 and uint8(38) < 135 and filesize ^ uint8(10) != 205 and uint8(25) & 128 == 0 and uint8(13) < 147 and uint8(42) & 128 == 0 and hash.md5(76, 2) == "f98ed07a4d5f50f7de1410d905f1477f" and filesize ^ uint8(48) != 99 and filesize ^ uint8(16) != 7 and uint8(11) < 154 and filesize ^ uint8(76) != 30 and uint8(30) % 15 < 15 and filesize ^ uint8(74) != 193 and filesize ^ uint8(52) != 22 and filesize ^ uint8(36) != 6 and uint8(22) % 22 < 22 and uint8(44) & 128 == 0 and uint8(50) & 128 == 0 and filesize ^ uint8(25) != 224 and uint8(15) > 26 and filesize ^ uint8(60) != 43 and uint8(22) & 128 == 0 and uint8(82) & 128 == 0 and uint32(80) - 473886976 == 69677856 and uint8(75) > 30 and uint8(32) % 17 < 17 and filesize ^ uint8(15) != 27 and uint8(67) % 16 < 16 and uint8(23) > 2 and uint8(62) % 13 < 13 and uint8(34) < 138 and filesize ^ uint8(31) != 32 and uint8(72) % 14 < 14 and filesize ^ uint8(81) != 242 and filesize ^ uint8(54) != 141 and uint8(63) & 128 == 0 and uint8(0) < 129 and uint8(70) % 21 < 21 and uint8(8) & 128 == 0 and uint8(61) > 12 and uint8(24) > 22 and uint8(53) % 23 < 23 and uint8(46) & 128 == 0 and uint8(24) % 26 < 26 and uint32(3) ^ 298697263 == 2108416586 and uint8(21) - 21 == 94 and uint8(67) < 144 and uint8(48) > 15 and uint8(37) > 16 and uint8(42) < 157 and uint8(16) ^ 7 == 115 and uint8(13) > 21 and filesize ^ uint8(45) != 19 and uint8(47) & 128 == 0 and filesize ^ uint8(80) != 56 and filesize ^ uint8(78) != 6 and uint8(76) % 24 < 24 and uint8(73) < 136 and filesize ^ uint8(52) != 238 and uint8(50) % 11 < 11 and filesize ^ uint8(7) != 15 and filesize ^ uint8(66) != 51 and uint8(59) > 4 and uint8(46) > 22 and filesize ^ uint8(3) != 147 and uint8(63) % 30 < 30 and uint8(36) < 146 and uint8(26) < 132 and uint8(6) & 128 == 0 and filesize ^ uint8(30) != 249 and uint32(41) + 404880684 == 1699114335 and filesize ^ uint8(5) != 243 and uint8(70) & 128 == 0 and uint8(9) % 22 < 22 and uint8(59) < 141 and filesize ^ uint8(79) != 104 and filesize ^ uint8(5) != 43 and filesize ^ uint8(72) != 219 and uint8(52) > 25 and uint8(74) & 128 == 0 and uint8(28) < 160 and uint8(51) & 128 == 0 and hash.md5(50, 2) == "657dae0913ee12be6fb2a6f687aae1c7" and uint8(83) > 16 and uint8(31) > 7 and uint8(84) & 128 == 0 and filesize ^ uint8(46) != 18 and uint8(2) > 20 and uint8(5) < 158 and filesize ^ uint8(32) != 30 and filesize ^ uint8(50) != 219 and uint8(26) - 7 == 25 and uint8(53) > 24 and uint8(77) % 24 < 24 and uint8(3) % 13 < 13 and filesize ^ uint8(9) != 164 and filesize ^ uint8(80) != 236 and uint8(65) % 22 < 22 and filesize ^ uint8(84) != 231 and filesize ^ uint8(49) != 10 and uint8(67) > 27 and uint8(34) % 19 < 19 and uint8(64) & 128 == 0 and filesize ^ uint8(27) != 244 and uint8(12) & 128 == 0 and uint8(51) < 139 and uint8(35) % 15 < 15 and uint8(5) > 14 and filesize ^ uint8(34) != 115 and filesize ^ uint8(38) != 8 and filesize ^ uint8(72) != 37 and uint8(20) & 128 == 0 and uint8(17) < 150 and filesize ^ uint8(70) != 41 and uint8(66) % 16 < 16 and uint8(17) & 128 == 0 and uint8(19) & 128 == 0 and filesize ^ uint8(33) != 157 and uint8(21) > 7 and uint8(58) & 128 == 0 and uint8(71) < 130 and uint8(41) & 128 == 0 and uint8(57) > 11 and hash.md5(32, 2) == "738a656e8e8ec272ca17cd51e12f558b" and filesize ^ uint8(8) != 2 and filesize ^ uint8(57) != 186 and uint8(11) & 128 == 0 and uint8(2) < 147 and uint8(23) % 16 < 16 and uint8(78) < 141 and uint8(38) > 18 and filesize ^ uint8(41) != 233 and uint8(18) < 137 and uint8(40) & 128 == 0 and filesize ^ uint8(21) != 188 and filesize ^ uint8(57) != 14 and filesize ^ uint8(4) != 253 and uint8(14) < 153 and uint8(31) & 128 == 0 and uint8(81) > 11 and uint8(2) & 128 == 0 and filesize ^ uint8(22) != 191 and uint8(44) > 5 and uint8(84) + 3 == 128 and uint8(20) < 135 and filesize ^ uint8(73) != 61 and filesize ^ uint8(26) != 44 and uint8(1) < 158 and filesize ^ uint8(29) != 158 and uint8(49) < 129 and filesize ^ uint8(64) != 158 and uint8(25) < 154 and uint8(63) < 129 and uint8(84) > 26 and uint8(39) & 128 == 0 and uint8(25) > 27 and uint8(49) > 27 and uint8(9) > 23 and filesize ^ uint8(7) != 221 and uint8(50) < 138 and uint8(76) < 156 and filesize ^ uint8(61) != 239 and uint8(57) % 27 < 27 and filesize ^ uint8(8) != 107 and uint8(79) < 146 and filesize ^ uint8(40) != 49 and uint8(0) > 30 and uint8(45) > 17 and uint8(16) % 31 < 31 and filesize ^ uint8(1) != 232 and filesize ^ uint8(56) != 22 and uint8(42) > 3 and uint8(52) & 128 == 0 and uint8(69) % 30 < 30 and uint8(55) < 153 and filesize ^ uint8(41) != 74 and filesize ^ uint8(1) != 0 and filesize ^ uint8(44) != 96 and filesize ^ uint8(58) != 77 and uint8(34) > 18 and uint8(8) > 3 9 | } 10 | -------------------------------------------------------------------------------- /flare-on_11/ch03/bf.py: -------------------------------------------------------------------------------- 1 | 2 | from hashlib import sha256, md5 3 | from itertools import permutations 4 | from zlib import crc32 5 | 6 | md = [ 7 | "89484b14b36a8d5329426a3d944d2983", 8 | "f98ed07a4d5f50f7de1410d905f1477f", 9 | "657dae0913ee12be6fb2a6f687aae1c7", 10 | "738a656e8e8ec272ca17cd51e12f558b", 11 | ] 12 | 13 | crc = [ 14 | 0x61089c5c, 15 | 0x5888fc1b, 16 | 0x66715919, 17 | 0x7cab8d64 18 | ] 19 | 20 | sha = [ 21 | "403d5f23d149670348b147a15eeb7010914701a7e99aad2e43f90cfa0325c76f", 22 | "593f2d04aab251f60c9e4b8bbc1e05a34e920980ec08351a18459b2bc7dbf2f6" 23 | ] 24 | 25 | 26 | for x in permutations(range(0x100), 2): 27 | if sha256(bytearray(x)).hexdigest() in sha: 28 | print(sha256(bytearray(x)).hexdigest()) 29 | print(bytearray(x)) 30 | print("----") 31 | 32 | 33 | if md5(bytearray(x)).hexdigest() in md: 34 | print(md5(bytearray(x)).hexdigest()) 35 | print(bytearray(x)) 36 | print("----") 37 | 38 | if crc32(bytearray(x)) in crc: 39 | print(hex(crc32(bytearray(x)))) 40 | print(bytearray(x)) 41 | print("----") 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /flare-on_11/ch03/slv.py: -------------------------------------------------------------------------------- 1 | from z3 import * 2 | 3 | filesize = 85 4 | s = Solver() 5 | flag = [] 6 | for x in range(filesize): 7 | f = BitVec("f%d"%x, 16) 8 | s.add(And(f >= ord(" "), f <= ord("~"))) 9 | flag.append(f) 10 | 11 | """ 12 | s.add(hash.md5(0, filesize] == "b7dc94ca98aa58dabb5404541c812db2") 13 | """ 14 | 15 | 16 | def bv32(n): 17 | return Concat(Extract(7, 0, flag[n + 3]), 18 | Extract(7, 0, flag[n + 2]), 19 | Extract(7, 0, flag[n + 1]), 20 | Extract(7, 0, flag[n + 0]) 21 | ) 22 | 23 | 24 | #s.add(hash.sha256(14, 2] == "403d5f23d149670348b147a15eeb7010914701a7e99aad2e43f90cfa0325c76f") 25 | s.add(flag[14] == ord(" ")) 26 | s.add(flag[15] == ord("s")) 27 | 28 | #s.add(hash.sha256(56, 2] == "593f2d04aab251f60c9e4b8bbc1e05a34e920980ec08351a18459b2bc7dbf2f6") 29 | s.add(flag[56] == ord("f")) 30 | s.add(flag[57] == ord("l")) 31 | 32 | 33 | #s.add(hash.md5(0, 2] == "89484b14b36a8d5329426a3d944d2983") 34 | s.add(flag[0] == ord("r")) 35 | s.add(flag[1] == ord("u")) 36 | 37 | #s.add(hash.md5(76, 2] == "f98ed07a4d5f50f7de1410d905f1477f") 38 | s.add(flag[76] == ord("i")) 39 | s.add(flag[77] == ord("o")) 40 | 41 | #s.add(hash.md5(50, 2] == "657dae0913ee12be6fb2a6f687aae1c7") 42 | s.add(flag[50] == ord("3")) 43 | s.add(flag[51] == ord("A")) 44 | 45 | #s.add(hash.md5(32, 2] == "738a656e8e8ec272ca17cd51e12f558b") 46 | s.add(flag[32] == ord("u")) 47 | s.add(flag[33] == ord("l")) 48 | 49 | 50 | #s.add(hash.crc32(8, 2] == 0x61089c5c) 51 | s.add(flag[8] == ord("r")) 52 | s.add(flag[9] == ord("e")) 53 | #s.add(hash.crc32(34, 2] == 0x5888fc1b) 54 | s.add(flag[34] == ord("e")) 55 | s.add(flag[35] == ord("A")) 56 | #s.add(hash.crc32(63, 2] == 0x66715919) 57 | s.add(flag[63] == ord("n")) 58 | s.add(flag[64] == ord(".")) 59 | #s.add(hash.crc32(78, 2] == 0x7cab8d64) 60 | s.add(flag[78] == ord("n")) 61 | s.add(flag[79] == ord(":")) 62 | 63 | s.add(bv32(52) ^ 425706662 == 1495724241) 64 | s.add(bv32(17) - 323157430 == 1412131772) 65 | s.add(bv32(59) ^ 512952669 == 1908304943) 66 | s.add(bv32(28) - 419186860 == 959764852) 67 | s.add(bv32(66) ^ 310886682 == 849718389) 68 | s.add(bv32(10) + 383041523 == 2448764514) 69 | s.add(bv32(37) + 367943707 == 1228527996) 70 | s.add(bv32(22) ^ 372102464 == 1879700858) 71 | s.add(bv32(46) - 412326611 == 1503714457) 72 | s.add(bv32(70) + 349203301 == 2034162376) 73 | s.add(bv32(80) - 473886976 == 69677856) 74 | s.add(bv32(3) ^ 298697263 == 2108416586) 75 | s.add(bv32(41) + 404880684 == 1699114335) 76 | 77 | 78 | s.add(filesize ^ flag[11] != 107) 79 | s.add(flag[55] & 128 == 0) 80 | s.add(flag[58] + 25 == 122) 81 | s.add(flag[7] & 128 == 0) 82 | s.add(flag[48] % 12 < 12) 83 | s.add(flag[17] > 31) 84 | s.add(flag[68] > 10) 85 | s.add(flag[56] < 155) 86 | s.add(flag[0] % 25 < 25) 87 | s.add(filesize ^ flag[75] != 25) 88 | s.add(filesize ^ flag[28] != 12) 89 | s.add(flag[35] < 160) 90 | s.add(flag[3] & 128 == 0) 91 | s.add(flag[56] & 128 == 0) 92 | s.add(flag[28] % 27 < 27) 93 | s.add(flag[4] > 30) 94 | s.add(flag[15] & 128 == 0) 95 | s.add(flag[68] % 19 < 19) 96 | s.add(flag[19] < 151) 97 | s.add(filesize ^ flag[73] != 17) 98 | s.add(filesize ^ flag[31] != 5) 99 | s.add(flag[38] % 24 < 24) 100 | s.add(flag[3] > 21) 101 | s.add(flag[54] & 128 == 0) 102 | s.add(filesize ^ flag[66] != 146) 103 | s.add(filesize ^ flag[77] != 22) 104 | s.add(flag[75] % 24 < 24) 105 | s.add(flag[66] < 133) 106 | s.add(flag[21] % 11 < 11) 107 | s.add(flag[46] < 154) 108 | s.add(flag[55] > 5) 109 | s.add(flag[36] + 4 == 72) 110 | s.add(filesize ^ flag[82] != 228) 111 | s.add(filesize ^ flag[13] != 42) 112 | s.add(filesize ^ flag[6] != 39) 113 | s.add(flag[33] < 160) 114 | s.add(filesize ^ flag[55] != 244) 115 | s.add(filesize ^ flag[15] != 205) 116 | s.add(filesize ^ flag[3] != 43) 117 | s.add(filesize ^ flag[54] != 39) 118 | s.add(flag[28] & 128 == 0) 119 | s.add(flag[10] < 146) 120 | s.add(filesize ^ flag[56] != 246) 121 | s.add(filesize ^ flag[32] != 77) 122 | s.add(flag[73] > 26) 123 | s.add(flag[36] > 11) 124 | s.add(flag[70] > 6) 125 | s.add(filesize ^ flag[33] != 27) 126 | s.add(flag[48] & 128 == 0) 127 | s.add(filesize ^ flag[74] != 45) 128 | s.add(flag[27] ^ 21 == 40) 129 | s.add(flag[60] % 23 < 23) 130 | s.add(filesize ^ flag[67] != 63) 131 | s.add(filesize ^ flag[0] != 16) 132 | s.add(flag[51] % 15 < 15) 133 | s.add(flag[50] > 19) 134 | s.add(flag[27] < 147) 135 | s.add(filesize ^ flag[40] != 230) 136 | s.add(filesize ^ flag[2] != 205) 137 | s.add(flag[79] % 24 < 24) 138 | s.add(flag[69] < 148) 139 | s.add(flag[16] & 128 == 0) 140 | s.add(flag[61] % 26 < 26) 141 | s.add(flag[63] > 31) 142 | s.add(flag[14] & 128 == 0) 143 | s.add(flag[35] > 1) 144 | s.add(filesize ^ flag[11] != 33) 145 | s.add(flag[52] < 136) 146 | s.add(flag[54] > 15) 147 | s.add(filesize ^ flag[20] != 83) 148 | s.add(flag[43] > 24) 149 | s.add(flag[82] < 152) 150 | s.add(filesize ^ flag[79] != 186) 151 | s.add(filesize ^ flag[83] != 197) 152 | s.add(flag[39] < 134) 153 | s.add(filesize ^ flag[43] != 33) 154 | s.add(flag[72] > 10) 155 | s.add(flag[83] < 134) 156 | s.add(flag[44] % 27 < 27) 157 | s.add(flag[40] < 131) 158 | s.add(flag[80] % 31 < 31) 159 | s.add(filesize ^ flag[47] != 11) 160 | s.add(flag[55] % 11 < 11) 161 | s.add(filesize ^ flag[71] != 3) 162 | s.add(flag[65] - 29 == 70) 163 | s.add(flag[58] > 30) 164 | s.add(filesize ^ flag[37] != 37) 165 | s.add(flag[60] < 130) 166 | s.add(flag[27] & 128 == 0) 167 | s.add(flag[3] < 141) 168 | s.add(flag[73] & 128 == 0) 169 | s.add(filesize ^ flag[70] != 209) 170 | s.add(filesize ^ flag[2] != 54) 171 | s.add(filesize ^ flag[20] != 17) 172 | s.add(flag[33] > 18) 173 | s.add(flag[37] % 19 < 19) 174 | s.add(filesize ^ flag[62] != 15) 175 | s.add(filesize ^ flag[10] != 44) 176 | s.add(flag[7] % 12 < 12) 177 | s.add(flag[71] > 19) 178 | s.add(filesize ^ flag[50] != 86) 179 | s.add(flag[45] ^ 9 == 104) 180 | s.add(flag[8] < 133) 181 | s.add(flag[31] < 145) 182 | s.add(flag[14] > 20) 183 | s.add(flag[54] % 25 < 25) 184 | s.add(filesize ^ flag[49] != 156) 185 | s.add(flag[47] > 13) 186 | s.add(flag[29] > 22) 187 | s.add(flag[14] % 19 < 19) 188 | s.add(filesize ^ flag[17] != 16) 189 | s.add(filesize ^ flag[12] != 226) 190 | s.add(filesize ^ flag[65] != 28) 191 | s.add(flag[45] & 128 == 0) 192 | s.add(filesize ^ flag[6] != 129) 193 | s.add(flag[18] % 30 < 30) 194 | s.add(filesize ^ flag[62] != 246) 195 | s.add(flag[78] % 13 < 13) 196 | s.add(flag[36] & 128 == 0) 197 | s.add(flag[10] & 128 == 0) 198 | s.add(flag[62] > 1) 199 | s.add(flag[33] & 128 == 0) 200 | s.add(filesize ^ flag[83] != 31) 201 | s.add(flag[83] % 21 < 21) 202 | s.add(flag[11] > 18) 203 | s.add(flag[80] < 143) 204 | s.add(flag[81] % 14 < 14) 205 | s.add(flag[43] < 160) 206 | s.add(flag[1] > 19) 207 | s.add(flag[42] % 17 < 17) 208 | s.add(flag[44] < 147) 209 | s.add(filesize ^ flag[63] != 34) 210 | s.add(filesize ^ flag[44] != 17) 211 | s.add(flag[74] + 11 == 116) 212 | s.add(flag[48] < 136) 213 | s.add(flag[47] < 142) 214 | s.add(flag[58] < 146) 215 | s.add(filesize ^ flag[71] != 128) 216 | s.add(flag[45] < 136) 217 | s.add(flag[31] % 17 < 17) 218 | s.add(flag[43] & 128 == 0) 219 | s.add(filesize ^ flag[43] != 251) 220 | s.add(flag[65] > 1) 221 | s.add(flag[24] & 128 == 0) 222 | s.add(flag[37] < 139) 223 | s.add(filesize ^ flag[28] != 238) 224 | s.add(flag[78] & 128 == 0) 225 | s.add(filesize ^ flag[13] != 219) 226 | s.add(flag[19] % 30 < 30) 227 | s.add(filesize ^ flag[53] != 243) 228 | s.add(flag[81] & 128 == 0) 229 | s.add(flag[46] % 28 < 28) 230 | s.add(filesize ^ flag[65] != 215) 231 | s.add(filesize ^ flag[0] != 41) 232 | s.add(flag[84] < 129) 233 | s.add(flag[60] & 128 == 0) 234 | s.add(flag[20] > 1) 235 | s.add(flag[2] % 28 < 28) 236 | s.add(flag[58] % 14 < 14) 237 | s.add(flag[34] & 128 == 0) 238 | s.add(flag[21] & 128 == 0) 239 | s.add(flag[84] % 18 < 18) 240 | s.add(flag[74] % 10 < 10) 241 | s.add(flag[9] < 151) 242 | s.add(flag[73] % 23 < 23) 243 | s.add(filesize ^ flag[39] != 49) 244 | s.add(flag[4] % 17 < 17) 245 | s.add(filesize ^ flag[60] != 142) 246 | s.add(filesize ^ flag[69] != 30) 247 | s.add(flag[30] > 6) 248 | s.add(flag[65] & 128 == 0) 249 | s.add(flag[39] % 11 < 11) 250 | s.add(flag[13] % 27 < 27) 251 | s.add(flag[17] % 11 < 11) 252 | s.add(flag[56] % 26 < 26) 253 | s.add(flag[29] < 157) 254 | s.add(flag[57] & 128 == 0) 255 | s.add(filesize ^ flag[29] != 37) 256 | s.add(flag[77] > 5) 257 | s.add(filesize ^ flag[16] != 144) 258 | s.add(flag[37] & 128 == 0) 259 | s.add(filesize ^ flag[25] != 47) 260 | s.add(flag[67] & 128 == 0) 261 | s.add(filesize ^ flag[24] != 94) 262 | s.add(flag[68] < 138) 263 | s.add(flag[57] < 138) 264 | s.add(filesize ^ flag[27] != 43) 265 | s.add(filesize ^ flag[30] != 18) 266 | s.add(filesize ^ flag[59] != 13) 267 | s.add(flag[27] % 26 < 26) 268 | s.add(flag[56] > 8) 269 | s.add(flag[69] & 128 == 0) 270 | s.add(flag[18] & 128 == 0) 271 | s.add(flag[64] < 154) 272 | s.add(flag[76] & 128 == 0) 273 | s.add(flag[71] % 28 < 28) 274 | s.add(filesize ^ flag[84] != 3) 275 | s.add(filesize ^ flag[38] != 84) 276 | s.add(flag[32] < 140) 277 | s.add(filesize ^ flag[42] != 91) 278 | s.add(flag[40] > 15) 279 | s.add(flag[27] > 23) 280 | s.add(flag[6] % 12 < 12) 281 | s.add(flag[10] % 10 < 10) 282 | s.add(flag[8] % 21 < 21) 283 | s.add(filesize ^ flag[18] != 234) 284 | s.add(flag[68] & 128 == 0) 285 | s.add(flag[7] < 131) 286 | s.add(flag[72] < 134) 287 | s.add(flag[16] > 25) 288 | s.add(flag[12] % 23 < 23) 289 | s.add(flag[41] % 27 < 27) 290 | s.add(flag[1] % 17 < 17) 291 | s.add(flag[26] > 31) 292 | s.add(flag[65] < 149) 293 | s.add(filesize ^ flag[51] != 0) 294 | s.add(flag[66] > 30) 295 | s.add(filesize ^ flag[68] != 8) 296 | s.add(flag[25] % 23 < 23) 297 | s.add(flag[1] & 128 == 0) 298 | s.add(filesize ^ flag[81] != 7) 299 | s.add(flag[36] % 22 < 22) 300 | s.add(flag[24] < 148) 301 | s.add(flag[12] < 147) 302 | s.add(flag[74] < 152) 303 | s.add(filesize ^ flag[21] != 27) 304 | s.add(filesize ^ flag[23] != 18) 305 | s.add(flag[38] & 128 == 0) 306 | s.add(flag[26] % 25 < 25) 307 | s.add(filesize ^ flag[19] != 31) 308 | s.add(flag[82] > 3) 309 | s.add(flag[5] % 27 < 27) 310 | s.add(flag[5] & 128 == 0) 311 | s.add(flag[75] - 30 == 86) 312 | s.add(flag[54] < 152) 313 | s.add(flag[75] < 142) 314 | s.add(flag[20] % 28 < 28) 315 | s.add(flag[30] & 128 == 0) 316 | s.add(flag[64] % 24 < 24) 317 | s.add(flag[79] & 128 == 0) 318 | s.add(filesize ^ flag[59] != 194) 319 | s.add(flag[61] & 128 == 0) 320 | s.add(flag[70] < 139) 321 | s.add(flag[77] & 128 == 0) 322 | s.add(flag[13] & 128 == 0) 323 | s.add(flag[21] < 138) 324 | s.add(filesize ^ flag[46] != 186) 325 | s.add(flag[43] % 26 < 26) 326 | s.add(flag[61] < 160) 327 | s.add(filesize ^ flag[34] != 39) 328 | s.add(flag[6] > 6) 329 | s.add(flag[35] & 128 == 0) 330 | s.add(flag[23] < 141) 331 | s.add(filesize ^ flag[82] != 32) 332 | s.add(filesize ^ flag[48] != 29) 333 | s.add(flag[59] & 128 == 0) 334 | s.add(flag[40] % 19 < 19) 335 | s.add(filesize ^ flag[39] != 18) 336 | s.add(filesize ^ flag[45] != 146) 337 | s.add(flag[80] & 128 == 0) 338 | s.add(flag[16] < 134) 339 | s.add(flag[74] > 1) 340 | s.add(flag[23] & 128 == 0) 341 | s.add(flag[32] & 128 == 0) 342 | s.add(filesize ^ flag[47] != 119) 343 | s.add(filesize ^ flag[63] != 135) 344 | s.add(flag[64] > 27) 345 | s.add(flag[82] % 28 < 28) 346 | s.add(flag[32] > 28) 347 | s.add(filesize ^ flag[24] != 217) 348 | s.add(flag[53] < 144) 349 | s.add(flag[29] & 128 == 0) 350 | s.add(flag[52] % 23 < 23) 351 | s.add(filesize ^ flag[76] != 88) 352 | s.add(filesize ^ flag[55] != 17) 353 | s.add(flag[26] & 128 == 0) 354 | s.add(flag[51] > 7) 355 | s.add(flag[12] > 19) 356 | s.add(filesize ^ flag[14] != 99) 357 | s.add(filesize ^ flag[37] != 141) 358 | s.add(filesize ^ flag[14] != 161) 359 | s.add(flag[45] % 17 < 17) 360 | s.add(flag[33] % 25 < 25) 361 | s.add(filesize ^ flag[67] != 55) 362 | s.add(filesize ^ flag[53] != 19) 363 | s.add(flag[30] < 131) 364 | s.add(flag[0] & 128 == 0) 365 | s.add(flag[66] & 128 == 0) 366 | s.add(flag[41] > 5) 367 | s.add(flag[71] & 128 == 0) 368 | s.add(flag[29] % 12 < 12) 369 | s.add(flag[4] < 139) 370 | s.add(flag[77] < 154) 371 | s.add(filesize ^ flag[12] != 116) 372 | s.add(flag[39] > 7) 373 | s.add(flag[75] & 128 == 0) 374 | s.add(flag[78] > 24) 375 | s.add(flag[69] > 25) 376 | s.add(flag[2] + 11 == 119) 377 | s.add(flag[15] < 156) 378 | s.add(filesize ^ flag[69] != 241) 379 | s.add(filesize ^ flag[35] != 18) 380 | s.add(filesize ^ flag[17] != 208) 381 | s.add(filesize ^ flag[4] != 23) 382 | s.add(flag[15] % 16 < 16) 383 | s.add(filesize ^ flag[75] != 35) 384 | s.add(flag[11] % 27 < 27) 385 | s.add(flag[83] & 128 == 0) 386 | s.add(filesize ^ flag[26] != 161) 387 | s.add(flag[49] % 13 < 13) 388 | s.add(filesize ^ flag[18] != 33) 389 | s.add(flag[6] < 155) 390 | s.add(flag[41] < 140) 391 | s.add(filesize ^ flag[68] != 135) 392 | s.add(filesize ^ flag[9] != 5) 393 | s.add(flag[9] & 128 == 0) 394 | s.add(filesize ^ flag[36] != 95) 395 | s.add(flag[7] > 18) 396 | s.add(filesize ^ flag[23] != 242) 397 | s.add(flag[62] < 146) 398 | s.add(flag[49] & 128 == 0) 399 | s.add(flag[62] & 128 == 0) 400 | s.add(flag[4] & 128 == 0) 401 | s.add(filesize ^ flag[58] != 12) 402 | s.add(flag[72] & 128 == 0) 403 | s.add(flag[18] > 13) 404 | s.add(filesize ^ flag[42] != 1) 405 | s.add(flag[59] % 23 < 23) 406 | s.add(flag[53] & 128 == 0) 407 | s.add(filesize ^ flag[78] != 163) 408 | s.add(flag[60] > 14) 409 | s.add(flag[47] % 18 < 18) 410 | s.add(flag[79] > 31) 411 | s.add(flag[22] < 152) 412 | s.add(filesize ^ flag[64] != 50) 413 | s.add(filesize ^ flag[19] != 222) 414 | s.add(flag[81] < 131) 415 | s.add(flag[7] - 15 == 82) 416 | s.add(filesize ^ flag[51] != 204) 417 | s.add(flag[28] > 27) 418 | s.add(filesize ^ flag[61] != 94) 419 | s.add(flag[76] > 2) 420 | s.add(filesize ^ flag[77] != 223) 421 | s.add(flag[19] > 4) 422 | s.add(flag[80] > 2) 423 | s.add(filesize ^ flag[35] != 120) 424 | s.add(filesize ^ flag[22] != 31) 425 | s.add(flag[10] > 9) 426 | s.add(flag[22] > 20) 427 | s.add(flag[38] < 135) 428 | s.add(filesize ^ flag[10] != 205) 429 | s.add(flag[25] & 128 == 0) 430 | s.add(flag[13] < 147) 431 | s.add(flag[42] & 128 == 0) 432 | s.add(filesize ^ flag[48] != 99) 433 | s.add(filesize ^ flag[16] != 7) 434 | s.add(flag[11] < 154) 435 | s.add(filesize ^ flag[76] != 30) 436 | s.add(flag[30] % 15 < 15) 437 | s.add(filesize ^ flag[74] != 193) 438 | s.add(filesize ^ flag[52] != 22) 439 | s.add(filesize ^ flag[36] != 6) 440 | s.add(flag[22] % 22 < 22) 441 | s.add(flag[44] & 128 == 0) 442 | s.add(flag[50] & 128 == 0) 443 | s.add(filesize ^ flag[25] != 224) 444 | s.add(flag[15] > 26) 445 | s.add(filesize ^ flag[60] != 43) 446 | s.add(flag[22] & 128 == 0) 447 | s.add(flag[82] & 128 == 0) 448 | s.add(flag[75] > 30) 449 | s.add(flag[32] % 17 < 17) 450 | s.add(filesize ^ flag[15] != 27) 451 | s.add(flag[67] % 16 < 16) 452 | s.add(flag[23] > 2) 453 | s.add(flag[62] % 13 < 13) 454 | s.add(flag[34] < 138) 455 | s.add(filesize ^ flag[31] != 32) 456 | s.add(flag[72] % 14 < 14) 457 | s.add(filesize ^ flag[81] != 242) 458 | s.add(filesize ^ flag[54] != 141) 459 | s.add(flag[63] & 128 == 0) 460 | s.add(flag[0] < 129) 461 | s.add(flag[70] % 21 < 21) 462 | s.add(flag[8] & 128 == 0) 463 | s.add(flag[61] > 12) 464 | s.add(flag[24] > 22) 465 | s.add(flag[53] % 23 < 23) 466 | s.add(flag[46] & 128 == 0) 467 | s.add(flag[24] % 26 < 26) 468 | s.add(flag[21] - 21 == 94) 469 | s.add(flag[67] < 144) 470 | s.add(flag[48] > 15) 471 | s.add(flag[37] > 16) 472 | s.add(flag[42] < 157) 473 | s.add(flag[16] ^ 7 == 115) 474 | s.add(flag[13] > 21) 475 | s.add(filesize ^ flag[45] != 19) 476 | s.add(flag[47] & 128 == 0) 477 | s.add(filesize ^ flag[80] != 56) 478 | s.add(filesize ^ flag[78] != 6) 479 | s.add(flag[76] % 24 < 24) 480 | s.add(flag[73] < 136) 481 | s.add(filesize ^ flag[52] != 238) 482 | s.add(flag[50] % 11 < 11) 483 | s.add(filesize ^ flag[7] != 15) 484 | s.add(filesize ^ flag[66] != 51) 485 | s.add(flag[59] > 4) 486 | s.add(flag[46] > 22) 487 | s.add(filesize ^ flag[3] != 147) 488 | s.add(flag[63] % 30 < 30) 489 | s.add(flag[36] < 146) 490 | s.add(flag[26] < 132) 491 | s.add(flag[6] & 128 == 0) 492 | s.add(filesize ^ flag[30] != 249) 493 | s.add(filesize ^ flag[5] != 243) 494 | s.add(flag[70] & 128 == 0) 495 | s.add(flag[9] % 22 < 22) 496 | s.add(flag[59] < 141) 497 | s.add(filesize ^ flag[79] != 104) 498 | s.add(filesize ^ flag[5] != 43) 499 | s.add(filesize ^ flag[72] != 219) 500 | s.add(flag[52] > 25) 501 | s.add(flag[74] & 128 == 0) 502 | s.add(flag[28] < 160) 503 | s.add(flag[51] & 128 == 0) 504 | s.add(flag[83] > 16) 505 | s.add(flag[31] > 7) 506 | s.add(flag[84] & 128 == 0) 507 | s.add(filesize ^ flag[46] != 18) 508 | s.add(flag[2] > 20) 509 | s.add(flag[5] < 158) 510 | s.add(filesize ^ flag[32] != 30) 511 | s.add(filesize ^ flag[50] != 219) 512 | s.add(flag[26] - 7 == 25) 513 | s.add(flag[53] > 24) 514 | s.add(flag[77] % 24 < 24) 515 | s.add(flag[3] % 13 < 13) 516 | s.add(filesize ^ flag[9] != 164) 517 | s.add(filesize ^ flag[80] != 236) 518 | s.add(flag[65] % 22 < 22) 519 | s.add(filesize ^ flag[84] != 231) 520 | s.add(filesize ^ flag[49] != 10) 521 | s.add(flag[67] > 27) 522 | s.add(flag[34] % 19 < 19) 523 | s.add(flag[64] & 128 == 0) 524 | s.add(filesize ^ flag[27] != 244) 525 | s.add(flag[12] & 128 == 0) 526 | s.add(flag[51] < 139) 527 | s.add(flag[35] % 15 < 15) 528 | s.add(flag[5] > 14) 529 | s.add(filesize ^ flag[34] != 115) 530 | s.add(filesize ^ flag[38] != 8) 531 | s.add(filesize ^ flag[72] != 37) 532 | s.add(flag[20] & 128 == 0) 533 | s.add(flag[17] < 150) 534 | s.add(filesize ^ flag[70] != 41) 535 | s.add(flag[66] % 16 < 16) 536 | s.add(flag[17] & 128 == 0) 537 | s.add(flag[19] & 128 == 0) 538 | s.add(filesize ^ flag[33] != 157) 539 | s.add(flag[21] > 7) 540 | s.add(flag[58] & 128 == 0) 541 | s.add(flag[71] < 130) 542 | s.add(flag[41] & 128 == 0) 543 | s.add(flag[57] > 11) 544 | s.add(filesize ^ flag[8] != 2) 545 | s.add(filesize ^ flag[57] != 186) 546 | s.add(flag[11] & 128 == 0) 547 | s.add(flag[2] < 147) 548 | s.add(flag[23] % 16 < 16) 549 | s.add(flag[78] < 141) 550 | s.add(flag[38] > 18) 551 | s.add(filesize ^ flag[41] != 233) 552 | s.add(flag[18] < 137) 553 | s.add(flag[40] & 128 == 0) 554 | s.add(filesize ^ flag[21] != 188) 555 | s.add(filesize ^ flag[57] != 14) 556 | s.add(filesize ^ flag[4] != 253) 557 | s.add(flag[14] < 153) 558 | s.add(flag[31] & 128 == 0) 559 | s.add(flag[81] > 11) 560 | s.add(flag[2] & 128 == 0) 561 | s.add(filesize ^ flag[22] != 191) 562 | s.add(flag[44] > 5) 563 | s.add(flag[84] + 3 == 128) 564 | s.add(flag[20] < 135) 565 | s.add(filesize ^ flag[73] != 61) 566 | s.add(filesize ^ flag[26] != 44) 567 | s.add(flag[1] < 158) 568 | s.add(filesize ^ flag[29] != 158) 569 | s.add(flag[49] < 129) 570 | s.add(filesize ^ flag[64] != 158) 571 | s.add(flag[25] < 154) 572 | s.add(flag[63] < 129) 573 | s.add(flag[84] > 26) 574 | s.add(flag[39] & 128 == 0) 575 | s.add(flag[25] > 27) 576 | s.add(flag[49] > 27) 577 | s.add(flag[9] > 23) 578 | s.add(filesize ^ flag[7] != 221) 579 | s.add(flag[50] < 138) 580 | s.add(flag[76] < 156) 581 | s.add(filesize ^ flag[61] != 239) 582 | s.add(flag[57] % 27 < 27) 583 | s.add(filesize ^ flag[8] != 107) 584 | s.add(flag[79] < 146) 585 | s.add(filesize ^ flag[40] != 49) 586 | s.add(flag[0] > 30) 587 | s.add(flag[45] > 17) 588 | s.add(flag[16] % 31 < 31) 589 | s.add(filesize ^ flag[1] != 232) 590 | s.add(filesize ^ flag[56] != 22) 591 | s.add(flag[42] > 3) 592 | s.add(flag[52] & 128 == 0) 593 | s.add(flag[69] % 30 < 30) 594 | s.add(flag[55] < 153) 595 | s.add(filesize ^ flag[41] != 74) 596 | s.add(filesize ^ flag[1] != 0) 597 | s.add(filesize ^ flag[44] != 96) 598 | s.add(filesize ^ flag[58] != 77) 599 | s.add(flag[34] > 18) 600 | s.add(flag[8] > 3) 601 | 602 | 603 | while s.check() == sat: 604 | cond = [] 605 | out = "" 606 | m = s.model() 607 | 608 | for c in flag: 609 | out += chr(m[c].as_long()) 610 | cond.append(c != m[c].as_long()) 611 | s.add(Or(cond)) 612 | 613 | print([out]) 614 | 615 | 616 | -------------------------------------------------------------------------------- /flare-on_11/ch04/README.md: -------------------------------------------------------------------------------- 1 | ## 4. FLARE Meme Maker 3000 2 | 3 | stare, manually deobfuscate the end of the code, figure where the flag trigger condition is, copy paste in console 4 | 5 | ```javascript 6 | t = a0p; 7 | a = Object[t(0x59c5)](a0e)[0x5]; 8 | b = a0c[0xe]; 9 | c = a0c[a0c[t(0x1544d)] - 0x1 ]; 10 | d = a0c[0x16]; 11 | 12 | var f = d[0x3] + 'h' + a[0xa] + b[0x2] + a[0x3] + c[0x5] + c[c[t(0x1544d)] - 0x1] + '5' + a[0x3] + '4' + a[0x3] + c[0x2] + c[0x4] + c[0x3] + '3' + d[0x2] + a[0x3] + 'j4' + a0c[0x1][0x2] + d[0x4] + '5' + c[0x2] + d[0x5] + '1' + c[0xb] + '7' + a0c[0x15][0x1] + b[t(0x15e39) + 'e']('\x20', '-') + a[0xb] + a0c[0x4][t(0x9a82) + t(0x1656b)](0xc, 0xf); 13 | 14 | f = f[t(0x143fc) + t(0x8c67)](),console.log(atob(t(0x14e2b) + t(0x4c22) + 'YXRpb2' + t(0x1708e) + t(0xaa98) + t(0x16697) + t(0x109c4)) + f); 15 | ``` 16 | 17 | ``` 18 | Congratulations! Here you go: wh0a_it5_4_cru3l_j4va5cr1p7@flare-on.com 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /flare-on_11/ch05/README.md: -------------------------------------------------------------------------------- 1 | ## 5. sshd 2 | 3 | - extract and decrypt shellcode: [extract_shellcode.py](extract_shellcode.py) 4 | - get key using: [emu.py](emu.py) 5 | -------------------------------------------------------------------------------- /flare-on_11/ch05/emu.py: -------------------------------------------------------------------------------- 1 | 2 | from unicorn import * 3 | from unicorn.x86_const import * 4 | from capstone import * 5 | from capstone.x86 import * 6 | 7 | 8 | def disas_single(code, addr): 9 | md = Cs(CS_ARCH_X86, CS_MODE_64) 10 | md.detail = True 11 | for i in md.disasm(code, addr): 12 | print(" >> 0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 13 | return 14 | 15 | 16 | def hook_code(mu, addr, size, user_data): 17 | mem = mu.mem_read(addr, size) 18 | disas_single(mem, addr) 19 | 20 | 21 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 22 | mu.mem_map(0x00000, 0x1000) 23 | mu.mem_map(0x700000, 0x10000) 24 | 25 | mu.mem_write(0x00000, open("shellcode.1", "rb").read()) 26 | 27 | 28 | 29 | 30 | rbp = 0x704000 31 | 32 | mu.reg_write(UC_X86_REG_RBP, rbp) 33 | mu.reg_write(UC_X86_REG_RSP, 0x701000) 34 | 35 | 36 | content = rbp - 0x1148 37 | key = rbp - 0x1278 38 | nonce = rbp - 0x1258 39 | 40 | mu.mem_write(key, b'\x8d\xec\x91\x12\xeb\x76\x0e\xda\x7c\x7d\x87\xa4\x43\x27\x1c\x35\xd9\xe0\xcb\x87\x89\x93\xb4\xd9\x04\xae\xf9\x34\xfa\x21\x66\xd7') 41 | mu.mem_write(nonce, b'\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11') 42 | mu.mem_write(content, b'\xa9\xf6\x34\x08\x42\x2a\x9e\x1c\x0c\x03\xa8\x08\x94\x70\xbb\x8d\xaa\xdc\x6d\x7b\x24\xff\x7f\x24\x7c\xda\x83\x9e\x92\xf7\x07\x1d\x02\x63\x90\x2e\xc1\x58') 43 | 44 | #mu.hook_add(UC_HOOK_CODE, hook_code) 45 | 46 | mu.emu_start(0x00000 + 0xe7f, 0x00000 + 0xecf) 47 | 48 | #print(mu.mem_read(0x700000, 0x10000).replace(b'\x00', b'')) 49 | print(mu.mem_read(content, 0x80)) 50 | -------------------------------------------------------------------------------- /flare-on_11/ch05/extract_shellcode.py: -------------------------------------------------------------------------------- 1 | 2 | from Crypto.Cipher import ChaCha20 3 | from pwn import p32 4 | 5 | offset = 0x23960 6 | size = 0xf96 7 | 8 | 9 | #key_marker = 0xc5407a48 10 | 11 | """ 12 | pwndbg> search -t dword 0xc5407a48 13 | Searching for value: b'Hz@\xc5' 14 | load7 0x55b46d51dde0 0x38f63d94c5407a48 15 | load7 0x55b46d58ca40 0x38f63d94c5407a48 16 | load7 0x55b46d58da0f 0x38f63d94c5407a48 17 | load7 0x55b46d58dd34 0x38f63d94c5407a48 18 | pwndbg> x/16dx 0x55b46d51dde0 19 | 0x55b46d51dde0: 0xc5407a48 0x38f63d94 0xe21318a8 0xa51863de 20 | 0x55b46d51ddf0: 0xbaa0f907 0x7b8abb2d 0xd06636a6 0x5ea6118d 21 | 0x55b46d51de00: 0x6fd614c9 0x9f8336f2 0x1a71cd4d 0x55298652 22 | 0x55b46d51de10: 0xb7d15858 0x0dc2a7f9 0x190ede36 0x9605a3ea 23 | """ 24 | 25 | key = [0x38f63d94, 0xe21318a8, 0xa51863de, 0xbaa0f907, 26 | 0x7b8abb2d, 0xd06636a6, 0x5ea6118d, 0x6fd614c9] 27 | 28 | nonce = [0x9f8336f2, 0x1a71cd4d, 0x55298652] 29 | 30 | key_ = b''.join([p32(_) for _ in key]) 31 | nonce_ = b''.join([p32(_) for _ in nonce]) 32 | 33 | print(key_.hex()) 34 | print(nonce_.hex()) 35 | 36 | 37 | 38 | with open("liblzma.so.5.4.1", "rb") as fp: 39 | fp.seek(offset) 40 | data = fp.read(size) 41 | 42 | 43 | c = ChaCha20.new(key=key_, nonce=nonce_) 44 | 45 | print(data.hex()) 46 | with open("shellcode.1", "wb") as fp: 47 | fp.write(c.decrypt(data)) 48 | -------------------------------------------------------------------------------- /flare-on_11/ch05/liblzma.so.5.4.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_11/ch05/liblzma.so.5.4.1 -------------------------------------------------------------------------------- /flare-on_11/ch06/README.md: -------------------------------------------------------------------------------- 1 | ## 6. bloke2 2 | 3 | Couldn't trigger the test mode, but figured out it was just doing some xor on the flag, so i tried all kind of xors :) 4 | -------------------------------------------------------------------------------- /flare-on_11/ch06/slv.py: -------------------------------------------------------------------------------- 1 | from pwn import xor 2 | 3 | hs = [ 4 | "3643e6e6054190369109e8ae8bd92ccac59fd49b273ac4ec4e7c59e2f5d73c277de64526d3aa9b32e4d9ac0cf09e33f1b953d6ec3737339526fb41da0981d637", 5 | "37d68109da41fb2695333737ecd653b9f1339ef00cacd9e4329baad32645e67d273cd7f5e2597c4eecc43a279bd49fc5ca2cd98baee8099136904105e6e64336", 6 | "d7c0cf2d4de0e6554335e8778d9705cb0e4524b70f1adaa0e2b730ce92bd3a9210fd6752778adc6971e48b5b3e23fba379e1a042c2cc387916160f0f5d71bd18", 7 | "18bd715d0f0f16167938ccc242a0e179a3fb233e5b8be47169dc8a775267fd10923abd92ce30b7e2a0da1a0fb724450ecb05978d77e8354355e6e04d2dcfc0d7", 8 | "51f39383b141688a26ea6d793315bbfe30de9f8689fa95656ad55fb143beef9cd3a8781ec867769624dba3c97027b39f1688dbd0f419bcb4337328112bcfd230", 9 | "30d2cf2b11287333b4bc19f4d0db88169fb32770c9a3db24967667c81e78a8d39cefbe43b15fd56a6595fa89869fde30febb1533796dea268a6841b18393f351" 10 | ] 11 | 12 | kk = bytes.fromhex("3c9cf0addf2e45ef548b011f736cc99144bdfee0d69df4090c8a39c520e18ec3bdc1277aad1706f756affca41178dac066e4beb8ab7dd2d1402c4d624aaabe40") 13 | 14 | for h in hs: 15 | h = bytes.fromhex(h) 16 | print(h.hex()) 17 | print(xor(h, kk)) 18 | print(xor(h[::-1], kk)[::-1]) 19 | -------------------------------------------------------------------------------- /flare-on_11/ch07/README.md: -------------------------------------------------------------------------------- 1 | ## 7. fullspeed 2 | 3 | - bleed from the eyes 4 | - use OllyDumpEx to dump the binary once it's all "hydrated" 5 | - compile some .NET AOT hello world with bouncy castle 6 | - generate sigs 7 | - cry 8 | - make a [C2](c2_working.py) to verify the reverse is good 9 | - harass ChatGPT until it spits [solve_script.sage](solve_script.sage) out because i'm too dumb for maths 10 | - [decrypt](decrypt.py) the pcap 11 | -------------------------------------------------------------------------------- /flare-on_11/ch07/c2_working.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | from ecpy.curves import Curve, Point, WeierstrassCurve 4 | from ecpy.keys import ECPublicKey, ECPrivateKey 5 | from Crypto.Util.number import getRandomInteger, bytes_to_long, long_to_bytes 6 | from Crypto.Cipher import ChaCha20 7 | from hashlib import sha512 8 | 9 | 10 | def xor(number): 11 | return number ^ 0x133713371337133713371337133713371337133713371337133713371337133713371337133713371337133713371337 12 | 13 | curve_def = { 14 | 'name': "flare", 15 | 'type': "weierstrass", 16 | 'size': 384, 17 | 'field': 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd, 18 | 'generator': (0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8, 19 | 0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182), 20 | 'order': 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e547761ec3ea549979d50c95478998110005c8c2b7f3498ee71, 21 | 'cofactor': 1, 22 | 'a': 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f, 23 | 'b': 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380 24 | } 25 | 26 | 27 | fc = WeierstrassCurve(curve_def) 28 | 29 | # random private key on the curve 30 | privKey = ECPrivateKey(getRandomInteger(128), fc) 31 | 32 | # derive public key from the private one 33 | pubKey = privKey.get_public_key() 34 | 35 | print(privKey) 36 | print(pubKey) 37 | 38 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 39 | s.bind(("0.0.0.0", 31337)) 40 | s.listen() 41 | 42 | while True: 43 | conn, addr = s.accept() 44 | with conn: 45 | print(f"Connected by {addr}") 46 | 47 | # 48 | # receive client's pubkey (x, y) 49 | rem_x = xor(bytes_to_long(conn.recv(48))) 50 | rem_y = xor(bytes_to_long(conn.recv(48))) 51 | 52 | print(hex(rem_x)) 53 | print(hex(rem_y)) 54 | 55 | # 56 | # send out public key x and y 57 | conn.send(long_to_bytes(xor(pubKey.W.x))) 58 | conn.send(long_to_bytes(xor(pubKey.W.y))) 59 | 60 | # 61 | # Curve generator * Point(client_x, client_y) 62 | remote_pubKey = ECPublicKey(Point(rem_x, rem_y, fc)) 63 | meh = remote_pubKey.W * privKey.d 64 | 65 | print(meh) 66 | 67 | # 68 | # sha512(meh.x) is our chacha20 key and iv 69 | # 70 | h = sha512(long_to_bytes(meh.x)).digest() 71 | key = h[:32] 72 | nonce = b'\x00\x00\x00\x00' + h[32:32+8] 73 | print("key: %s"%key.hex()) 74 | print("nonce: %s"%nonce.hex()) 75 | 76 | c = ChaCha20.new(key=key, nonce=nonce) 77 | 78 | conn.send(c.encrypt(b'verify\x00')) 79 | 80 | print("bye") 81 | -------------------------------------------------------------------------------- /flare-on_11/ch07/decrypt.py: -------------------------------------------------------------------------------- 1 | from ecpy.curves import Curve, Point, WeierstrassCurve 2 | from ecpy.keys import ECPublicKey, ECPrivateKey 3 | from Crypto.Util.number import getRandomInteger, bytes_to_long, long_to_bytes 4 | from Crypto.Cipher import ChaCha20 5 | from hashlib import sha512 6 | 7 | 8 | def xor(number): 9 | return number ^ 0x133713371337133713371337133713371337133713371337133713371337133713371337133713371337133713371337 10 | 11 | curve_def = { 12 | 'name': "flare", 13 | 'type': "weierstrass", 14 | 'size': 384, 15 | 'field': 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd, 16 | 'generator': (0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8, 17 | 0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182), 18 | 'order': 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e547761ec3ea549979d50c95478998110005c8c2b7f3498ee71, 19 | 'cofactor': 1, 20 | 'a': 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f, 21 | 'b': 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380 22 | } 23 | 24 | 25 | fc = WeierstrassCurve(curve_def) 26 | 27 | # random private key on the curve 28 | privKey = ECPrivateKey(168606034648973740214207039875253762473, fc) 29 | 30 | # derive public key from the private one 31 | pubKey = privKey.get_public_key() 32 | 33 | r_x = xor(0xa0d2eba817e38b03cd063227bd32e353880818893ab02378d7db3c71c5c725c6bba0934b5d5e2d3ca6fa89ffbb374c31) 34 | r_y = xor(0x96a35eaf2a5e0b430021de361aa58f8015981ffd0d9824b50af23b5ccf16fa4e323483602d0754534d2e7a8aaf8174dc) 35 | 36 | remote_pubKey = ECPublicKey(Point(r_x, r_y, fc)) 37 | meh = remote_pubKey.W * privKey.d 38 | h = sha512(long_to_bytes(meh.x)).digest() 39 | key = h[:32] 40 | nonce = b'\x00\x00\x00\x00' + h[32:32+8] 41 | print("key: %s"%key.hex()) 42 | print("nonce: %s"%nonce.hex()) 43 | 44 | c = ChaCha20.new(key=key, nonce=nonce) 45 | 46 | data = [ 47 | "f272d54c31860f", 48 | "3fbd43da3ee325", 49 | "86dfd7", 50 | "c50cea1c4aa064c35a7f6e3ab0258441ac1585c36256dea83cac93007a0c3a29864f8e285ffa79c8eb43976d5b587f8f35e699547116", 51 | "fcb1d2cdbba979c989998c", 52 | "61490b", 53 | "ce39da", 54 | "577011e0d76ec8eb0b8259331def13ee6d86723eac9f0428924ee7f8411d4c701b4d9e2b3793f6117dd30dacba", 55 | "2cae600b5f32cea193e0de63d709838bd6", 56 | "a7fd35", 57 | "edf0fc", 58 | "802b15186c7a1b1a475daf94ae40f6bb81afcedc4afb158a5128c28c91cd7a8857d12a661acaec", 59 | "aec8d27a7cf26a17273685", 60 | "35a44e", 61 | "2f3917", 62 | "ed09447ded797219c966ef3dd5705a3c32bdb1710ae3b87fe66669e0b4646fc416c399c3a4fe1edc0a3ec5827b84db5a79b81634e7c3afe528a4da15457b637815373d4edcac2159d056", 63 | "f5981f71c7ea1b5d8b1e5f06fc83b1def38c6f4e694e3706412eabf54e3b6f4d19e8ef46b04e399f2c8ece8417fa", 64 | "4008bc", 65 | "54e41e", 66 | "f701fee74e80e8dfb54b487f9b2e3a277fa289cf6cb8df986cdd387e342ac9f5286da11ca2784084", 67 | "5ca68d1394be2a4d3d4d7c82e5", 68 | "31b6dac62ef1ad8dc1f60b79265ed0deaa31ddd2d53aa9fd9343463810f3e2232406366b48415333d4b8ac336d4086efa0f15e6e59", 69 | "0d1ec06f36", 70 | ] 71 | 72 | for msg in data: 73 | msg = bytes.fromhex(msg) 74 | print(c.decrypt(msg)) 75 | 76 | -------------------------------------------------------------------------------- /flare-on_11/ch07/solve_script.sage: -------------------------------------------------------------------------------- 1 | # Define the elliptic curve parameters and the generator point 2 | p = 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd # Field prime 3 | a = 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f # Curve parameter a 4 | b = 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380 # Curve parameter b 5 | 6 | E = EllipticCurve(GF(p), [a, b]) 7 | 8 | # Generator point G 9 | G = E(0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8, 10 | 0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182) 11 | 12 | # Public key PA 13 | PA = E(0x195b46a760ed5a425dadcab37945867056d3e1a50124fffab78651193cea7758d4d590bed4f5f62d4a291270f1dcf499, 14 | 0x357731edebf0745d081033a668b58aaa51fa0b4fc02cd64c7e8668a016f0ec1317fcac24d8ec9f3e75167077561e2a15) 15 | 16 | # The prime factors of the order of the curve (provided by you) 17 | small_factors = [35809, 46027, 56369, 57301, 65063, 111659, 113111] 18 | 19 | # The large prime factor 20 | large_factor = 7072010737074051173701300310820071551428959987622994965153676442076542799542912293 21 | 22 | # Total curve order is the product of these factors 23 | curve_order = prod(small_factors) * large_factor 24 | print(hex(curve_order)) 25 | 26 | # Step 1: Pohlig-Hellman for each small prime factor 27 | mod_solutions = [] # To store solutions d_A mod p_i 28 | moduli = [] # To store the moduli (the prime factors) 29 | 30 | for q in small_factors: 31 | print("step1: %d"%q) 32 | # Define the subgroup order for each factor q 33 | order_q = curve_order // q 34 | 35 | # Multiply the public key and generator by the cofactor (curve_order // q) 36 | PA_q = order_q * PA 37 | G_q = order_q * G 38 | 39 | # Solve the discrete logarithm mod q using Pohlig-Hellman 40 | d_A_mod_q = discrete_log(PA_q, G_q, operation="+") 41 | 42 | # Store the result for the CRT 43 | mod_solutions.append(d_A_mod_q) 44 | moduli.append(q) 45 | 46 | # Step 2: Use Chinese Remainder Theorem to combine solutions 47 | d_A_mod_P = crt(mod_solutions, moduli) 48 | print("d_A_mod_P = %s"%d_A_mod_P) 49 | 50 | # Step 3: Brute force search for the full private key 51 | # P is the product of small prime factors 52 | P = prod(small_factors) 53 | 54 | # Known size of the private key is 128 bits 55 | max_d = 2^128 56 | 57 | # Upper bound on x in the equation: d_A = x * P + d_A_mod_P 58 | upper_bound = (max_d - d_A_mod_P) // P 59 | 60 | # Step 4: Brute force search for the correct x 61 | print("bruteforcing...") 62 | for x in range(upper_bound + 1): 63 | # Compute candidate private key d_A 64 | d_A = x * P + d_A_mod_P 65 | 66 | # Check if d_A * G equals PA (i.e., check if this is the correct private key) 67 | if d_A * G == PA: 68 | print(f"Found private key: {d_A}") 69 | break 70 | else: 71 | print("Private key not found in the search range.") 72 | 73 | -------------------------------------------------------------------------------- /flare-on_11/ch09/README.md: -------------------------------------------------------------------------------- 1 | ## 9. Serpentine 2 | 3 | Supporting code and data for the [full writeup](https://matth.dmz42.org/posts/2024/flare-on_11_9_serpentine/) 4 | 5 | 6 | - For the emulator, you need unicorn 2.0.1 - there's a caching issue with 2.1.0+ 7 | - For Triton, you need python 3.11 8 | 9 | if you use [anaconda](https://www.anaconda.com/), this should quickly get you sorted: 10 | ``` 11 | conda create -n "fl311" python=3.11.10 12 | conda activate fl311 13 | pip install -r requirements.txt 14 | ``` 15 | 16 | TL;DR: run this to get flag: 17 | ``` 18 | mkdir stages 19 | python ../emu_v7.py 20 | cat stages/* > stages/full.bin 21 | python ../triton_solver.py 22 | ``` 23 | -------------------------------------------------------------------------------- /flare-on_11/ch09/emu_v4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # this gives interesting results: 4 | # python emu_v2.py 2>/dev/null| grep -v rip | grep -Pv 'push|pop|lea|mov *rax, 0|call|ret' 5 | # 6 | # https://doxygen.reactos.org/d8/d2f/unwind_8c_source.html 7 | import io 8 | import sys 9 | from enum import Enum 10 | from unicorn import * 11 | from unicorn.x86_const import * 12 | from capstone import * 13 | from capstone.x86 import * 14 | import hexdump 15 | import pefile 16 | from malduck import p64, p32, p16, u64, u32, u16 17 | 18 | DEBUG = True 19 | 20 | class UWND_CODE(Enum): 21 | UWOP_PUSH_NONVOL = 0 22 | UWOP_ALLOC_LARGE = 1 23 | UWOP_ALLOC_SMALL = 2 24 | UWOP_SET_FPREG = 3 25 | UWOP_SAVE_NONVOL = 4 26 | UWOP_SAVE_NONVOL_FAR = 5 27 | UWOP_EPILOG = 6 28 | UWOP_SPARE_CODE = 7 29 | UWOP_SAVE_XMM128 = 8 30 | UWOP_SAVE_XMM128_FAR = 9 31 | UWOP_PUSH_MACHFRAME = 10 32 | 33 | class REGS(Enum): 34 | RAX = 0 35 | RCX = 1 36 | RDX = 2 37 | RBX = 3 38 | RSP = 4 39 | RBP = 5 40 | RSI = 6 41 | RDI = 7 42 | R8 = 8 43 | R9 = 9 44 | R10 = 10 45 | R11 = 11 46 | R12 = 12 47 | R13 = 13 48 | R14 = 14 49 | R15 = 15 50 | RIP = 16 51 | 52 | UNICORN_REGS = { 53 | REGS.RAX: UC_X86_REG_RAX, 54 | REGS.RCX: UC_X86_REG_RCX, 55 | REGS.RDX: UC_X86_REG_RDX, 56 | REGS.RBX: UC_X86_REG_RBX, 57 | REGS.RSP: UC_X86_REG_RSP, 58 | REGS.RBP: UC_X86_REG_RBP, 59 | REGS.RSI: UC_X86_REG_RSI, 60 | REGS.RDI: UC_X86_REG_RDI, 61 | REGS.R8: UC_X86_REG_R8, 62 | REGS.R9: UC_X86_REG_R9, 63 | REGS.R10: UC_X86_REG_R10, 64 | REGS.R11: UC_X86_REG_R11, 65 | REGS.R12: UC_X86_REG_R12, 66 | REGS.R13: UC_X86_REG_R13, 67 | REGS.R14: UC_X86_REG_R14, 68 | REGS.R15: UC_X86_REG_R15, 69 | REGS.RIP: UC_X86_REG_RIP 70 | } 71 | 72 | def disas_single(code, addr): 73 | md = Cs(CS_ARCH_X86, CS_MODE_64) 74 | md.detail = True 75 | for i in md.disasm(code, addr): 76 | return i 77 | 78 | def dbg(line): 79 | if DEBUG: 80 | print(line) 81 | 82 | def dump_context(mu): 83 | if not DEBUG: 84 | return 85 | return 86 | def r(r): 87 | return mu.reg_read(r) 88 | print(" rax: 0x%016x rcx: 0x%016x rdx: 0x%016x rbx: 0x%016x"%(r(UC_X86_REG_RAX), r(UC_X86_REG_RCX), r(UC_X86_REG_RDX), r(UC_X86_REG_RBX))) 89 | print(" rsp: 0x%016x rbp: 0x%016x rsi: 0x%016x rdi: 0x%016x"%(r(UC_X86_REG_RSP), r(UC_X86_REG_RBP), r(UC_X86_REG_RSI), r(UC_X86_REG_RDI))) 90 | print(" r8: 0x%016x r9: 0x%016x r10: 0x%016x r11: 0x%016x"%(r(UC_X86_REG_R8), r(UC_X86_REG_R9), r(UC_X86_REG_R10), r(UC_X86_REG_R11))) 91 | print(" r12: 0x%016x r13: 0x%016x r14: 0x%016x r15: 0x%016x"%(r(UC_X86_REG_R12), r(UC_X86_REG_R13), r(UC_X86_REG_R14), r(UC_X86_REG_R15))) 92 | print(" mxcsr: 0x%08x"%r(UC_X86_REG_MXCSR)) 93 | 94 | return 95 | rsp = r(UC_X86_REG_RSP) 96 | for x in range(10): 97 | print("rsp+%x 0x%x"%(x*8, u64(mu.mem_read(rsp + x*8, 8)))) 98 | 99 | 100 | 101 | class Context: 102 | # fuckers are using MxCsr reg 103 | # >> 0x69b08eb mov r11d, dword ptr [rbx + 0x34] 104 | def __init__(self, mu): 105 | # addr = address of DISPARTCHER_CONTEXT 106 | dbg("saving context...") 107 | self.mu = mu 108 | self.regs = {} 109 | self.mxcsr = None 110 | 111 | for reg in REGS: 112 | self.regs[reg] = mu.reg_read(UNICORN_REGS[reg]) 113 | self.mxcsr = mu.reg_read(UC_X86_REG_MXCSR) 114 | 115 | 116 | def to_memory(self, addr): 117 | # addr is DISPATCHER_CONTEXT address 118 | dbg("context to mem...") 119 | context_addr = addr + 0x1000 # arbitrary addr for CONTEXT 120 | offset = 0x78 # start of regs in the struct 121 | 122 | # write CONTEXT struct 123 | for n, reg in enumerate(REGS): 124 | self.mu.mem_write(context_addr + offset + n*8, p64(self.regs[reg])) 125 | # write MxCsr 126 | self.mu.mem_write(context_addr + 0x34, p32(self.mxcsr)) 127 | 128 | # fill in pointer in DISPATCHER_CONTEXT 129 | self.mu.mem_write(addr + 0x28, p64(context_addr)) 130 | 131 | def dump(self): 132 | print("CTX rax: 0x%016x rcx: 0x%016x rdx: 0x%016x rbx: 0x%016x"%(self.regs[REGS.RAX], self.regs[REGS.RCX], 133 | self.regs[REGS.RDX], self.regs[REGS.RBX])) 134 | print("CTX rsp: 0x%016x rbp: 0x%016x rsi: 0x%016x rdi: 0x%016x"%(self.regs[REGS.RSP], self.regs[REGS.RBP], 135 | self.regs[REGS.RSI], self.regs[REGS.RDI])) 136 | print("CTX r8: 0x%016x r9: 0x%016x r10: 0x%016x r11: 0x%016x"%(self.regs[REGS.R8], self.regs[REGS.R9], 137 | self.regs[REGS.R10], self.regs[REGS.R11])) 138 | print("CTX r12: 0x%016x r13: 0x%016x r14: 0x%016x r15: 0x%016x"%(self.regs[REGS.R12], self.regs[REGS.R13], 139 | self.regs[REGS.R14], self.regs[REGS.R15])) 140 | print("CTX: rip: 0x%016x mxcsr: 0x%08x"%(self.regs[REGS.RIP], self.mxcsr)) 141 | 142 | 143 | 144 | 145 | 146 | 147 | class Unwinder: 148 | def __init__(self, mu, unwind_addr, ctx): 149 | self.mu = mu # unicorn 150 | self.addr = unwind_addr # start of UNWIND_INFO 151 | self.ctx = ctx 152 | self.n_codes = None # number of codes 153 | self.codes = None # raw codes bytes 154 | 155 | 156 | unwind_info = mu.mem_read(unwind_addr, 4) 157 | unwind_addr += 4 158 | 159 | flag = unwind_info[0] >> 3 160 | self.n_codes = unwind_info[2] 161 | dbg("flag: %d / opcodes: %d"%(flag, self.n_codes)) 162 | 163 | self.frame_reg = unwind_info[3] & 0xf 164 | self.frame_offset = unwind_info[3] >> 4 165 | 166 | 167 | # if CountOfCodes is uneven, read one more short word 168 | codes_size = 2 * (( self.n_codes + 1) &~1) 169 | self.codes = io.BytesIO(mu.mem_read(unwind_addr, codes_size)) 170 | unwind_addr += codes_size 171 | 172 | self.exception_handler = u32(mu.mem_read(unwind_addr, 4)) 173 | dbg("exception_handler: %s"%(hex(self.exception_handler))) 174 | 175 | 176 | def mem_read(self, addr): 177 | # read a QWORD at addr 178 | return u64(self.mu.mem_read(addr, 8)) 179 | 180 | 181 | def do_unwind(self): 182 | # https://github.com/google/orbit/blob/02f72c7311ed08e6418ecbfab4a457db67aa38d9/third_party/libunwindstack/PeCoffUnwindInfoUnwinderX86_64.cpp 183 | # https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64?view=msvc-170 184 | node = 0 185 | while node < self.n_codes: 186 | unwind_code = self.codes.read(2) 187 | node += 1 188 | 189 | unwind_op_code = UWND_CODE(unwind_code[1] & 0xf ) 190 | unwind_op_info = unwind_code[1] >> 4 191 | 192 | dbg("unwind_op_code: %r"%unwind_op_code) 193 | dbg("unwind_op_info: %d"%unwind_op_info) 194 | 195 | match unwind_op_code: 196 | 197 | case UWND_CODE.UWOP_PUSH_MACHFRAME: 198 | # https://github.com/azakharchenko-msol/wine/commit/400520192284c34e5b34b52b657cd3dda084403f 199 | offset = 0x20 if unwind_op_info == 1 else 0x18 200 | print(" setting rsp to 0x%08x"%self.mem_read(self.ctx.regs[REGS.RSP] + offset)) 201 | self.ctx.regs[REGS.RSP] = self.mem_read(self.ctx.regs[REGS.RSP] + offset) 202 | 203 | case UWND_CODE.UWOP_PUSH_NONVOL: 204 | reg = REGS(unwind_op_info) 205 | 206 | # check for input reading 207 | if 0x14089b8e8 <= self.ctx.regs[REGS.RSP] < 0x14089b8e8+0x20: 208 | print(" reading 8 bytes of input @ offset %d into %r"%(self.ctx.regs[REGS.RSP] - 0x14089b8e8, self.ctx.regs[reg])) 209 | 210 | v = self.mem_read(self.ctx.regs[REGS.RSP]) # v = [rsp] 211 | print(" PUSH %r = 0x%x (from: rsp=0x%x)"%(reg, v, self.ctx.regs[REGS.RSP])) 212 | self.ctx.regs[reg] = v # regX = v 213 | self.ctx.regs[REGS.RSP] += 8 # rsp += 8 214 | 215 | case UWND_CODE.UWOP_ALLOC_LARGE: 216 | # alloc size = next slot * 8 217 | if unwind_op_info == 0: 218 | size = u16(self.codes.read(2)) * 8 219 | node += 1 220 | # alloc size = next 2 slot 221 | elif unwind_op_info == 1: 222 | size = u32(self.codes.read(4)) 223 | node += 2 224 | self.ctx.regs[REGS.RSP] += size 225 | print(" ALLOC %d"%size) 226 | 227 | case UWND_CODE.UWOP_ALLOC_SMALL: 228 | size = unwind_op_info * 8 + 8 229 | self.ctx.regs[REGS.RSP] += size 230 | print(" ALLOC %d"%size) 231 | 232 | case UWND_CODE.UWOP_SET_FPREG: 233 | self.ctx.regs[REGS.RSP] = self.ctx.regs[REGS(self.frame_reg)] 234 | self.ctx.regs[REGS.RSP] -= self.frame_offset * 16 235 | print(" rsp = 0x%x off=0x%x reg=%r"%(self.ctx.regs[REGS.RSP], self.frame_offset, REGS(self.frame_reg))) 236 | 237 | case _: 238 | raise 239 | 240 | if DEBUG: self.ctx.dump() 241 | 242 | 243 | 244 | 245 | 246 | # maybe a good place to return the exception handler offset 247 | return self.exception_handler 248 | 249 | 250 | 251 | 252 | 253 | class Emu: 254 | def __init__(self): 255 | # load PE 256 | self.sections = {} 257 | 258 | pe = pefile.PE('serpentine.exe') 259 | for section in pe.sections: 260 | name = section.Name.rstrip(b'\x00').decode() 261 | self.sections[name] = section 262 | 263 | def log_instruction(self, mu, i): 264 | dump_context(mu) 265 | print(">> 0x%x\t%s\t%s"%(i.address, i.mnemonic, i.op_str)) 266 | 267 | def hook_code(self, mu, addr, size, user_data): 268 | mem = mu.mem_read(addr, size) 269 | dis = disas_single(mem, addr) 270 | 271 | self.log_instruction(mu, dis) 272 | 273 | # 274 | # maaaagic 275 | # 276 | if dis.mnemonic == "hlt": 277 | self.handle_exception(mu, addr) 278 | 279 | 280 | 281 | 282 | def handle_exception(self, mu, addr): 283 | ctx = Context(mu) # initialize context 284 | 285 | # how many bytes to skip before findind the unwind_handle 286 | # byte at faulty rip + 1 287 | unwind_addr = mu.mem_read(addr + 1, 1)[0] + addr + 2 288 | # alignment 289 | if unwind_addr & 1: 290 | unwind_addr += 1 291 | 292 | if DEBUG: 293 | ctx.dump() 294 | 295 | unwinder = Unwinder(mu, unwind_addr, ctx) 296 | 297 | 298 | exception_handler_addr = unwinder.do_unwind() 299 | exception_handler_addr += self.code_addr 300 | 301 | if DEBUG: 302 | ctx.dump() 303 | 304 | print("HANDLER ADDR: 0x%x"%exception_handler_addr) 305 | 306 | # save CONTEXT to memory 307 | ctx.to_memory(self.dis_ctx_addr) 308 | 309 | # 310 | # setup call context 311 | # we might need a "call rax" trampoline instead 312 | # 313 | # https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64?view=msvc-170 314 | # PARAMS 315 | # typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) ( 316 | # IN PEXCEPTION_RECORD ExceptionRecord, 317 | # IN ULONG64 EstablisherFrame, 318 | # IN OUT PCONTEXT ContextRecord, 319 | # IN OUT PDISPATCHER_CONTEXT DispatcherContext 320 | # ); 321 | mu.reg_write(UC_X86_REG_RCX, 0) # ExceptionRecord, 322 | mu.reg_write(UC_X86_REG_RDX, 0) # EstablisherFrame, 323 | mu.reg_write(UC_X86_REG_R8, 0) # ContextRecord, 324 | mu.reg_write(UC_X86_REG_R9, self.dis_ctx_addr) # DispatcherContext 325 | mu.reg_write(UC_X86_REG_RAX, exception_handler_addr) 326 | mu.reg_write(UC_X86_REG_RIP, exception_handler_addr) 327 | 328 | 329 | 330 | 331 | def emu(self): 332 | def align(addr): 333 | return addr + 0x1000 & ~(0x1000-1) 334 | 335 | # 336 | # map all section 337 | # 338 | base_addr = 0x0000000140000000 339 | mu = Uc(UC_ARCH_X86, UC_MODE_64) 340 | 341 | for name, section in self.sections.items(): 342 | s_addr = section.VirtualAddress + base_addr 343 | s_size = align(section.Misc_VirtualSize) 344 | print("mapping %s @ 0x%x (sz: 0x%x)"%(name, s_addr, s_size)) 345 | mu.mem_map(s_addr, s_size) 346 | mu.mem_write(s_addr, section.get_data()) 347 | 348 | # 349 | # stack 350 | # 351 | initial_rsp = 0x00000000067FFEB0 352 | mu.mem_map(0x67f0000, 0x100000) 353 | mu.reg_write(UC_X86_REG_RBP, 0) 354 | mu.reg_write(UC_X86_REG_RSP, initial_rsp) 355 | 356 | 357 | # 358 | # madness code 359 | # 360 | self.code_addr = 0x60000000 361 | mu.mem_map(self.code_addr, 0x800000) 362 | mu.mem_write(self.code_addr, bytes(mu.mem_read(self.sections[".data"].VirtualAddress + base_addr + 0x75af0, 0x800000))) 363 | mu.mem_write(0x14089b8e0, p64(self.code_addr)) 364 | 365 | # to store context and stuff 366 | self.dis_ctx_addr = 0x90000000 367 | mu.mem_map(self.dis_ctx_addr, 0x2000) 368 | #self.call_rax_trampoline = self.dis_ctx_addr + 0x1900 369 | #mu.mem_write(self.call_rax_trampoline, b'\xff\xd0') # call rax 370 | 371 | # 372 | # init 373 | # 374 | #mu.reg_write(UC_X86_REG_R9, 0x00000000067FDB00) # test 375 | # need to setup the input key somewhere... 376 | #key = b'ABCDEFGHIJKLMNOPabcdefghijklmnop' 377 | if len(sys.argv) == 2: 378 | key = sys.argv[1].encode() 379 | else: 380 | key = b'AAAABBBBCCCCDDDDaaaabbbbccccdddd' 381 | #key = b'\xd0AAA\x0fAAA\x4aAAA\x7fAAA\xe2AAA\xd2AAA\xd2AAA\xceAAA' 382 | #key = b's\x00\x00\x00\xa2\x00\x00\x00h\x00\x00\x00\xf4\x00\x00\x00\x85\x00\x00\x00\x1b\x00\x00\x00=\x00\x00\x00[\x00\x00\x00' 383 | key_addr = 0x14089b8e8 384 | mu.mem_write(key_addr, key) 385 | 386 | # 387 | # go 388 | # 389 | mu.hook_add(UC_HOOK_CODE, self.hook_code) 390 | 391 | # emulate up until 392 | # >> 0x60017a07 cmovne r12, r15 393 | # >> 0x60017a0b jmp r12 394 | mu.emu_start(0x140001642, -1) 395 | 396 | 397 | if __name__ == "__main__": 398 | print(sys.argv) 399 | emu = Emu() 400 | emu.emu() 401 | 402 | -------------------------------------------------------------------------------- /flare-on_11/ch09/requirements.txt: -------------------------------------------------------------------------------- 1 | unicorn==2.0.1.post1 2 | setuptools 3 | capstone 4 | keystone-engine 5 | malduck 6 | pefile 7 | triton-library 8 | 9 | -------------------------------------------------------------------------------- /flare-on_11/ch09/serpentine.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_11/ch09/serpentine.exe -------------------------------------------------------------------------------- /flare-on_11/ch09/triton_solver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | from triton import * 4 | 5 | # random addresses 6 | BASE_ARGV = 0x20000000 7 | BASE_STACK = 0x9ffffff0 8 | BASE_CODE = 0x600000 9 | 10 | # not random stuff 11 | FLAG_ADDR = 0x14089b8e8 12 | FLAG_LEN = 0x20 13 | 14 | 15 | def emulate(ctx, pc): 16 | test_count = 0 17 | 18 | while pc: 19 | opcode = ctx.getConcreteMemoryAreaValue(pc, 16) 20 | instruction = Instruction(pc, opcode) 21 | addr = instruction.getAddress() 22 | 23 | ctx.processing(instruction) 24 | #print(instruction) 25 | 26 | # test instruction 27 | if instruction.getType() == OPCODE.X86.TEST: 28 | test_count += 1 29 | 30 | register = instruction.getOperands()[0] 31 | print("testing %s @ 0x%x"%(register.getName(), addr)) 32 | r = ctx.simplify(ctx.getSymbolicRegister(register).getAst()) 33 | 34 | # we want that register to be 0 35 | ctx.pushPathConstraint(r == 0) 36 | 37 | # got enough "test", get model 38 | if test_count == 32: 39 | mod = ctx.getModel(ctx.getPathPredicate()) 40 | for k,v in list(mod.items()): 41 | ctx.setConcreteVariableValue(ctx.getSymbolicVariable(v.getId()), v.getValue()) 42 | 43 | flag = '' 44 | for x in range(FLAG_LEN): 45 | flag += chr(ctx.getConcreteMemoryValue(FLAG_ADDR + x)) 46 | print("flag: %s@flare-on.com"%flag) 47 | 48 | sys.exit(0) 49 | 50 | # set rip 51 | pc = ctx.getConcreteRegisterValue(ctx.registers.rip) 52 | 53 | 54 | def main(): 55 | ctx = TritonContext(ARCH.X86_64) 56 | # Set a symbolic optimization mode 57 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 58 | ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True) 59 | ctx.setSolver(SOLVER.BITWUZLA) 60 | 61 | ast = ctx.getAstContext() 62 | 63 | # load code 64 | ctx.setConcreteMemoryAreaValue(BASE_CODE, open("stages/full.bin", "rb").read()) 65 | 66 | 67 | # setup key 68 | key = b'********************************' 69 | ctx.setConcreteMemoryAreaValue(FLAG_ADDR, key + b'\x00') 70 | 71 | # symbolize key 72 | for i in range(len(key)): 73 | var = ctx.symbolizeMemory(MemoryAccess(FLAG_ADDR + i, CPUSIZE.BYTE)) 74 | #vast = ast.variable(var) 75 | #ctx.pushPathConstraint(ast.land([vast >= ord(b' '), vast <= ord(b'~')])) 76 | 77 | 78 | # stack 79 | ctx.setConcreteRegisterValue(ctx.registers.rbp, BASE_STACK) 80 | ctx.setConcreteRegisterValue(ctx.registers.rsp, BASE_STACK) 81 | 82 | # fire 83 | emulate(ctx, BASE_CODE) 84 | 85 | 86 | if __name__ == "__main__": 87 | main() 88 | -------------------------------------------------------------------------------- /flare-on_11/ch10/README.md: -------------------------------------------------------------------------------- 1 | ## 10. CATBERG Ransomware 2 | 3 | Almost complete blackbox solve (... of the VM :-)). 4 | 5 | Use `uefiextract` to get the shell PE ([uefi_shell.pe](uefi_shell.pe)), then reverse it enough 6 | to notice the VM interpreter is for the most part "self contained" into a single function 7 | without any further function call except for the "PUTCHAR" opcode (and we can deal with it should we need it - but we don't). 8 | 9 | Since debugging UEFI seems like a pain, we can feed that function only to an emulator and play with it =) 10 | 11 | VM interpreter is at `0x00031274`, the rest (file format, etc...) is better described in the [official solution](https://services.google.com/fh/files/misc/flare-on11-challenge10-catbert-ransomware.pdf). 12 | 13 | 14 | ### VM 1 & 2 15 | 16 | The 2 first VMs can be side-channel'd by counting the instructions: there's an early exit 17 | condition whenever a password char is invalid, we can abuse that to brutefoce it character by 18 | character. 19 | 20 | I'm using Triton here because I was trying the symbolic way. Unicorn would probably be faster 21 | but the runtime benefit was nullified by the time required to rewrite it :-) 22 | 23 | ``` 24 | % python vm1_and_2.py ./enc/catmeme1.jpg.c4tb 25 | bytearray(b'D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 26 | bytearray(b'Da\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 27 | bytearray(b'DaC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 28 | bytearray(b'DaCu\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 29 | bytearray(b'DaCub\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 30 | bytearray(b'DaCubi\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 31 | bytearray(b'DaCubic\x00\x00\x00\x00\x00\x00\x00\x00\x00') 32 | bytearray(b'DaCubicl\x00\x00\x00\x00\x00\x00\x00\x00') 33 | bytearray(b'DaCubicle\x00\x00\x00\x00\x00\x00\x00') 34 | bytearray(b'DaCubicleL\x00\x00\x00\x00\x00\x00') 35 | bytearray(b'DaCubicleLi\x00\x00\x00\x00\x00') 36 | bytearray(b'DaCubicleLif\x00\x00\x00\x00') 37 | bytearray(b'DaCubicleLife\x00\x00\x00') 38 | bytearray(b'DaCubicleLife1\x00\x00') 39 | bytearray(b'DaCubicleLife10\x00') 40 | bytearray(b'DaCubicleLife101') 41 | ``` 42 | ``` 43 | % python vm1_and_2.py ./enc/catmeme2.jpg.c4tb 44 | bytearray(b'G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 45 | bytearray(b'G3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 46 | bytearray(b'G3t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 47 | bytearray(b'G3tD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 48 | bytearray(b'G3tDa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 49 | bytearray(b'G3tDaJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 50 | bytearray(b'G3tDaJ0\x00\x00\x00\x00\x00\x00\x00\x00\x00') 51 | bytearray(b'G3tDaJ0b\x00\x00\x00\x00\x00\x00\x00\x00') 52 | bytearray(b'G3tDaJ0bD\x00\x00\x00\x00\x00\x00\x00') 53 | bytearray(b'G3tDaJ0bD0\x00\x00\x00\x00\x00\x00') 54 | bytearray(b'G3tDaJ0bD0n\x00\x00\x00\x00\x00') 55 | bytearray(b'G3tDaJ0bD0ne\x00\x00\x00\x00') 56 | bytearray(b'G3tDaJ0bD0neM\x00\x00\x00') 57 | bytearray(b'G3tDaJ0bD0neM4\x00\x00') 58 | bytearray(b'G3tDaJ0bD0neM4t\x00') 59 | bytearray(b'G3tDaJ0bD0neM4te') 60 | ``` 61 | 62 | ### VM 3 63 | 64 | VM 3 can't be abused this way. I made a symbolic solver with Triton, pushing path constraints 65 | everytime there's a symbolized `CMP` VM handler (takes a while to solve: 50mins on my computer). 66 | 67 | ``` 68 | % time python vm3.py ./enc/catmeme3.jpg.c4tb 69 | 314c6 CMP 0x7c9c60e3 , 0x7c8df4cb 70 | flag: Ves8???????????? 71 | 314c6 CMP 0x7ff80fff , 0x8b681d82 72 | flag: Ves8DumB???????? 73 | 314c6 CMP 0x08e401f9 , 0x0f910374 74 | flag: Ves8DumBs]yz_zmj 75 | 314c6 CMP 0x489fdf28 , 0x31f009d2 76 | flag: VerYDumBpassword 77 | 314c6 CMP 0x00000004 , 0x00000004 78 | python vm3.py ./enc/catmeme3.jpg.c4tb 3035.92s user 0.32s system 99% cpu 50:37.12 total 79 | ``` 80 | -------------------------------------------------------------------------------- /flare-on_11/ch10/enc/catmeme1.jpg.c4tb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_11/ch10/enc/catmeme1.jpg.c4tb -------------------------------------------------------------------------------- /flare-on_11/ch10/enc/catmeme2.jpg.c4tb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_11/ch10/enc/catmeme2.jpg.c4tb -------------------------------------------------------------------------------- /flare-on_11/ch10/enc/catmeme3.jpg.c4tb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_11/ch10/enc/catmeme3.jpg.c4tb -------------------------------------------------------------------------------- /flare-on_11/ch10/uefi_shell.pe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthw/ctf/d62efaed7429348a03c1a3427cd1b7063a8f95cc/flare-on_11/ch10/uefi_shell.pe -------------------------------------------------------------------------------- /flare-on_11/ch10/vm1_and_2.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import string 4 | from triton import * 5 | from pwn import u32, p32, u64, p64 6 | 7 | BYTECODE_BASE = 0x600000 8 | BYTECODE_PTR = 0x00e85a8 9 | 10 | with open('uefi_shell.pe', 'rb') as fp: 11 | fp.seek(0x31274) 12 | VM_CODE = fp.read(0x31933 - 0x31274 + 1) 13 | 14 | 15 | def read_enc_file(fname): 16 | header_size = 0x10 17 | 18 | with open(fname, 'rb') as fp: 19 | assert fp.read(4) == b'C4TB' 20 | enc_data_len = u32(fp.read(4)) 21 | bytecode_offset = u32(fp.read(4)) 22 | bytecode_len = u32(fp.read(4)) 23 | 24 | # read enc_data 25 | fp.seek(header_size) 26 | enc_data = fp.read(enc_data_len) 27 | 28 | # read bc 29 | fp.seek(bytecode_offset) 30 | bytecode = fp.read(bytecode_len) 31 | 32 | return (enc_data, bytecode) 33 | 34 | 35 | 36 | def emulate(ctx, pc): 37 | vm_ins = 0 38 | while pc: 39 | opcode = ctx.getConcreteMemoryAreaValue(pc, 16) 40 | instruction = Instruction(pc, opcode) 41 | addr = instruction.getAddress() 42 | ctx.processing(instruction) 43 | 44 | # VM opcode switch case head 45 | if addr == 0x312c0: 46 | #print("############") 47 | #print("# handler: 0x%02x"%ctx.getConcreteRegisterValue(ctx.registers.eax)) 48 | #print("############") 49 | vm_ins += 1 50 | 51 | if instruction.getType() == OPCODE.X86.RET: 52 | break 53 | 54 | pc = ctx.getConcreteRegisterValue(ctx.registers.rip) 55 | return vm_ins 56 | 57 | 58 | 59 | 60 | 61 | def main(argv, key): 62 | enc_data, bytecode = read_enc_file(argv[1]) 63 | 64 | ctx = TritonContext(ARCH.X86_64) 65 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 66 | ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True) 67 | 68 | ast = ctx.getAstContext() 69 | 70 | # stack crap 71 | ctx.setConcreteRegisterValue(ctx.registers.rbp, 0x9ffffff0) 72 | ctx.setConcreteRegisterValue(ctx.registers.rsp, 0x9ffffff0) 73 | 74 | # load VM bytecode 75 | ctx.setConcreteMemoryAreaValue(BYTECODE_BASE, bytecode) 76 | ctx.setConcreteMemoryAreaValue(BYTECODE_PTR, p64(BYTECODE_BASE)) 77 | 78 | # load VM interpreter 79 | ctx.setConcreteMemoryAreaValue(0x31274, VM_CODE) 80 | 81 | # key shizzle 82 | key_offsets = (0x5, 0x4, 0xc, 0xb, 0x13, 0x12, 0x1a, 0x19, 83 | 0x21, 0x20, 0x28, 0x27, 0x2f, 0x2e, 0x36, 0x35) 84 | 85 | for n, offset in enumerate(key_offsets): 86 | ctx.setConcreteMemoryValue(BYTECODE_BASE + offset, key[n]) 87 | 88 | return emulate(ctx, 0x31274) 89 | 90 | 91 | if __name__ == "__main__": 92 | key = bytearray(0x10) 93 | prev_max = main(sys.argv, key) 94 | 95 | for x in range(0x10): 96 | for c in string.printable.encode(): 97 | key[x] = c 98 | cnt = main(sys.argv, key) 99 | 100 | if cnt > prev_max: 101 | prev_max = cnt 102 | print(key) 103 | break 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /flare-on_11/ch10/vm3.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | from triton import * 4 | from malduck import u32, p32, u64, p64 5 | 6 | BYTECODE_BASE = 0x600000 7 | BYTECODE_PTR = 0x00e85a8 8 | 9 | 10 | with open('uefi_shell.pe', 'rb') as fp: 11 | fp.seek(0x31274) 12 | VM_CODE = fp.read(0x31933 - 0x31274 + 1) 13 | 14 | 15 | def read_enc_file(fname): 16 | header_size = 0x10 17 | 18 | with open(fname, 'rb') as fp: 19 | assert fp.read(4) == b'C4TB' 20 | enc_data_len = u32(fp.read(4)) 21 | bytecode_offset = u32(fp.read(4)) 22 | bytecode_len = u32(fp.read(4)) 23 | 24 | # read enc_data 25 | fp.seek(header_size) 26 | enc_data = fp.read(enc_data_len) 27 | 28 | # read bc 29 | fp.seek(bytecode_offset) 30 | bytecode = fp.read(bytecode_len) 31 | 32 | return (enc_data, bytecode) 33 | 34 | 35 | def emulate(ctx, pc): 36 | while pc: 37 | opcode = ctx.getConcreteMemoryAreaValue(pc, 16) 38 | instruction = Instruction(pc, opcode) 39 | addr = instruction.getAddress() 40 | ctx.processing(instruction) 41 | #print(instruction) 42 | 43 | # CMP VM handler 44 | if addr == 0x314c6: 45 | r9 = ctx.getConcreteRegisterValue(ctx.registers.r9) 46 | v1 = u64(ctx.getConcreteMemoryAreaValue(r9, CPUSIZE.QWORD)) 47 | v2 = u64(ctx.getConcreteMemoryAreaValue(r9 - 8, CPUSIZE.QWORD)) 48 | 49 | print("%x CMP 0x%08x , 0x%08x"%(addr, v1, v2)) 50 | r = ctx.getSymbolicRegister(ctx.registers.zf) 51 | 52 | # push zf == 1 if it's symbolized 53 | if r is not None: 54 | r = r.getAst() 55 | ctx.pushPathConstraint(r == 1) 56 | mod = ctx.getModel(ctx.getPathPredicate()) 57 | flag = "" 58 | for k,v in sorted(list(mod.items())): 59 | flag += chr(v.getValue()) 60 | ctx.setConcreteVariableValue(ctx.getSymbolicVariable(v.getId()), v.getValue()) 61 | print("flag: "+flag) 62 | 63 | 64 | if instruction.getType() == OPCODE.X86.RET: 65 | break 66 | 67 | pc = ctx.getConcreteRegisterValue(ctx.registers.rip) 68 | 69 | 70 | 71 | 72 | def main(argv): 73 | enc_data, bytecode = read_enc_file(argv[1]) 74 | 75 | ctx = TritonContext(ARCH.X86_64) 76 | ctx.setMode(MODE.ALIGNED_MEMORY, True) 77 | ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True) 78 | ctx.setSolver(SOLVER.BITWUZLA) 79 | 80 | ast = ctx.getAstContext() 81 | 82 | # stack crap 83 | ctx.setConcreteRegisterValue(ctx.registers.rbp, 0x9ffffff0) 84 | ctx.setConcreteRegisterValue(ctx.registers.rsp, 0x9ffffff0) 85 | 86 | # load VM bytecode 87 | ctx.setConcreteMemoryAreaValue(BYTECODE_BASE, bytecode) 88 | ctx.setConcreteMemoryAreaValue(BYTECODE_PTR, p64(BYTECODE_BASE)) 89 | 90 | # load VM interpreter 91 | ctx.setConcreteMemoryAreaValue(0x31274, VM_CODE) 92 | 93 | # key shizzle 94 | key = b'poopoopoopoopoop' 95 | sym_key = [] 96 | key_offsets = (0x5, 0x4, 0xc, 0xb, 0x13, 0x12, 0x1a, 0x19, 97 | 0x21, 0x20, 0x28, 0x27, 0x2f, 0x2e, 0x36, 0x35) 98 | 99 | for n, offset in enumerate(key_offsets): 100 | ctx.setConcreteMemoryValue(BYTECODE_BASE + offset, key[n]) 101 | var = ctx.symbolizeMemory(MemoryAccess(BYTECODE_BASE + offset, CPUSIZE.BYTE)) 102 | vast = ast.variable(var) 103 | ctx.pushPathConstraint(ast.land([vast >= ord(b'0'), vast <= ord(b'z')])) 104 | 105 | 106 | emulate(ctx, 0x31274) 107 | 108 | 109 | if __name__ == "__main__": 110 | main(sys.argv) 111 | 112 | 113 | 114 | --------------------------------------------------------------------------------