背景
本文以分析OpenSSL开源代码为切入点,分享扫过证书绑定的逆向思路
逆向思路
- 获取关键信息
- 通过Log信息
- 通过App报错弹框信息
- 通过一些常见的报错信息,如verify error、ssl error等
- 根据关键信息定位到函数
- strings命令搜索
- grep命令搜索
- IDA搜索
- 静态分析
- 利用IDA Graph进行分支分析
- 一般是寻找返回值为int的函数
- 或者是寻找cmp的比较值
- 利用Frida Hook关键函数
- 通过开启/关闭代理开发,对比进行Hook,找出差异点
例子
- 获取LOG信息–> SSL error code
- 定位函数
- 静态分析
- Google搜索到是openssl源码
- 耐心分析源码校验的地方
- 关键点: 一定是调用链相对深入的地方,找到关键的int值,多尝试,坚持
ssl_verify_result_t SSLClientSocketImpl::HandleVerifyResult() {
// Verification is in progress. Inform BoringSSL it should retry the
// callback later. The next call to VerifyCertCallback will be a
// continuation of the same verification, so leave
// cert_verification_result_ as-is.
if (cert_verification_result_ == ERR_IO_PENDING)
return ssl_verify_retry;
// In BoringSSL's calling convention for asynchronous callbacks,
// after a callback returns a non-retry value, the operation has
// completed. Subsequent calls are of new operations with potentially
// different arguments. Reset cert_verification_result_ to inform
// VerifyCertCallback not to replay the result on subsequent calls.
int result = cert_verification_result_;
cert_verification_result_ = kCertVerifyPending;
cert_verifier_request_.reset();
if (!start_cert_verification_time_.is_null()) {
base::TimeDelta verify_time =
base::TimeTicks::Now() - start_cert_verification_time_;
if (result == OK) {
UMA_HISTOGRAM_TIMES("Net.SSLCertVerificationTime", verify_time);
} else {
UMA_HISTOGRAM_TIMES("Net.SSLCertVerificationTimeError", verify_time);
}
}
// Enforce keyUsage extension for RSA leaf certificates chaining up to known
// roots.
// TODO(crbug.com/795089): Enforce this unconditionally.
if (server_cert_verify_result_.is_issued_by_known_root) {
SSL_set_enforce_rsa_key_usage(ssl_.get(), 1);
}
// If the connection was good, check HPKP and CT status simultaneously,
// but prefer to treat the HPKP error as more serious, if there was one.
if (result == OK) {
int ct_result = CheckCTCompliance();
TransportSecurityState::PKPStatus pin_validity =
context_->transport_security_state()->CheckPublicKeyPins(
host_and_port_, server_cert_verify_result_.is_issued_by_known_root,
server_cert_verify_result_.public_key_hashes, server_cert_.get(),
server_cert_verify_result_.verified_cert.get(),
TransportSecurityState::ENABLE_PIN_REPORTS,
ssl_config_.network_isolation_key, &pinning_failure_log_);
switch (pin_validity) {
case TransportSecurityState::PKPStatus::VIOLATED:
server_cert_verify_result_.cert_status |=
CERT_STATUS_PINNED_KEY_MISSING;
result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
break;
case TransportSecurityState::PKPStatus::BYPASSED:
pkp_bypassed_ = true;
FALLTHROUGH;
case TransportSecurityState::PKPStatus::OK:
// Do nothing.
break;
}
if (result != ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN && ct_result != OK)
result = ct_result;
}
// If no other errors occurred, check whether the connection used a legacy TLS
// version.
if (result == OK &&
SSL_version(ssl_.get()) < context_->config().version_min_warn &&
base::FeatureList::IsEnabled(features::kLegacyTLSEnforced)) {
server_cert_verify_result_.cert_status |= CERT_STATUS_LEGACY_TLS;
// Only set the resulting net error if it hasn't been previously bypassed.
if (!ssl_config_.IsAllowedBadCert(server_cert_.get(), nullptr))
result = ERR_SSL_OBSOLETE_VERSION;
}
is_fatal_cert_error_ =
IsCertStatusError(server_cert_verify_result_.cert_status) &&
result != ERR_CERT_KNOWN_INTERCEPTION_BLOCKED &&
result != ERR_SSL_OBSOLETE_VERSION &&
context_->transport_security_state()->ShouldSSLErrorsBeFatal(
host_and_port_.host());
if (IsCertificateError(result) && ssl_config_.ignore_certificate_errors) {
result = OK;
}
if (result == OK) {
return ssl_verify_ok;
}
OpenSSLPutNetError(FROM_HERE, result);
return ssl_verify_invalid;
}
通过源码的字符串(HandleVerifyResult\Net.SSLCertVerificationTime)定位bin文件的位置
利用frida修改x0的值进行试探分析