Problem

How-to_solve

우선 구동을 봄.

코드 삽입 시 참 거짓을 판별하는 프로그램임.

IDA혹은 x64dbg로 하면 되는데 난 다 해보기로 했음.

 

1. IDA

확인해보니 Input에 따라 대조하여 판별하는 형식이었음.

sub_14001000이 확인 코드로 보여 더블클릭으로 따라가 봄.

위에 보면 코드가 나오는 것을 확인할 수 있음.

 

2. x64dbg

 

(난 IDA가 좋음.......)

우선 시작 지점을 찾아보자.

어;;;;;; 찾았다!

하지만 찾는 방법을 알아보고 싶었다.

input을 따라가 보니 IDA와 같은 코드를 찾을 수 있었음.

마찬가지로 call chall0.7FF787351000이 확인 코드로 보여 따라가 보기로 함.

답을 확인할 수 있음.

'Wargame > dreamhack' 카테고리의 다른 글

simple-sqli  (0) 2022.01.25
cookie  (0) 2022.01.25
pathtraversal  (0) 2022.01.25
file-download-1  (0) 2022.01.25
xss-1  (0) 2022.01.25

Problem

How-to_solve

URL에서 쿠키값을 읽고 있음.

주소는 127.0.0.1에서 가능하며 받은 주소가 다름.

cookie값을 얻어보기로 했으나 쿠키값이 memo에 남길 수 있어서 넘기기로 결정.

vuln 페이지
memo 페이지

memo에서 시도해 봤으나 입력이 안 되는 것 같음

로컬(127.0.0.1) 확인을 해야 해서 flag에서 시도를 하는 게 맞음.

로컬을 통해 memo에 쿠키를 남겨야하므로 memo페이지로 올리기로 함.

<script>location.href="http://127.0.0.1:8000/memo?memo=hello"+document.cookie;</script>

이유는 스크립트가 삽입되며 cookie값이 저장되도록 했는데 로컬(127.0.0.1)에서 받아와야 하므로 저렇게 짰음.

끝!

'Wargame > dreamhack' 카테고리의 다른 글

simple-sqli  (0) 2022.01.25
cookie  (0) 2022.01.25
pathtraversal  (0) 2022.01.25
file-download-1  (0) 2022.01.25
rev-basic-0  (0) 2022.01.25

1. 개요

frida를 이용한 SSL Pinning 방법은 다양하다. frida-server의 경우에는 루팅/탈옥 환경에서 사용하며 이용성이 크기 때문에 많이 쓰이고 있다. 하지만 frida-server는 여러 가지 이유로 상황적 제약을 받는데 이를 타파하기 위해 frida-gadget을 사용하기도 하며 비 루팅/비 탈옥 환경에서 유용하게 쓰이기도 한다. 이번에는 간단한 후킹을 통해 frida-server 활용한 루팅/탈옥 환경에서의 SSL Pinning을 우회하는 방법을 해보자.

 

2. 준비

Frida가 설치된 환경, adb

 

3. 방법

우선 일전에 frida, frida-server설치 방법은 제외하고 진행하도록 하겠습니다.

 

frida는 실행 시에 코드를 주입하여 쓰는 방법을 많이 쓰는데 이는 웹페이지를 직접 연결하여 사용하기도 합니다.

 

frida -U -l ~~.js <hooking 할 어플명>

 

위의 명령어를 통해 진행하면 됩니다.

 

아래의 코드를 이용하여 진행하면 됩니다.

 

