# X Ét Ét

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FrZFghTpbQOae67AObPhS%2Fimage.png?alt=media&#x26;token=9e82916b-742e-4e27-849e-e5d362bd1ec5" alt=""><figcaption></figcaption></figure>

## 1. General

Bài này cho ta một ứng dụng có sử dụng electron và với tên chall thì biết ngay hướng của bài là XSS => RCE thông qua electron

Chall có cấu trúc thư mục như sau:

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FneiUZ377f61SSXkLnTkb%2Fimage.png?alt=media&#x26;token=b0c60eff-aab2-43f7-af22-610dc9a0db1c" alt=""><figcaption></figcaption></figure>

Với thư mục `app` là code của electron, code web chính nằm ở `server` còn `bot` thì đơn giản là con bot sẽ gọi đến electron

Tóm tắt qua workflow của ứng dụng như sau

* Trang web có tính năng login, signup
* Sau khi đăng nhập thì có thể tạo ticket với các data sau

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FtgW8YSzGuqy89uELyX9x%2Fimage.png?alt=media&#x26;token=123c2fd8-d138-4acd-a0a8-b866091323ad" alt=""><figcaption></figcaption></figure>

* Khi tạo ticket ta sẽ được như sau

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FIurxwbGgxSV27R67e8JI%2Fimage.png?alt=media&#x26;token=47ed7012-c4e9-436a-bd03-d6fa5cbce5d5" alt=""><figcaption></figcaption></figure>

* Khi report admin thì code server sẽ santitize title và content để gửi cho bot

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FlcZ0j2SOF2fSQC1CN9wB%2Fimage.png?alt=media&#x26;token=788d4168-ded9-4741-9075-6f8ddf25a73a" alt=""><figcaption></figcaption></figure>

* Tại code xử lý của con bot nó sẽ base64 encode id, title và content lưu vào env, và sau đó gọi đến ứng dụng electron

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F3SByeP4B2eixmhwgx2B5%2Fimage.png?alt=media&#x26;token=756b8cd2-3abe-425d-94b8-86d5b17b254c" alt=""><figcaption></figcaption></figure>

* Tại code của electron nó sẽ decode env và load giao diện

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Flnr8pZ7rOUiPiuQPVvvU%2Fimage.png?alt=media&#x26;token=82d72d53-3371-47be-be39-45647ef8aa5f" alt=""><figcaption></figcaption></figure>

## 2. XSS

Ở trên là cách trang web hoạt động, mình bắt đầu nhảy vào phân tích src

Đầu tiên, trang ticket dùng jinja2 template để load nội dung 1 cách an toàn nên không thể XSS => loại

Tại trang `admin.html` sẽ load data với options `safe` do đó trang này có thể bị XSS

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FEQT2ZPw5s2FKO3daZ1c2%2Fimage.png?alt=media&#x26;token=589ad260-7b9e-4fc5-b679-9f1124fe355a" alt=""><figcaption></figcaption></figure>

Ta có thể load `admin.html` thông qua route `/IsNew`. Tuy nhiên để đến được trang này thì phải là request internal, cộng thêm vào đó trang được trang bị CSP

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Fwl8UV5OAG9wuSnqzimFZ%2Fimage.png?alt=media&#x26;token=f31f56c0-4613-4e24-b6f8-5c853e6a4dc1" alt=""><figcaption></figcaption></figure>

Giá trị CSP của trang

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FnXzOw5M9YGzEfsm3RHBM%2Fimage.png?alt=media&#x26;token=f7701bc3-617c-424d-8c6e-fb92ad55e5bf" alt=""><figcaption></figcaption></figure>

Với `script-src 'self'` thì ta có thể bypass bằng upload file và trang web cũng có tính năng đó&#x20;

Để đi đến được `admin.html` ta sẽ có 2 hướng

* Thông qua iframe, vì code load iframe chỉ check phần content có tồn tại "<http://localhost/tmp/>" hay không, do đó mình có thể bypass bằng "<http://localhost/tmp/../IsNew?id=\\><id>/#" và khiến iframe load một ticket bất kỳ. Tuy nhiên XSS được thì không thể RCE được, lý do vì sao thì một lát các bạn sẽ biết
* Hướng thứ 2 là khiến renderer kích hoạt envent `CreateViewer => createNotificationWindow(id) => child.loadURL("http://localhost/IsNew?id="+id) => admin.html => XSS` , để làm được điều đó mình phải bypass được check admin

Để bypass check admin thì đơn giản mình chỉ cần tạo user admin có thêm khoảng trắng phía sau, vì khi trang web lưu session sẽ strip username

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F7JucabC8ARsMgqX9Pu05%2Fimage.png?alt=media&#x26;token=09602a84-55d5-4165-9931-27bbc7a5ada2" alt=""><figcaption></figcaption></figure>

Điều này khiến username `admin` hay `admin` đều có `session['username']` là admin

