Some time ago after I had defended my diploma 論題/論文 on OAuth 安全 my groupmate asked me: "Hey, have you looked into Android OAuth?", and I felt わずかに lost since I realized there is yet another OAuth 実施, and I didn't know how it 作品.
Lately I 設立する some time to 解決する this problem. The 仕事 seemed challenging at the beginning since Android OAuth is a part of Google Play, which is の近くにd source: this was the first time I had to 逆転する-engineer to see how the open 基準 作品 (すなわち OAuth). Instead of explaining the whole design myself in this 令状 up, I recommend to read sbktech's blog where he has recently published his 十分な, descriptive, and 平易な to read explanation of Android OAuth 内部のs. I would just 追加する a few 公式文書,認めるs about my own findings to the 存在するing sbktech's 地位,任命する:
As a first step to understand the weak parts of the OAuth logic I binded to the com.google.android.gms/.auth.GetToken service manually and made, perhaps, a classic mistake with "NetworkOnMainThreadException", which thankfully brought me the "getToken() -> ... -> 網状組織 request" callstack in a logcat to 調査する:
W/GLSUser ( 602): GoogleAccountDataService.getToken()
I/GoogleHttpClient( 602): 落ちるing 支援する to old SSLCertificateSocketFactory
I/GoogleHttpClient( 602): Using GMS GoogleHttpClient
W/GLSActivity( 602): [GetToken] - getToken exception!
W/GLSActivity( 602): android.os.NetworkOnMainThreadException
W/GLSActivity( 602): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)
W/GLSActivity( 602): at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
W/GLSActivity( 602): at libcore.io.IoBridge.connectErrno(IoBridge.java:144)
W/GLSActivity( 602): at libcore.io.IoBridge.connect(IoBridge.java:112)
W/GLSActivity( 602): at java.逮捕する.PlainSocketImpl.connect(PlainSocketImpl.java:192)
W/GLSActivity( 602): at java.逮捕する.PlainSocketImpl.connect(PlainSocketImpl.java:459)
W/GLSActivity( 602): at java.逮捕する.Socket.connect(Socket.java:843)
W/GLSActivity( 602): at com.android.okhttp.内部の.壇・綱領・公約.connectSocket(壇・綱領・公約.java:131)
W/GLSActivity( 602): at com.android.okhttp.関係.connect(関係.java:101)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpEngine.connect(HttpEngine.java:294)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpEngine.sendRequest(HttpEngine.java:206)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpURLConnectionImpl.遂行する/発効させる(HttpURLConnectionImpl.java:345)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:89)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:197)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:254)
W/GLSActivity( 602): at gaz.a(SourceFile:823)
W/GLSActivity( 602): at gaz.c(SourceFile:692)
W/GLSActivity( 602): at gaz.遂行する/発効させる(SourceFile:601)
W/GLSActivity( 602): at xt.遂行する/発効させる(SourceFile:365)
W/GLSActivity( 602): at xt.遂行する/発効させる(SourceFile:447)
W/GLSActivity( 602): at avc.a(SourceFile:258)
W/GLSActivity( 602): at avd.a(SourceFile:575)
W/GLSActivity( 602): at avd.a(SourceFile:649)
W/GLSActivity( 602): at avd.a(SourceFile:812)
W/GLSActivity( 602): at avi.a(SourceFile:282)
W/GLSActivity( 602): at avh.a(SourceFile:163)
W/GLSActivity( 602): at axm.a(SourceFile:133)
W/GLSActivity( 602): at axf.a(SourceFile:337)
W/GLSActivity( 602): at axf.a(SourceFile:132)
W/GLSActivity( 602): at arx.a(SourceFile:92)
W/GLSActivity( 602): at arh.a(SourceFile:107)
W/GLSActivity( 602): at wj.onTransact(SourceFile:63)
W/GLSActivity( 602): at android.os.Binder.execTransact(Binder.java:404)
W/GLSActivity( 602): at dalvik.system.NativeStart.run(Native Method)
W/System.err( 1093): android.os.NetworkOnMainThreadException
W/System.err( 1093): at android.os.小包.readException(小包.java:1475)
W/System.err( 1093): at android.os.小包.readException(小包.java:1419)
W/System.err( 1093): at com.google.android.gms.auth.見本.helloauth.GetNameInForeground$myConnection.onServiceConnected(GetNameInForeground.java:100)
W/System.err( 1093): at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1110)
W/System.err( 1093): at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1127)
W/System.err( 1093): at android.os.Handler.handleCallback(Handler.java:733)
W/System.err( 1093): at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err( 1093): at android.os.Looper.宙返り飛行(Looper.java:136)
W/System.err( 1093): at android.app.ActivityThread.main(ActivityThread.java:5017)
D/ConnectivityService( 389): handleInetConditionHoldEnd: 逮捕する=0, 条件=0, published 条件=0
W/System.err( 1093): at java.lang.反映する.Method.invokeNative(Native Method)
W/System.err( 1093): at java.lang.反映する.Method.invoke(Method.java:515)
W/System.err( 1093): at com.android.内部の.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
W/System.err( 1093): at com.android.内部の.os.ZygoteInit.main(ZygoteInit.java:595)
W/System.err( 1093): at dalvik.system.NativeStart.main(Native Method)
I 回復するd the logic of those three-letter classes from arh to gaz (that's the Google Play part) and felt an extreme sympathy to the avd class because of the two に引き続いて 推論する/理由s:
The below 機能(する)/行事 of the avd class parsed the getToken Bundle extras argument and 挿入するd all _opt_XXX parameters from it inside the HTTP request as XXX, 明白に 許すing to 始める,決める has_permission=1 without any 使用者 同意:
public final 名簿(に載せる)/表(にあげる) a(String paramString1, String paramString2, int paramInt, String paramString3, boolean paramBoolean1, Bundle paramBundle, boolean paramBoolean2, String paramString4, boolean paramBoolean3, boolean paramBoolean4, CaptchaSolution paramCaptchaSolution, PACLConfig paramPACLConfig, FACLConfig paramFACLConfig, String paramString5)
if (str8.startsWith("_opt_"))
{
localaux1.a(str8.replaceFirst("_opt_", ""), paramBundle.getString(str8));
...
The GooglePlay also 喜んで 認めるd me a couple of undocumented 範囲s, 現実に giving me 支援する those SID and LSID 開会/開廷/会期 cookies in (疑いを)晴らす:
public final TokenResponse a(TokenResponse paramTokenResponse, 地図/計画する paramMap, int paramInt, String paramString1, boolean paramBoolean1, boolean paramBoolean2, String paramString2, PACLConfig paramPACLConfig, FACLConfig paramFACLConfig)
{
...
if (("SID".equals(paramString1)) || ("LSID".equals(paramString1)))
{
str1 = (String)paramMap.get(paramString1);
...
Additionally, I made a few more steps on my way to the PoC:
As a result, considering an 任命する/導入するd app 要求するing no 許可s, (1) 許すd me to just 漏れる all possible oauth2 範囲s, while with (2) I was able to take over Google account.
PoC: https://gist.github.com/isciurus/df4d7edd9c3efb4a0753
Timeline:
December 2, 2014 — 報告(する)/憶測d the vulnerability to the Android 安全, @natashenka 確認するd the repro 作品
January 6, 2015 — 返答 form Android 安全 説 that the 直す/買収する,八百長をする was 押し進めるd in 中央の-December, I checked that the repro stopped working on all my phones
January 9, 2015 — Public 公表,暴露
Thanks to @evdokimovds from DSecRG for helping with unpacking 道具s and to @jduck from droidsec for 立証するing the code on 多重の Android phones.
Lately I 設立する some time to 解決する this problem. The 仕事 seemed challenging at the beginning since Android OAuth is a part of Google Play, which is の近くにd source: this was the first time I had to 逆転する-engineer to see how the open 基準 作品 (すなわち OAuth). Instead of explaining the whole design myself in this 令状 up, I recommend to read sbktech's blog where he has recently published his 十分な, descriptive, and 平易な to read explanation of Android OAuth 内部のs. I would just 追加する a few 公式文書,認めるs about my own findings to the 存在するing sbktech's 地位,任命する:
TL;DR: I was able to find two vulnerabilities in Google Play system apk which 許すd me to bypass the Android 使用/適用 許可 model: an 任命する/導入するd app asking no 許可s could get 十分な 接近 to the 装置 owner's Google account (it is 十分な for a new app 任命する/導入する or Chrome sync 接近).
As a first step to understand the weak parts of the OAuth logic I binded to the com.google.android.gms/.auth.GetToken service manually and made, perhaps, a classic mistake with "NetworkOnMainThreadException", which thankfully brought me the "getToken() -> ... -> 網状組織 request" callstack in a logcat to 調査する:
W/GLSUser ( 602): GoogleAccountDataService.getToken()
I/GoogleHttpClient( 602): 落ちるing 支援する to old SSLCertificateSocketFactory
I/GoogleHttpClient( 602): Using GMS GoogleHttpClient
W/GLSActivity( 602): [GetToken] - getToken exception!
W/GLSActivity( 602): android.os.NetworkOnMainThreadException
W/GLSActivity( 602): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)
W/GLSActivity( 602): at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
W/GLSActivity( 602): at libcore.io.IoBridge.connectErrno(IoBridge.java:144)
W/GLSActivity( 602): at libcore.io.IoBridge.connect(IoBridge.java:112)
W/GLSActivity( 602): at java.逮捕する.PlainSocketImpl.connect(PlainSocketImpl.java:192)
W/GLSActivity( 602): at java.逮捕する.PlainSocketImpl.connect(PlainSocketImpl.java:459)
W/GLSActivity( 602): at java.逮捕する.Socket.connect(Socket.java:843)
W/GLSActivity( 602): at com.android.okhttp.内部の.壇・綱領・公約.connectSocket(壇・綱領・公約.java:131)
W/GLSActivity( 602): at com.android.okhttp.関係.connect(関係.java:101)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpEngine.connect(HttpEngine.java:294)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpEngine.sendSocketRequest(HttpEngine.java:255)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpEngine.sendRequest(HttpEngine.java:206)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpURLConnectionImpl.遂行する/発効させる(HttpURLConnectionImpl.java:345)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:89)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:197)
W/GLSActivity( 602): at com.android.okhttp.内部の.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:254)
W/GLSActivity( 602): at gaz.a(SourceFile:823)
W/GLSActivity( 602): at gaz.c(SourceFile:692)
W/GLSActivity( 602): at gaz.遂行する/発効させる(SourceFile:601)
W/GLSActivity( 602): at xt.遂行する/発効させる(SourceFile:365)
W/GLSActivity( 602): at xt.遂行する/発効させる(SourceFile:447)
W/GLSActivity( 602): at avc.a(SourceFile:258)
W/GLSActivity( 602): at avd.a(SourceFile:575)
W/GLSActivity( 602): at avd.a(SourceFile:649)
W/GLSActivity( 602): at avd.a(SourceFile:812)
W/GLSActivity( 602): at avi.a(SourceFile:282)
W/GLSActivity( 602): at avh.a(SourceFile:163)
W/GLSActivity( 602): at axm.a(SourceFile:133)
W/GLSActivity( 602): at axf.a(SourceFile:337)
W/GLSActivity( 602): at axf.a(SourceFile:132)
W/GLSActivity( 602): at arx.a(SourceFile:92)
W/GLSActivity( 602): at arh.a(SourceFile:107)
W/GLSActivity( 602): at wj.onTransact(SourceFile:63)
W/GLSActivity( 602): at android.os.Binder.execTransact(Binder.java:404)
W/GLSActivity( 602): at dalvik.system.NativeStart.run(Native Method)
W/System.err( 1093): android.os.NetworkOnMainThreadException
W/System.err( 1093): at android.os.小包.readException(小包.java:1475)
W/System.err( 1093): at android.os.小包.readException(小包.java:1419)
W/System.err( 1093): at com.google.android.gms.auth.見本.helloauth.GetNameInForeground$myConnection.onServiceConnected(GetNameInForeground.java:100)
W/System.err( 1093): at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1110)
W/System.err( 1093): at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1127)
W/System.err( 1093): at android.os.Handler.handleCallback(Handler.java:733)
W/System.err( 1093): at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err( 1093): at android.os.Looper.宙返り飛行(Looper.java:136)
W/System.err( 1093): at android.app.ActivityThread.main(ActivityThread.java:5017)
D/ConnectivityService( 389): handleInetConditionHoldEnd: 逮捕する=0, 条件=0, published 条件=0
W/System.err( 1093): at java.lang.反映する.Method.invokeNative(Native Method)
W/System.err( 1093): at java.lang.反映する.Method.invoke(Method.java:515)
W/System.err( 1093): at com.android.内部の.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
W/System.err( 1093): at com.android.内部の.os.ZygoteInit.main(ZygoteInit.java:595)
W/System.err( 1093): at dalvik.system.NativeStart.main(Native Method)
I 回復するd the logic of those three-letter classes from arh to gaz (that's the Google Play part) and felt an extreme sympathy to the avd class because of the two に引き続いて 推論する/理由s:
1. URL parameter 注射
public final 名簿(に載せる)/表(にあげる) a(String paramString1, String paramString2, int paramInt, String paramString3, boolean paramBoolean1, Bundle paramBundle, boolean paramBoolean2, String paramString4, boolean paramBoolean3, boolean paramBoolean4, CaptchaSolution paramCaptchaSolution, PACLConfig paramPACLConfig, FACLConfig paramFACLConfig, String paramString5)
if (str8.startsWith("_opt_"))
{
localaux1.a(str8.replaceFirst("_opt_", ""), paramBundle.getString(str8));
...
2. 魔法 範囲s "SID" and "LSID"
public final TokenResponse a(TokenResponse paramTokenResponse, 地図/計画する paramMap, int paramInt, String paramString1, boolean paramBoolean1, boolean paramBoolean2, String paramString2, PACLConfig paramPACLConfig, FACLConfig paramFACLConfig)
{
...
if (("SID".equals(paramString1)) || ("LSID".equals(paramString1)))
{
str1 = (String)paramMap.get(paramString1);
...
Additionally, I made a few more steps on my way to the PoC:
- I impersonated the gms app by setting _opt_app=com.google.android.gms
- I bypassed the 署名 立証 by copy-pasting the 署名s and setting them through _opt_client_sig=<sig> (sorry, no crypto 欠陥s here)
- I collected 署名s for all 見解/翻訳/版s of gms (two in total: 58e1c4133f7441ec3d2c270270a14802da47ba0e and 38918a453d07199354f8b19af05ec6562ced5788), so that my code worked on all Android 4/5 phones
- I was able to 漏れる the 装置 owner's email through the AccountManager.newChooseAccountIntent for using it in GoogleAuthUtil.getToken (this 意図 silently returns the 使用者's email if you 調印するd into the only one Google account)
As a result, considering an 任命する/導入するd app 要求するing no 許可s, (1) 許すd me to just 漏れる all possible oauth2 範囲s, while with (2) I was able to take over Google account.
PoC: https://gist.github.com/isciurus/df4d7edd9c3efb4a0753
Timeline:
December 2, 2014 — 報告(する)/憶測d the vulnerability to the Android 安全, @natashenka 確認するd the repro 作品
January 6, 2015 — 返答 form Android 安全 説 that the 直す/買収する,八百長をする was 押し進めるd in 中央の-December, I checked that the repro stopped working on all my phones
January 9, 2015 — Public 公表,暴露
Thanks to @evdokimovds from DSecRG for helping with unpacking 道具s and to @jduck from droidsec for 立証するing the code on 多重の Android phones.