Java.perform(function() {

 

/*

hook list:

1.SSLcontext

2.okhttp

3.webview

4.XUtils

5.httpclientandroidlib

6.JSSE

7.network\_security\_config (android 7.0+)

8.Apache Http client (support partly)

*/

 

// Attempts to bypass SSL pinning implementations in a number of

// ways. These include implementing a new TrustManager that will

// accept any SSL certificate, overriding OkHTTP v3 check()

// method etc.

var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');

var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');

var SSLContext = Java.use('javax.net.ssl.SSLContext');

var quiet_output = false;

 

// Helper method to honor the quiet flag.

function quiet_send(data) {

 

if (quiet_output) {

 

return;

}

 

send(data)

}

 

 

// Implement a new TrustManager

// ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8

// Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用.

/*

06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing

06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err: at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)

at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)

06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err: at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)

at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)

at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)

*/

var X509Certificate = Java.use("java.security.cert.X509Certificate");

var TrustManager;

try {

TrustManager = Java.registerClass({

name: 'org.wooyun.TrustManager',

implements: [X509TrustManager],

methods: {

checkClientTrusted: function (chain, authType) {

},

checkServerTrusted: function (chain, authType) {

},

getAcceptedIssuers: function () {

// var certs = [X509Certificate.$new()];

// return certs;

return [];

}

}

});

} catch (e) {

console.log("registerClass from X509TrustManager >>>>>>>> " + e.message);

}

 

 

 

 

 

// Prepare the TrustManagers array to pass to SSLContext.init()

var TrustManagers = [TrustManager.$new()];

 

try {

// Prepare a Empty SSLFactory

var TLS_SSLContext = SSLContext.getInstance("TLS");

TLS_SSLContext.init(null,TrustManagers,null);

var EmptySSLFactory = TLS_SSLContext.getSocketFactory();

} catch (e) {

console.log(e.message);

}

 

send('Custom, Empty TrustManager ready');

 

// Get a handle on the init() on the SSLContext class

var SSLContext_init = SSLContext.init.overload(

'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');

 

// Override the init method, specifying our new TrustManager

SSLContext_init.implementation = function (keyManager, trustManager, secureRandom) {

 

quiet_send('Overriding SSLContext.init() with the custom TrustManager');

 

SSLContext_init.call(this, null, TrustManagers, null);

};

 

/*** okhttp3.x unpinning ***/

 

 

// Wrap the logic in a try/catch as not all applications will have

// okhttp as part of the app.

try {

 

var CertificatePinner = Java.use('okhttp3.CertificatePinner');

 

console.log('OkHTTP 3.x Found');

 

CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function () {

 

quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');

}

 

} catch (err) {

 

// If we dont have a ClassNotFoundException exception, raise the

// problem encountered.

if (err.message.indexOf('ClassNotFoundException') === 0) {

 

throw new Error(err);

}

}

 

// Appcelerator Titanium PinningTrustManager

 

// Wrap the logic in a try/catch as not all applications will have

// appcelerator as part of the app.

try {

 

var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');

 

send('Appcelerator Titanium Found');

 

PinningTrustManager.checkServerTrusted.implementation = function () {

 

quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');

}

 

} catch (err) {

 

// If we dont have a ClassNotFoundException exception, raise the

// problem encountered.

if (err.message.indexOf('ClassNotFoundException') === 0) {

 

throw new Error(err);

}

}

 

/*** okhttp unpinning ***/

 

 

try {

var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");

OkHttpClient.setCertificatePinner.implementation = function(certificatePinner){

// do nothing

console.log("OkHttpClient.setCertificatePinner Called!");

return this;

};

 

// Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)

var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");

CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1){

// do nothing

console.log("okhttp Called! [Certificate]");

return;

};

CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1){

// do nothing

console.log("okhttp Called! [List]");

return;

};

} catch (e) {

console.log("com.squareup.okhttp not found");

}

 

/*** WebView Hooks ***/

 

/* frameworks/base/core/java/android/webkit/WebViewClient.java */

/* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */

var WebViewClient = Java.use("android.webkit.WebViewClient");

 

WebViewClient.onReceivedSslError.implementation = function (webView,sslErrorHandler,sslError){

quiet_send("WebViewClient onReceivedSslError invoke");

//执行proceed方法

sslErrorHandler.proceed();

return ;

};

 

WebViewClient.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function (a,b,c,d){

quiet_send("WebViewClient onReceivedError invoked");

return ;

};

 

WebViewClient.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function (){

quiet_send("WebViewClient onReceivedError invoked");

return ;

};

 

/*** JSSE Hooks ***/

 

/* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java */

/* public final TrustManager[] getTrustManager() */

 

var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");

TrustManagerFactory.getTrustManagers.implementation = function(){

quiet_send("TrustManagerFactory getTrustManagers invoked");

return TrustManagers;

}

 

var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");

/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */

/* public void setDefaultHostnameVerifier(HostnameVerifier) */

HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier){

quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked");

return null;

};

/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */

/* public void setSSLSocketFactory(SSLSocketFactory) */

HttpsURLConnection.setSSLSocketFactory.implementation = function(SSLSocketFactory){

quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");

return null;

};

/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java */

