首页
社区
课程
招聘
[原创]遇见非常规双向证书绕过
发表于: 2023-2-11 13:53 25436

[原创]遇见非常规双向证书绕过

2023-2-11 13:53
25436

这个app的本地证书做了处理,对证书名加密以及去掉证书后缀,导致用常规全局搜索.p12等文件失败。

该app可以抓到包,但是存在以下问题

一般是由于做了双向认证校验,服务器校验客户端证书导致的。

对app进行脱壳,全局搜索".cer"、".crt"、".pfx"、"PKCS12"、"keyStore"、".p12"等关键字。在
图片描述
跳转到目标代码上

找到调用上面Client的方法

从代码中可以看到

sslSocketFactory中参数open(E.i(value))打开本地的证书文件,参数EncryptNDK.getInstance().gainValue(value)为获取证书的key。
跟踪获取证书的key函数

调用了so文件,而gainValue中的str即是证书的key。利用frida去hook EncryptNDK.gainValue以及E.i(value)。

利用hook到的值去全局搜索即可得到被调用的证书以及证书的key。在burpsuit中的User options 》 TLS中导入相对于的证书与域名即可。

在证书存在于本地的资源文件中,对证书名进行加密和去掉证书后缀。导致常规的全局搜索找不到证书的所在,若是证书通过远程进行下载,以及存在这样的情况下如何去解决。

1、可以通过本地文件的更新时间去判断,然而可能需要去甄别那些是证书。若是存在上述的加密以及去掉证书后缀,则可能要花费很大功夫甚至是无法达成目的。
2、如果证书是以java.security.KeyStore来进行双向认证校验,那么通过hook该函数的load方法可获取证书文件和证书密码,即

针对于该脚本的实践中,发现应用于本次的app是需要改动的。本次的app存在spwan的检测,无法使用device.spawn去启动app,因此需要修改。去掉

同时将pid改为app_name

经过实践该脚本是可以成立的,但是好像只生成有带密码的证书文件,而且证书后缀为.jks。
3、如果app本身不存在检测的话,可以利用objection,去hook java.io.File

或使用spawn方式

HTTP/1.1 400 Bad Request
Server: openresty/1.17.8.1
Date: Wed, 14 Oct 2020 10:00:30 GMT
Content-Type: text/html; charset=utf8
Content-Length: 645
Connection: close
 
<html>
  <head><title>400 No required SSL certificate was sent</title></head>
  <body>
    <center><h1>400 Bad Request</h1></center>
    <center>No required SSL certificate was sent</center>
    <hr><center>openresty/1.17.8.1</center>
  </body>
</html>
HTTP/1.1 400 Bad Request
Server: openresty/1.17.8.1
Date: Wed, 14 Oct 2020 10:00:30 GMT
Content-Type: text/html; charset=utf8
Content-Length: 645
Connection: close
 
<html>
  <head><title>400 No required SSL certificate was sent</title></head>
  <body>
    <center><h1>400 Bad Request</h1></center>
    <center>No required SSL certificate was sent</center>
    <hr><center>openresty/1.17.8.1</center>
  </body>
