[Cloudflare] Fix recent CF JS Challenge error that calls DOM (#1919)

* [Cloudflare] Fix recent CF JS Challenge error that calls DOM

* Replace `atob` to pure js version. (was node.js API which invalid)

* Use `atob` as native function `Base64.decode()``

* Use okio Base64 decoder instead of Android one.
This commit is contained in:
DitFranXX 2019-03-23 03:25:21 +09:00 committed by inorichi
parent 9ca0307e1c
commit 7551941ef2

View File

@ -14,8 +14,20 @@ class CloudflareInterceptor : Interceptor {
private val sPattern = Regex("""name="s" value="([^"]+)""") private val sPattern = Regex("""name="s" value="([^"]+)""")
private val kPattern = Regex("""k\s+=\s+'([^']+)';""")
private val serverCheck = arrayOf("cloudflare-nginx", "cloudflare") private val serverCheck = arrayOf("cloudflare-nginx", "cloudflare")
private interface IBase64 {
fun decode(input: String): String
}
private val b64: IBase64 = object : IBase64 {
override fun decode(input: String): String {
return okio.ByteString.decodeBase64(input)!!.utf8()
}
}
@Synchronized @Synchronized
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request()) val response = chain.proceed(chain.request())
@ -49,17 +61,28 @@ class CloudflareInterceptor : Interceptor {
val pass = passPattern.find(content)?.groups?.get(1)?.value val pass = passPattern.find(content)?.groups?.get(1)?.value
val s = sPattern.find(content)?.groups?.get(1)?.value val s = sPattern.find(content)?.groups?.get(1)?.value
// If `k` is null, it uses old methods.
val k = kPattern.find(content)?.groups?.get(1)?.value ?: ""
val innerHTMLValue = Regex("""<div(.*)id="$k"(.*)>(.*)</div>""")
.find(content)?.groups?.get(3)?.value ?: ""
if (operation == null || challenge == null || pass == null || s == null) { if (operation == null || challenge == null || pass == null || s == null) {
throw Exception("Failed resolving Cloudflare challenge") throw Exception("Failed resolving Cloudflare challenge")
} }
// Export native Base64 decode function to js object.
duktape.set("b64", IBase64::class.java, b64)
// Return simulated innerHTML when call DOM.
val simulatedDocumentJS = """var document = { getElementById: function (x) { return { innerHTML: "$innerHTMLValue" }; } }"""
val js = operation val js = operation
.replace(Regex("""a\.value = (.+ \+ t\.length(\).toFixed\(10\))?).+"""), "$1") .replace(Regex("""a\.value = (.+\.toFixed\(10\);).+"""), "$1")
.replace(Regex("""\s{3,}[a-z](?: = |\.).+"""), "") .replace(Regex("""\s{3,}[a-z](?: = |\.).+"""), "")
.replace("t.length", "${domain.length}") .replace("t.length", "${domain.length}")
.replace("\n", "") .replace("\n", "")
val result = duktape.evaluate(js) as String val result = duktape.evaluate("""$simulatedDocumentJS;$ATOB_JS;var t="$domain";$js""") as String
val cloudflareUrl = HttpUrl.parse("${url.scheme()}://$domain/cdn-cgi/l/chk_jschl")!! val cloudflareUrl = HttpUrl.parse("${url.scheme()}://$domain/cdn-cgi/l/chk_jschl")!!
.newBuilder() .newBuilder()
@ -80,4 +103,8 @@ class CloudflareInterceptor : Interceptor {
} }
} }
companion object {
// atob() is browser API, Using Android's own function. (java.util.Base64 can't be used because of min API level)
private const val ATOB_JS = """var atob = function (input) { return b64.decode(input) }"""
}
} }