/* public void setHostnameVerifier(HostnameVerifier) */

HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier){

quiet_send("HttpsURLConnection.setHostnameVerifier invoked");

return null;

};

 

/*** Xutils3.x hooks ***/

//Implement a new HostnameVerifier

var TrustHostnameVerifier;

try {

TrustHostnameVerifier = Java.registerClass({

name: 'org.wooyun.TrustHostnameVerifier',

implements: [HostnameVerifier],

method: {

verify: function (hostname, session) {

return true;

}

}

});

 

} catch (e) {

//java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"

console.log("registerClass from hostnameVerifier >>>>>>>> " + e.message);

}

 

try {

var RequestParams = Java.use('org.xutils.http.RequestParams');

RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory){

sslSocketFactory = EmptySSLFactory;

return null;

}

 

RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier){

hostnameVerifier = TrustHostnameVerifier.$new();

return null;

}

 

} catch (e) {

console.log("Xutils hooks not Found");

}

 

/*** httpclientandroidlib Hooks ***/

try {

var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");

AbstractVerifier.verify.overload('java.lang.String','[Ljava.lang.String','[Ljava.lang.String','boolean').implementation = function(){

quiet_send("httpclientandroidlib Hooks");

return null;

}

} catch (e) {

console.log("httpclientandroidlib Hooks not found");

}

 

/***

android 7.0+ network_security_config TrustManagerImpl hook

apache httpclient partly

***/

var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");

// try {

// var Arrays = Java.use("java.util.Arrays");

// //apache http client pinning maybe baypass

// //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471

// TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {

// quiet_send("TrustManagerImpl checkTrusted called");

// //Generics currently result in java.lang.Object

// return Arrays.asList(chain);

// }

//

// } catch (e) {

// console.log("TrustManagerImpl checkTrusted nout found");

// }

 

try {

// Android 7+ TrustManagerImpl

TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {

quiet_send("TrustManagerImpl verifyChain called");

// Skip all the logic and just return the chain again :P

//https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/

// https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650

return untrustedChain;

}

} catch (e) {

console.log("TrustManagerImpl verifyChain nout found below 7.0");

}

// -- Sample Java

//

// "Generic" TrustManager Example

//

// TrustManager[] trustAllCerts = new TrustManager[] {

// new X509TrustManager() {

// public java.security.cert.X509Certificate[] getAcceptedIssuers() {

// return null;

// }

// public void checkClientTrusted(X509Certificate[] certs, String authType) { }

 

// public void checkServerTrusted(X509Certificate[] certs, String authType) { }

 

// }

// };

 

// SSLContext sslcontect = SSLContext.getInstance("TLS");

// sslcontect.init(null, trustAllCerts, null);

 

// OkHTTP 3 Pinning Example

// String hostname = "swapi.co";

// CertificatePinner certificatePinner = new CertificatePinner.Builder()

// .add(hostname, "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")

// .build();

 

// OkHttpClient client = new OkHttpClient.Builder()

// .certificatePinner(certificatePinner)

// .build();

 

// Request request = new Request.Builder()

// .url("https://swapi.co/api/people/1")

// .build();

 

// Response response = client.newCall(request).execute();

});

 

frida-gadget도 같은 코드를 이용하여 SSL Pinning이 가능하며 frida-gadget은 따로 페이지를 만들어 설치 방법을 쓰도록 하겠습니다.

 

설명이 길지 않은 것은 이미 일전에 설치방법을 올린 부분이 있어 사용하는 방법을 위주로 올렸습니다.

1. 개요

Frida는 다양한 활용법이 있습니다. 우선 Frida를 이용한 Method 변환 방법을 해보도록 하겠습니다.

 

2. 준비

Frida가 설치된 환경

 

3. 방법

참조 : https://blog.attify.com/bypass-jailbreak-detection-frida-ios-applications/

전부 위의 주소를 참조하여 진행하였습니다.

위의 페이지는 iOS 위주로 설명 중이지만 같은 방법으로 Android에도 적용 가능하며 변형하여 활용할 수도 있습니다.

 

for (var className in ObjC.classes){

if (ObjC.classes.hasOwnProperty(className))

{console.log(className);} }

 

위의 코드는 Class명을 찾는 소스입니다. 위의 소스를 .js파일로 만들어 Frida 실행 시에 소스를 주입하여 실행시키는 형식입니다.

 

