{"id":469,"date":"2025-12-18T16:02:57","date_gmt":"2025-12-18T16:02:57","guid":{"rendered":"https:\/\/hackingwithj.com\/?p=469"},"modified":"2025-12-18T16:02:57","modified_gmt":"2025-12-18T16:02:57","slug":"thm-ultratech","status":"publish","type":"post","link":"https:\/\/hackingwithj.com\/?p=469","title":{"rendered":"THM: UltraTech"},"content":{"rendered":"\n<p>This box turned out to be a great reminder that <strong>APIs + poor input validation<\/strong> can quickly turn into full system compromise. What started as a simple web app ended with a Docker-based root shell \u2014 all because of a single exposed endpoint.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">NMAP<\/h2>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-cd39bbb41ae7a52e980ef54636d33279\"><code>\u250c\u2500\u2500(kali\u327fkali)-&#91;~\/Downloads\/ctf\/ultratech]\n\u2514\u2500$ nmap -sCV -T4 -p- -Pn -v 10.80.151.200 \nPORT      STATE SERVICE VERSION\n8081\/tcp  open  http    Node.js Express framework\n|_http-title: Site doesn't have a title (text\/html; charset=utf-8).\n|_http-cors: HEAD GET POST PUT DELETE PATCH\n| http-methods: \n|_  Supported Methods: GET HEAD POST OPTIONS\n31331\/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))\n|_http-favicon: Unknown favicon MD5: 15C1B7515662078EF4B5C724E2927A96\n| http-methods: \n|_  Supported Methods: GET POST OPTIONS HEAD\n|_http-title: UltraTech - The best of technology (AI, FinTech, Big Data)\n|_http-server-header: Apache\/2.4.41 (Ubuntu)<\/code><\/pre>\n\n\n\n<p>Two web services immediately stood out:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>31331<\/strong> \u2192 Apache-hosted website<\/li>\n\n\n\n<li><strong>8081<\/strong> \u2192 Node.js \/ Express API<\/li>\n<\/ul>\n\n\n\n<p>That usually means logic lives in the API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">HTTP (31311)<\/h2>\n\n\n\n<p>Visiting port 31331 showed a generic corporate-style website.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"393\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165515.753-1024x393.png\" alt=\"\" class=\"wp-image-470\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165515.753-1024x393.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165515.753-300x115.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165515.753-768x295.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165515.753-1536x590.png 1536w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165515.753.png 1912w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Nothing interesting stood out during manual browsing, so I ran Gobuster.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"962\" height=\"405\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165529.900.png\" alt=\"\" class=\"wp-image-471\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165529.900.png 962w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165529.900-300x126.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165529.900-768x323.png 768w\" sizes=\"auto, (max-width: 962px) 100vw, 962px\" \/><\/figure>\n\n\n\n<p>This revealed a login page. Attempting to log in redirected the request to the API running on port 8081 \u2014 confirming the frontend was just a wrapper.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"694\" height=\"73\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165606.070.png\" alt=\"\" class=\"wp-image-472\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165606.070.png 694w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165606.070-300x32.png 300w\" sizes=\"auto, (max-width: 694px) 100vw, 694px\" \/><\/figure>\n\n\n\n<p>Time to pivot.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">API (8081)<\/h2>\n\n\n\n<p>At first glance the API only exposed authentication functionality. I ran an API wordlist to check for hidden endpoints and found something interesting:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"410\" height=\"71\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165647.616.png\" alt=\"\" class=\"wp-image-474\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165647.616.png 410w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165647.616-300x52.png 300w\" sizes=\"auto, (max-width: 410px) 100vw, 410px\" \/><\/figure>\n\n\n\n<p>The <strong><code>\/ping<\/code><\/strong> endpoint consistently returned server errors. Burp showed that the browser itself was making requests to this endpoint.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"824\" height=\"379\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165704.608.png\" alt=\"\" class=\"wp-image-475\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165704.608.png 824w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165704.608-300x138.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165704.608-768x353.png 768w\" sizes=\"auto, (max-width: 824px) 100vw, 824px\" \/><\/figure>\n\n\n\n<p>Testing the endpoint manually without the frontend confirmed it was executing a system ping command.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"253\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165722.334-1024x253.png\" alt=\"\" class=\"wp-image-476\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165722.334-1024x253.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165722.334-300x74.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165722.334-768x190.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165722.334-1536x380.png 1536w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165722.334.png 1542w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>That immediately raised a red flag.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Exploitation \u2014 OS Command Injection<\/h2>\n\n\n\n<p>Since the API was directly executing system commands, I started testing for OS command injection. After some trial and error, newline characters (<code>\\n<\/code>) worked as a command separator.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"259\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165814.280-1024x259.png\" alt=\"\" class=\"wp-image-477\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165814.280-1024x259.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165814.280-300x76.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165814.280-768x194.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165814.280-1536x388.png 1536w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165814.280.png 1538w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>At this point I had command execution. Using that access, I searched the filesystem and found a database containing two user password hashes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"795\" height=\"311\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165843.121.png\" alt=\"\" class=\"wp-image-478\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165843.121.png 795w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165843.121-300x117.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165843.121-768x300.png 768w\" sizes=\"auto, (max-width: 795px) 100vw, 795px\" \/><\/figure>\n\n\n\n<p>I cracked them using CrackStation.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"95\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165858.132-1024x95.png\" alt=\"\" class=\"wp-image-479\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165858.132-1024x95.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165858.132-300x28.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165858.132-768x71.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T165858.132.png 1047w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">SSH Access<\/h2>\n\n\n\n<p>Using the recovered credentials, I logged into the web app and was greeted with this message:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>Hey r00t, can you please have a look at the server&#8217;s configuration?<br>The intern did it and I don&#8217;t really trust him.<br>Thanks!<\/em><\/p>\n<\/blockquote>\n\n\n\n<p>That was a pretty clear hint.<\/p>\n\n\n\n<p>I tried SSH using the cracked credentials \u2014 and it worked.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Privilege Escalation<\/h2>\n\n\n\n<p>Running <code>id<\/code> showed something important, the fact that i was part of the docker user group. At first, I assumed I was inside a Docker container and wasted some time trying to escape it \u2014 a wrong assumption that cost me time. Eventually I realized the real issue:<\/p>\n\n\n\n<p>\ud83d\udc49 <strong>The user was a member of the Docker group.<\/strong><\/p>\n\n\n\n<p>Docker group membership is effectively root. Using the technique described by <a href=\"https:\/\/www.securitum.com\/privilege_escalation_through_docker_group_membership_and_sudo_backdoor.html\">Securitum<\/a>, I spawned a root shell by mounting the host filesystem:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-f578f72e409892e835a198633611c928\"><code>docker run -v \/:\/mnt --rm -it bash chroot \/mnt sh<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"653\" height=\"75\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T170124.509.png\" alt=\"\" class=\"wp-image-480\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T170124.509.png 653w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/12\/image-2025-12-18T170124.509-300x34.png 300w\" sizes=\"auto, (max-width: 653px) 100vw, 653px\" \/><\/figure>\n\n\n\n<p>Root access achieved.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Learning Notes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>OS command injection is still very real \u2014 especially in internal APIs.<\/li>\n\n\n\n<li>Newline characters can bypass simple filters.<\/li>\n\n\n\n<li>Docker group membership equals root access.<\/li>\n\n\n\n<li>Always verify assumptions \u2014 not every Docker-related issue is container escape.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts<\/h2>\n\n\n\n<p>This box was a solid example of how <strong>small API design mistakes<\/strong> can lead to full compromise. No fancy exploits, no obscure CVEs \u2014 just bad input handling and dangerous permissions. Exactly the kind of machine that rewards careful enumeration and thinking things through.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This box turned out to be a great reminder that APIs + poor input validation can quickly turn into full system compromise. What started as a simple web app ended with a Docker-based root shell \u2014 all because of a single exposed endpoint. NMAP Two web services immediately stood out: That usually means logic lives [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"site-container-style":"default","site-container-layout":"default","site-sidebar-layout":"default","disable-article-header":"default","disable-site-header":"default","disable-site-footer":"default","disable-content-area-spacing":"default","footnotes":""},"categories":[8,20],"tags":[],"class_list":["post-469","post","type-post","status-publish","format-standard","hentry","category-ctf","category-cybersecurity"],"_links":{"self":[{"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts\/469","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=469"}],"version-history":[{"count":3,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts\/469\/revisions"}],"predecessor-version":[{"id":482,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts\/469\/revisions\/482"}],"wp:attachment":[{"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}