{"id":2037,"date":"2026-08-17T01:41:02","date_gmt":"2026-08-17T05:41:02","guid":{"rendered":"https:\/\/www.peteonsoftware.com\/?p=2037"},"modified":"2026-08-17T01:46:32","modified_gmt":"2026-08-17T05:46:32","slug":"hack-the-box-walkthrough-phantomring","status":"publish","type":"post","link":"https:\/\/www.peteonsoftware.com\/index.php\/2026\/08\/17\/hack-the-box-walkthrough-phantomring\/","title":{"rendered":"Hack the Box Walkthrough: PhantomRing"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/peteonsoftware.com\/images\/2026\/phantomring_logo.png\" alt=\"PhantomRing Logo\" title=\"PhantomRing Logo\" style=\"float:left;margin:.5rem;\">This post, we are going to tackle a Hack the Box Sherlock called PhantomRing.  You can find it <a href=\"https:\/\/app.hackthebox.com\/sherlocks\/PhantomRing?tab=play_sherlock\">here<\/a>.  It is rated <em>Very Easy<\/em>, but Hack the Box skews a little harder than other sites, so there are still some good learning points here&#8230; even if you aren&#8217;t on Day 2 of your InfoSec journey.  To get started, download the zip and extract the files using the provided password <em>hacktheblue<\/em>.  Inside, you&#8217;ll find one file called simply <em>agent<\/em>.<\/p>\n<p>Our given scenario is <\/p>\n<pre>\r\nYour organization's SOC team intercepted a suspicious binary during a routine threat hunting operation on a Linux server. The file was found in \/var\/tmp with an unusual name and was attempting to establish outbound connections. Initial analysis suggests this could be a post-exploitation agent. Your task is to perform static analysis on the binary to identify its capabilities, extract indicators of compromise, and understand the threat actor's infrastructure.\r\n<\/pre>\n<p><strong>Task 1 Question: What is the SHA256 hash of the malicious binary?<\/strong><\/p>\n<p>This starts out easily enough.  If you&#8217;re using Linux, you can just issue this command and get the result.  (PS&#8230; yes this hash is in VirusTotal)<\/p>\n<pre>\r\n$ sha256sum agent                   \r\n2d7b1b2178f76c26893b2a56cbf9b36700235259e76b893d53817d5b66b634a5  agent\r\n<\/pre>\n<p><em><strong>Task 1 Answer: 2d7b1b2178f76c26893b2a56cbf9b36700235259e76b893d53817d5b66b634a5<\/strong><\/em><\/p>\n<p><strong>Task 2 Question: What is the IP address hardcoded in the binary for C2 communication?<\/strong><br \/>\nLet&#8217;s use strings right off the bat.  That might help us with a few of these next couple of questions.  In this case, I only see one IP Address returned.<\/p>\n<pre>\r\n$ strings agent              \r\n\/lib64\/ld-linux-x86-64.so.2\r\n_ITM_deregisterTMCloneTable\r\n## SNIP ## \r\nget \r\nrecv \r\nusers\r\nnetstat\r\nkick\r\nprivesc\r\nsdestruct\r\nkillbpf\r\nexit\r\n[*] 404 Command not found [*]\r\nio_uring_queue_init\r\n192.168.56.1\r\nsocket\r\nio_uring_wait_cqe: %s\r\nconnect() failed: trying to reconnect\r\n## SNIP ##\r\n.data\r\n.bss\r\n.comment            \r\n<\/pre>\n<p><em><strong>Task 2 Answer: 192.168.56.1<\/strong><\/em><\/p>\n<p><strong>Task 3 Question: What port does the agent connect to on the C2 server?<\/strong><br \/>\nOkay, the <em>strings<\/em> thing was a naive way to approach this and I knew Hack the Box would drive this deeper.  I don&#8217;t see anything overtly obvious in strings, so I decided to use <em>Ghidra<\/em> to decompile it.  You know you can do it without difficulty because it isn&#8217;t a stripped binary.  How do I know that?  If you run the <em>file<\/em> command on the file, it tells you.<\/p>\n<pre>\r\n$ file agent            \r\nagent: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter \/lib64\/ld-linux-x86-64.so.2, BuildID[sha1]=1f617f2ea259a7ec724d7bbc01627982dc2f0495, for GNU\/Linux 3.2.0, not stripped\r\n<\/pre>\n<p>This isn&#8217;t a Ghidra tutorial (and trust me, you wouldn&#8217;t want me to give you one), but here are the steps I took.  I created a new project, did <em>File -> Import File<\/em> to bring in the agent executable, and then after it loaded, right clicked it and did <em>Open With -> CodeBrowser<\/em>.  Then, on the left hand side, there is a section called <em>Symbol Tree<\/em>.  Scroll into functions, find the main and click to select it.  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/peteonsoftware.com\/images\/2026\/phantomring_symboltree.jpg\" alt=\"Ghidra's Symbol Tree\" title=\"Ghidra's Symbol Tree\"><\/p>\n<p>Then, go to the menu at the top of Ghidra and select <em>Window -> Decompile:main<\/em> (or hit <em>CTRL-E<\/em>).<\/p>\n<p>That opens a code window to the right of the assembly code you had been looking at.  It is written in C and doesn&#8217;t have any variables named in a friendly way.  However, you can scan over the code or read through if you&#8217;re familiar with C.  I was looking for something related to this network call, and found where it logged out what was being attempted.<\/p>\n<pre>printf(\"[+] Connected to %s:%d\\n\",\"192.168.56.1\",0x115d);<\/pre>\n<p>That string format is telling you it is going to output this in the format of string:decimal and we know the string is going to be <em>192.168.56.1<\/em>.  But what&#8217;s the decimal?  The decimal is being represented by the hex of <em>0x115d<\/em>, which if you hover on it in CodeBrowser (or do the math), tells you that is 4445.  And that&#8217;s our answer.<\/p>\n<p><em><strong>Task 3 Answer: 4445<\/strong><\/em><\/p>\n<p><strong>Task 4 Question: How many seconds does the agent wait before attempting to reconnect after a failed connection?<\/strong><\/p>\n<p>Inside one of the nested <em>while(true)<\/em> loops, as it ends, the code does this:<\/p>\n<pre>sleep(0x78);<\/pre>\n<p>0x78 (again, do the math or hover on the value in Ghidra) is 120, which is our answer.<\/p>\n<p><em><strong>Task 4 Answer: 120<\/strong><\/em><\/p>\n<p><strong>Task 5 Question: How many different commands does the agent support? (excluding invalid commands)<\/strong><\/p>\n<p>Over in the Symbol Tree, there is another function called <em>process_cmd<\/em>.  Since we already have the <em>Decompile<\/em> window open, just click on <em>process_cmd<\/em> and the relevant code shows up.  There is a long <em>if-else<\/em> statement in there that covers all the commands: <em>recv, users, ss, netstat, ps, me, kick, privesc, sdestruct, killbpf, and exit<\/em>.<\/p>\n<p><em><strong>Task 5 Answer: 11<\/strong><\/em><\/p>\n<p><strong>Task 6 Question: What Linux kernel interface does this malware abuse to evade EDR syscall monitoring?<\/strong><br \/>\nSo, this one is what gives the room its name.  If you look in the symbol tree, there are a ton of calls to <em>io_uring_cq_advance, io_uring_cqe_seen, io_uring_prep_close<\/em>&#8230; I think 14 in total.  Googling it, you see that this interface was abused in <em>RingReaper<\/em> malware, so that should be our answer&#8230; and it is.<\/p>\n<p><em><strong>Task 6 Answer: io_uring<\/strong><\/em><\/p>\n<p><strong>Task 7 Question: What file does the agent read to enumerate logged-in users?<\/strong><\/p>\n<p>Another function is called <em>cmd_users<\/em>, if we open it for decompile, we see the file being read<\/p>\n<pre>iVar2 = read_file_uring(param_1,\"\/var\/run\/utmp\",local_4018,0x2000);<\/pre>\n<p><em><strong>Task 7 Answer: \/var\/run\/utmp<\/strong><\/em><\/p>\n<p><strong>Task 8 Question: What directory does the agent scan when searching for SUID binaries for privilege escalation?<\/strong><\/p>\n<p>There is another function called <em>cmd_privesc<\/em>.  Inside there, you can see this, giving us our answer.<\/p>\n<pre>local_4330 = opendir(\"\/usr\/bin\");<\/pre>\n<p><em><strong>Task 8 Answer: \/usr\/bin<\/strong><\/em><\/p>\n<p><strong>Task 9 Question: What string does the agent search for in \/proc\/[pid]\/maps to identify security tools using eBPF?<\/strong><\/p>\n<p>In the function, <em>cmd_killbpf<\/em>, if we scan down we can find this path.  While working in there, it reads the file for the process (using <em>read_file_uring<\/em>), then searches for the string <em>anon_inode:bpf-map<\/em><\/p>\n<pre>\r\nsnprintf(local_6118,0x100,\"\/proc\/%s\/maps\",local_6150->d_name);\r\n*(undefined8 *)(puVar6 + -0x11a8) = 0x103cb2;\r\niVar2 = read_file_uring(param_1,local_6118,local_4018,0x4000);\r\n} while (iVar2 < 1);\r\n  *(undefined8 *)(puVar6 + -0x11a8) = 0x103cde;\r\n  pcVar4 = strstr(local_4018,\"anon_inode:bpf-map\");\r\n<\/pre>\n<p><em><strong>Task 9 Answer: anon_inode:bpf-map<\/strong><\/em><\/p>\n<p><strong>Task 10 Question: What is the full path of the first tracing file the agent attempts to disable?<\/strong><\/p>\n<p>Same function.  The first one in the array is the answer.<\/p>\n<pre>\r\n  local_6138[0] = \"\/sys\/kernel\/debug\/tracing\/tracing_on\";\r\n  local_6138[1] = \"\/sys\/kernel\/debug\/tracing\/set_event\";\r\n  local_6138[2] = \"\/sys\/kernel\/debug\/tracing\/current_tracer\";\r\n<\/pre>\n<p><em><strong>Task 10 Answer: \/sys\/kernel\/debug\/tracing\/tracing_on<\/strong><\/em><\/p>\n<p><strong>Task 11 Question: What procfs path does the agent read to find its own executable location before self-destruction?<\/strong><\/p>\n<p>In <em>cmd_selfdestruct<\/em>, it is right here:<\/p>\n<pre>\r\nlocal_2a8 = readlink(\"\/proc\/self\/exe\",local_218,0x1ff);\r\n<\/pre>\n<p><em><strong>Task 11 Answer: \/proc\/self\/exe<\/strong><\/em><\/p>\n<p><strong>Task 12 Question: What command string is compared by the agent to trigger deletion of its own binary?<\/strong><\/p>\n<p>Back in Task 5, I listed all the commands and where to see them.  It is the obvious one where the <em>if\/else<\/em> leads to <em>cmd_selfdestruct<\/em>.<\/p>\n<p><em><strong>Task 12 Answer: sdestruct<\/strong><\/em><\/p>\n<p>And here we go.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/peteonsoftware.com\/images\/2026\/phantomring_pwned.jpg\" alt=\"PhantomRing Pwned\" title=\"PhantomRing Pwned\"><\/p>\n<p>Any questions, let me know.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post, we are going to tackle a Hack the Box Sherlock called PhantomRing. You can find it here. It is rated Very Easy, but Hack the Box skews a little harder than other sites, &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[153],"tags":[160,141,142],"class_list":["post-2037","post","type-post","status-publish","format-standard","hentry","category-capture-the-flag","tag-dfir","tag-information-security","tag-infosec"],"_links":{"self":[{"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/posts\/2037","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/comments?post=2037"}],"version-history":[{"count":0,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/posts\/2037\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/media?parent=2037"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/categories?post=2037"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/tags?post=2037"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}