frida -U -l ~~.js <어플명> > class.txt

 

페이지에서는 직접 띄우는 형식이거나 grep으로 해당 부분을 찾아내는 형식이었지만 windows환경에서 활용하기에는 파일로 업데이트하여 직접 찾는 것이 훨씬 수월했습니다.

 

console.log("[*] Started: Find All Methods of a Specific Class");

if (ObjC.available) {

try {

var className = "의심되는 Class";

var methods = eval('ObjC.classes.' + className + '.$methods');

for (var i = 0; i < methods.length; i++) {

try { console.log("[-] "+methods[i]); }

catch(err) { console.log("[!] Exception1: " + err.message); }

} }

catch(err) { console.log("[!] Exception2: " + err.message); } }

else { console.log("Objective-C Runtime is not available!"); }

console.log("[*] Completed: Find All Methods of a Specific Class");

 

위의 소스는 의심스러운 ClassMethod를 찾는 소스입니다.

 

위에 있는 의심되는 ClassClass들을 찾은 뒤 그 Class내에 있는 Method들 중 필요한 Method를 변조하기 위한 과정입니다.

위의 소스 또한 .js파일로 만들어 줍니다.

 

frida -U -l ~~.js <어플명> > test.txt

 

전에 말씀드린 것과 같이 windows의 환경을 고려하여 txttxt 파일로 만들어주어 찾기 쉽도록 한 것입니다.

위의 txt 파일에 Method들 중 의심스러운 Method 혹은 확인된 Method를 변조하는 과정을 수행하기 전에 해당 Method의 반환 값을 확인하는 과정을 진행합니다.

 

if (ObjC.available) {

 

try { var className = "의심되는 Class";

var funcName = "의심되는 Method";

var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');

 

Interceptor.attach(hook.implementation, {

onLeave: function(retval) { console.log("[*] Class Name: " + className);

console.log("[*] Method Name: " + funcName);

console.log("\t[-] Type of return value: " + typeof retval);

console.log("\t[-] Return Value: " + retval); } }); }

catch(err) { console.log("[!] Exception2: " + err.message); } }

 

else { console.log("Objective-C Runtime is not available!"); }

 

위의 ClassMethod를 써넣어줍니다.. 여기에서 주의할 점은 Method명을 넣을 때 Method“-” 혹은 “ ”처럼 띄어쓰기 및 빼기 표시는 꼭 같이 넣어주어야 합니다.

 

위처럼 진행할 경우 해당 Method의 반환 값이 True 혹은 False이거나 0x0 혹은 0x1의 값을 확인할 수 있습니다.

 

위와 같은 반환 값 즉 return value를 알게 되었다면 해당 값을 반대 값으로 바꾸어 줍니다. 이 과정을 수행하는 이유는 정상적으로 작동하는 앱에서 만약 문제 발생 시에 차단하는 것을 문제 존재 시 정상적으로 넘어가는 형식으로 바꾸어주는 것이라 생각하시면 되겠습니다.

 

if (ObjC.available) {

 

try {

var className = "의심되는 Class";

var funcName = "의심되는 Method";

var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');

 

Interceptor.attach(hook.implementation, {

onLeave: function(retval) { console.log("[*] Class Name: " + className);

console.log("[*] Method Name: " + funcName);

console.log("\t[-] Type of return value: " + typeof retval);

console.log("\t[-] Original Return Value: " + retval);

newretval = ptr("수정할 값")

retval.replace(newretval)

console.log("\t[-] New Return Value: " + newretval) } }); }

catch(err) { console.log("[!] Exception2: " + err.message); } }

 

else { console.log("Objective-C Runtime is not available!"); }

 

위의 수정할 값에는 0x0일 경우 0x1을 넣어주는 방식으로 진행하시면 됩니다.

 

위의 과정은 한 번에 성공할 확률이 높지 않습니다. 이 과정을 조금 더 세밀하고 정확하게 진행하고 싶으신 분은 ollydbg, IDA와 같은 동적 분석 툴을 이용하여 Method의 동작이나 탐지되는 Class를 분석하여 진행하면 실수를 줄일 수 있습니다.

 

하지만 위와 같은 방법을 반복적으로 진행하여 분석하여도 전혀 문제가 되지 않으며 하나씩 하다 보면 여러 가지 방법을 찾을 수 있을 것입니다.

+ Recent posts