</html>
        if (!hashMap.containsKey(host2)) {
            hashMap.put(host2, host2 + ".p12");
        }
    }
    Client(hashMap);
}
        if (!hashMap.containsKey(host2)) {
            hashMap.put(host2, host2 + ".p12");
        }
    }
    Client(hashMap);
}
private void Client(Map<String, String> map) {
        ...
        OkHttpClient.Builder createClientBuilder = createClientBuilder();
        createClientBuilder.sslSocketFactory(surea.a(App.b().getAssets().open(E.i(value)),EncryptNDK.getInstance().gainValue(value)), a.a());
private void Client(Map<String, String> map) {
        ...
        OkHttpClient.Builder createClientBuilder = createClientBuilder();
        createClientBuilder.sslSocketFactory(surea.a(App.b().getAssets().open(E.i(value)),EncryptNDK.getInstance().gainValue(value)), a.a());
createClientBuilder.sslSocketFactory(surea.a(App.b().getAssets().open(E.i(value)),EncryptNDK.getInstance().gainValue(value)), a.a());
createClientBuilder.sslSocketFactory(surea.a(App.b().getAssets().open(E.i(value)),EncryptNDK.getInstance().gainValue(value)), a.a());
public class EncryptNDK {
    static {
        System.loadLibrary("EncryptLib");
    }
    ...
    public native String gainValue(String str);
    ....
}
public class EncryptNDK {
    static {
        System.loadLibrary("EncryptLib");
    }
    ...
    public native String gainValue(String str);
    ....
}
#!/usr/bin/python3
import frida, sys, time
 
app_name = '包名'
i = 0
ext = ''
 
def on_message(message, data):
   global i, ext
   if (message['type'] == 'send' and 'event' in message['payload']):
       if (message['payload']['event'] == '+found'):
           i += 1
           print("\n[+] Hooked keystore" + str(i) + "...")
 
       elif (message['payload']['event'] == '+type'):
           print(" [+] Cert Type: " + ''.join(message['payload']['certType']))
           if (message['payload']['certType'] == 'PKCS12'):
               ext = '.jks'
 
       elif (message['payload']['event'] == '+pass'):
           print(" [+] Password: " + ''.join(message['payload']['password']))
 
       elif (message['payload']['event'] == '+write'):
           print(" [+] Writing to file: keystore" + str(i) + ext)
           f = open('keystore' + str(i) + ext, 'wb')
           f.write(bytes.fromhex(message['payload']['cert']))
           f.close()
   else:
       print(message)
 
jscode = """
setTimeout(function() {
  Java.perform(function () {
      var keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');
      /* following function hooks to a Keystore.load(InputStream stream, char[] password) */
      keyStoreLoadStream.implementation = function(stream, charArray) {
          /* sometimes this happen, I have no idea why, tho... */
          if (stream == null) {
              /* just to avoid interfering with app's flow */
              this.load(stream, charArray);
              return;
          }
          /* just to notice the client we've hooked a KeyStore.load */
          send({event: '+found'});
          /* read the buffer stream to a variable */
          var hexString = readStreamToHex (stream);
          /* send KeyStore type to client shell */
          send({event: '+type', certType: this.getType()});
          /* send KeyStore password to client shell */
          send({event: '+pass', password: charArray});
          /* send the string representation to client shell */
          send({event: '+write', cert: hexString});
          /* call the original implementation of 'load' */
          this.load(stream, charArray);
          /* no need to return anything */
      }
  });
},0);
/* following function reads an InputStream and returns an ASCII char representation of it */
function readStreamToHex (stream) {
  var data = [];
  var byteRead = stream.read();
  while (byteRead != -1)
  {
      data.push( ('0' + (byteRead & 0xFF).toString(16)).slice(-2) );
              /* <---------------- binary to hex ---------------> */
      byteRead = stream.read();
  }
  stream.close();
  return data.join('');
}
"""
 
print("[.] Attaching to device...")
try:
   device = frida.get_remote_device()     
except:
   print("[-] Can't attach. Is the device connected?")
   sys.exit()
 
print("[.] Spawning the app...")
try:
   pid = device.spawn(app_name)
   device.resume(pid)
   time.sleep(1)
except:
   print("[-] Can't spawn the App. Is filename correct?")
   sys.exit()
 
print("[.] Attaching to process...")
try:
   process = device.attach(pid)
except:
   print("[-] Can't connect to App.")
   sys.exit()
 
print("[.] Launching js code...")
print(" (run the app until needed, close it and then kill this script)")
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
try:
       sys.stdin.read()
except KeyboardInterrupt:
   print ("\nExiting now")
   exit(0)
#!/usr/bin/python3
import frida, sys, time
 
app_name = '包名'
i = 0
ext = ''
 
def on_message(message, data):
   global i, ext
   if (message['type'] == 'send' and 'event' in message['payload']):
       if (message['payload']['event'] == '+found'):
           i += 1
           print("\n[+] Hooked keystore" + str(i) + "...")
 
       elif (message['payload']['event'] == '+type'):
           print(" [+] Cert Type: " + ''.join(message['payload']['certType']))
           if (message['payload']['certType'] == 'PKCS12'):
               ext = '.jks'
 
       elif (message['payload']['event'] == '+pass'):
           print(" [+] Password: " + ''.join(message['payload']['password']))
 
       elif (message['payload']['event'] == '+write'):
           print(" [+] Writing to file: keystore" + str(i) + ext)
           f = open('keystore' + str(i) + ext, 'wb')
           f.write(bytes.fromhex(message['payload']['cert']))
           f.close()
   else:
       print(message)
 
jscode = """
setTimeout(function() {
  Java.perform(function () {
      var keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');
      /* following function hooks to a Keystore.load(InputStream stream, char[] password) */
      keyStoreLoadStream.implementation = function(stream, charArray) {
          /* sometimes this happen, I have no idea why, tho... */
          if (stream == null) {
              /* just to avoid interfering with app's flow */

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 6
支持
分享
最新回复 (4)
雪    币: 116
活跃值: (1012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
mark
2023-2-11 22:40
0
雪    币: 1318
活跃值: (2029)
能力值: ( LV4,RANK:49 )
在线值:
发帖
回帖
粉丝
3
即使证书远程下载,但是发起https下载请求就需要证书了,所以本地还是会有个证书
2023-2-19 15:36
0
雪    币: 571
活跃值: (4395)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
全局搜索".cer"、".crt"、".pfx"、"PKCS12"、"keyStore"、".p12"等关键字
2023-3-22 17:27
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2023-5-19 23:38
0
游客
登录 | 注册 方可回帖
返回
//