Bypass được admin mình có thể trigger được `CreateViewer` ,lúc này mình sẽ XSS thông qua title, vì trong electron ta có thể load được internal resource, nên mình sẽ lợi dụng tag `meta` để load file và trigger XSS.&#x20;

Để kiểm chứng thì mình sẽ tạo một file html tại máy (giả lập như file upload trên server), và dựng debug ở vscode để test

Tạo ticket chứa payload gọi đến file html của mình

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F9FS2Frj1YmNPmgyMSR5M%2Fimage.png?alt=media&#x26;token=b446eec9-6566-47ef-9613-05da8b91d31c" alt=""><figcaption></figcaption></figure>

Debug gọi đến electron app, thì mình đã XSS thành công

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Fs46xqQhVzFVzwrcwUqgH%2Fimage.png?alt=media&#x26;token=8eca3f8c-1359-43c8-88af-3d71bd2e5052" alt=""><figcaption></figcaption></figure>

Bây giờ đã có thể XSS, mình tìm cách để RCE

## 3. RCE

Mình tham khảo về cách XSS 2 RCE ở bài blog [này](https://nhienit.wordpress.com/2023/06/26/cve-2022-3133-draw-io-xss-leads-to-rce/)

Để ý [ ](https://nhienit.wordpress.com/2023/06/26/cve-2022-3133-draw-io-xss-leads-to-rce/)khi window popup thì sẽ set các options sau

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2Fnzst7KxR39qmies1OzJj%2Fimage.png?alt=media&#x26;token=d0a3157c-7cc5-4fff-96bf-4fd1aa835fb4" alt=""><figcaption></figcaption></figure>

Với option `nodeIntegrationInSubFrames: false` thì ta không thể call ipc từ iframe do đó attack vector XSS từ iframe lúc nảy mình đề cập không thể nào lợi dụng để leo thành RCE được

Tuy nhiên option `contextIsolation` cũng set là false, do đó main process và processor process có thể dùng chung global content, tuy nhiên không hề có preload nào được load trong event này [😭](https://emojipedia.org/loudly-crying-face)[😭](https://emojipedia.org/loudly-crying-face)[😭](https://emojipedia.org/loudly-crying-face)

Mình khá là bí đoạn này, sau một hồi search gg về các options được set có thể lợi dụng được gì không. Thì mình tìm được bài này: <https://powerofcommunity.net/poc2022/HectorPeralta.pdf>

Các bạn tập trung vào slide 49-52. Khi options `contextIsolation` và `sandbox` là false ta có thể lợi dụng để can thiệp vào quá trình khởi tạo môi trường nodeJS của renderer process

![](https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2F3ViiUPTAbD9iDWltADt5%2Fimage.png?alt=media\&token=4fc97d3c-8fa9-4330-b11a-217d1a9c3947)&#x20;

Ta sẽ lợi dụng cơ chế `__webpack_require__` để load bất kỳ module nào mà ta mong muốn

<figure><img src="https://84143647-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOzRSs0yxBc5GafSMIql8%2Fuploads%2FQ6Dg6gml3CMagucK6D8A%2Fimage.png?alt=media&#x26;token=9a767304-1b9f-4943-95b2-1bfd4b98a810" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Các bạn có thể tìm hiểu thêm về cơ chế load module webpack thông qua bài blog này : <https://www.freecodecamp.org/chinese/news/webpack-module-loading/>
{% endhint %}

Nói đơn giản thì webpack là cơ chế giúp load các module của commonJS, mà Nodejs mặc định sẽ sử dụng commonJS để làm module system. Do đó ý tưởng sẽ là ghi đè lại function call của webpack\_require trong quá trình module loading, bằng cách check arguments thứ 4 có phải là `__webpack_require__` không, sau đó lấy instance của `__webpack_require__` và load module child\_process để RCE

Ta có đoạn script sau để RCE

```html
<script>

    Function.prototype._call = Function.prototype.call;
    Function.prototype.call = function(arg1,arg2,arg3,arg4) {
        try {
            if (arg4.name == '__webpack_require__') {
            Function.prototype.call = Function.prototype._call;
            delete Function.prototype._call;
            webpack_1 = arg4;
            var cp = {};
            webpack_1.m.module(cp);
            cmd = cp.exports._load('child_process');
            cmd.exec('RCE');
        }} catch(er) {
            console.log(er);
        }
    }
    console.log(Function.prototype.call)
</script>
```

Tóm lại để exploit ta sẽ:

* Đăng ký user với tên là `admin` để bypass check admin
* Tạo ticket1 để upload malicous html
* Tạo ticket2, với title là `<meta http-equiv="refresh" content="0; url=file:///tmp/<ticket1_uuid>.html">`
* Report ticket2&#x20;
* Ghi flag vào /app/server/static và truy cập để đọc flag


---

# 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/x-et-et.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.
